Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions cli/k8s_client/k8s_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,8 @@ func (k *KubeClient) PatchCSIDriverByLabel(label string, patchBytes []byte, patc
return nil
}


// CheckNamespaceExists checks if the installation namespace already exists
func (k *KubeClient) CheckNamespaceExists(namespace string) (bool, error) {
if _, err := k.GetNamespace(namespace); err != nil {
if statusErr, ok := err.(*apierrors.StatusError); ok && statusErr.Status().Reason == metav1.StatusReasonNotFound {
Expand All @@ -1859,6 +1861,22 @@ func (k *KubeClient) CheckNamespaceExists(namespace string) (bool, error) {
return true, nil
}

// CheckNamespaceLabels checks if the installation namespace already has the required labels
func (k *KubeClient) CheckNamespaceLabels(namespace string, labels map[string]string) bool {
ns, err := k.GetNamespace(namespace)
if err != nil {
return false
}

for key, value := range labels {
if ns.Labels[key] != value {
return false
}
}
return true
}


// PatchNamespaceLabels patches the namespace with provided labels
func (k *KubeClient) PatchNamespaceLabels(namespace string, labels map[string]string) error {
patch, err := json.Marshal(map[string]interface{}{
Expand Down
4 changes: 4 additions & 0 deletions cli/k8s_client/k8s_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,10 @@ func TestNamespaceOperations(t *testing.T) {
assert.False(t, exists)
assert.NoError(t, err) // Existence checks should not error

// Test CheckNamespaceLabels
hasLabel := suite.kubeClient.CheckNamespaceLabels("nonexistent", map[string]string{"kubernetes.io/metadata.name": "test-namespace"})
assert.False(t, hasLabel)

// Test GetNamespace
namespace, err := suite.kubeClient.GetNamespace("test-namespace")
// Fake client may behave differently - accept either outcome
Expand Down
1 change: 1 addition & 0 deletions cli/k8s_client/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ type KubernetesClient interface {
DeleteCSIDriver(name string) error
PatchCSIDriverByLabel(label string, patchBytes []byte, patchType types.PatchType) error
CheckNamespaceExists(namespace string) (bool, error)
CheckNamespaceLabels(namespace string, labels map[string]string) error
PatchNamespaceLabels(namespace string, labels map[string]string) error
PatchNamespace(namespace string, patchBytes []byte, patchType types.PatchType) error
GetNamespace(namespace string) (*v1.Namespace, error)
Expand Down
12 changes: 12 additions & 0 deletions operator/controllers/orchestrator/installer/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,18 @@ func (i *Installer) createOrPatchTridentInstallationNamespace() error {
}
Log().WithField("namespace", i.namespace).Info("Created Trident installation namespace.")
} else {
// Check if namespace is already labeled
hasLabels := i.client.CheckNamespaceLabels(i.namespace, map[string]string{
commonconfig.PodSecurityStandardsEnforceLabel: commonconfig.PodSecurityStandardsEnforceProfile,
})

if hasLabels {
Log().WithField("namespace", i.namespace).Info("Trident installation namespace already has the correct labels.")
return nil
} else {
Log().WithField("namespace", i.namespace).Info("Namespace missing labels. Attempting to patch now.")
}

// Patch namespace
err := i.client.PatchNamespaceLabels(i.namespace, map[string]string{
commonconfig.PodSecurityStandardsEnforceLabel: commonconfig.PodSecurityStandardsEnforceProfile,
Expand Down
39 changes: 39 additions & 0 deletions operator/controllers/orchestrator/installer/installer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1857,6 +1857,19 @@ func TestInstallOrPatchTrident(t *testing.T) {
assert.Empty(t, acpVersion)
})

t.Run("namespace exists but missing labels", func(t *testing.T) {
// Setup: namespace exists BUT labels check fails
mockK8sClient.EXPECT().CheckNamespaceExists(installer.namespace).Return(true, nil)
mockK8sClient.EXPECT().CheckNamespaceLabels(installer.namespace, mock.Anything).Return(false)

// Maybe we expect it to patch the labels?
mockK8sClient.EXPECT().PatchNamespaceLabels(installer.namespace, mock.Anything).Return(nil)

// When we call the function, it should handle the missing labels
err := installer.createOrPatchTridentInstallationNamespace()
assert.NoError(t, err)
})

t.Run("failure creating RBAC objects", func(t *testing.T) {
// Mock setInstallationParams to succeed
mockK8sClient.EXPECT().ServerVersion().Return(&version.Version{}).AnyTimes()
Expand Down Expand Up @@ -3204,6 +3217,32 @@ func TestCreateOrPatchTridentInstallationNamespace(t *testing.T) {
assert.Contains(t, err.Error(), "failed to create Trident installation namespace")
})

t.Run("failure checking if the namespace has labels", func(t *testing.T) {
// Mock CheckNamespaceExists to return true (namespace exists)
mockK8sClient.EXPECT().CheckNamespaceExists(installer.namespace).Return(true, nil)

// Mock GetNamespace to return namespace WITHOUT the required labels
mockK8sClient.EXPECT().GetNamespace(installer.namespace).Return(
&corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: installer.namespace,
Labels: map[string]string{}, // Empty labels
},
},
nil,
)

// Mock CheckNamespaceLabels to return false (labels missing)
mockK8sClient.EXPECT().CheckNamespaceLabels(
installer.namespace,
map[string]string{"kubernetes.io/metadata.name": installer.namespace},
).Return(false)

err := installer.createOrPatchTridentInstallationNamespace()
assert.Error(t, err, "expected error when checking namespace labels fails")
assert.Contains(t, err.Error(), "Trident installation namespace does not have expected labels")
})

t.Run("failure patching namespace labels", func(t *testing.T) {
// Mock CheckNamespaceExists to return true (namespace exists)
mockK8sClient.EXPECT().CheckNamespaceExists(installer.namespace).Return(true, nil)
Expand Down