github.com/verrazzano/verrazzano@v1.7.0/platform-operator/namespacewatch/namespace_watcher_test.go (about)

     1  // Copyright (c) 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package namespacewatch
     5  
     6  import (
     7  	"context"
     8  	asserts "github.com/stretchr/testify/assert"
     9  	"github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1"
    10  	"github.com/verrazzano/verrazzano/platform-operator/constants"
    11  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/rancher"
    12  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/spi"
    13  	"github.com/verrazzano/verrazzano/platform-operator/internal/config"
    14  	appsv1 "k8s.io/api/apps/v1"
    15  	v1 "k8s.io/api/core/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/apimachinery/pkg/runtime"
    18  	"k8s.io/apimachinery/pkg/runtime/schema"
    19  	"k8s.io/apimachinery/pkg/types"
    20  	k8scheme "k8s.io/client-go/kubernetes/scheme"
    21  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    22  	"testing"
    23  	"time"
    24  )
    25  
    26  const (
    27  	reldir          = "../manifests/profiles"
    28  	cattleSystem    = "cattle-system"
    29  	testBomFilePath = "../controllers/verrazzano/testdata/test_bom.json"
    30  )
    31  
    32  var period = time.Duration(10) * time.Second
    33  var testScheme = runtime.NewScheme()
    34  
    35  func init() {
    36  	_ = k8scheme.AddToScheme(testScheme)
    37  	_ = v1alpha1.AddToScheme(testScheme)
    38  }
    39  
    40  func TestStart(t *testing.T) {
    41  	client := fake.NewClientBuilder().WithScheme(testScheme).WithObjects().Build()
    42  	ctx := spi.NewFakeContext(client, nil, nil, false)
    43  	namespaceWatcher = NewNamespaceWatcher(ctx.Client(), period)
    44  	asserts.Nil(t, namespaceWatcher.shutdown)
    45  	namespaceWatcher.Start()
    46  	asserts.NotNil(t, namespaceWatcher.shutdown)
    47  	namespaceWatcher.Start()
    48  	asserts.NotNil(t, namespaceWatcher.shutdown)
    49  	namespaceWatcher.Pause()
    50  	asserts.Nil(t, namespaceWatcher.shutdown)
    51  	namespaceWatcher.Pause()
    52  	asserts.Nil(t, namespaceWatcher.shutdown)
    53  }
    54  
    55  // TestNotToMoveSystemNamespacesWhenRancherNotReady tests the following cases
    56  // GIVEN that rancher component is enabled and in not ready state in Verrazzano installation
    57  // OR when subcomponents are not ready
    58  // THEN no operation takes place
    59  func TestNotToMoveSystemNamespacesWhenRancherNotReady(t *testing.T) {
    60  	namespace1 := &v1.Namespace{
    61  		ObjectMeta: metav1.ObjectMeta{
    62  			Name: "verrazzano-system",
    63  			Labels: map[string]string{
    64  				constants.VerrazzanoManagedKey: "verrazzano-system",
    65  			},
    66  		},
    67  	}
    68  	enabled := true
    69  	var availability v1alpha1.ComponentAvailability = "Available"
    70  	vzCR := &v1alpha1.Verrazzano{
    71  		ObjectMeta: metav1.ObjectMeta{
    72  			Namespace: "default",
    73  			Name:      "example",
    74  		},
    75  		Spec: v1alpha1.VerrazzanoSpec{
    76  			Components: v1alpha1.ComponentSpec{
    77  				Rancher: &v1alpha1.RancherComponent{
    78  					Enabled: &enabled,
    79  				},
    80  			},
    81  		},
    82  		Status: v1alpha1.VerrazzanoStatus{
    83  			Components: v1alpha1.ComponentStatusMap{
    84  				"rancher": &v1alpha1.ComponentStatusDetails{
    85  					Name:      "rancher",
    86  					State:     "Ready",
    87  					Available: &availability,
    88  				},
    89  			},
    90  		},
    91  	}
    92  
    93  	testScheme.AddKnownTypeWithName(schema.GroupVersionKind{
    94  		Group:   "install.verrazzano.io/v1alpha1",
    95  		Kind:    "Verrazzano",
    96  		Version: "v1alpha1",
    97  	}, &v1alpha1.Verrazzano{})
    98  	config.TestProfilesDir = reldir
    99  	defer func() { config.TestProfilesDir = "" }()
   100  	client := fake.NewClientBuilder().WithScheme(testScheme).WithObjects(vzCR,
   101  		newReplicaSet(cattleSystem, "rancher"),
   102  		newReplicaSet(cattleSystem, "rancher-webhook"),
   103  		newReplicaSet("cattle-fleet-system", "gitjob"),
   104  		newReplicaSet("cattle-fleet-system", "fleet-controller"),
   105  		newReplicaSet("cattle-fleet-local-system", "fleet-agent"),
   106  		newPod("cattle-system", "rancher"), namespace1).Build()
   107  	ctx := spi.NewFakeContext(client, vzCR, nil, false)
   108  	namespaceWatcher = NewNamespaceWatcher(ctx.Client(), period)
   109  	projectID := "p-47cnm"
   110  	err := namespaceWatcher.MoveSystemNamespacesToRancherSystemProject(projectID, "")
   111  	asserts.NoError(t, err)
   112  	ns := v1.Namespace{}
   113  	asserts.NoError(t, client.Get(context.Background(), types.NamespacedName{Name: "verrazzano-system"}, &ns))
   114  	asserts.Equal(t, ns.Annotations[RancherProjectIDLabelKey], "")
   115  }
   116  
   117  // TestToNotMoveSystemNamespaces tests the following cases
   118  // GIVEN that rancher component is enabled and in ready state in Verrazzano installation
   119  // When namespaces on the cluster does not have label "verrazzano.io/namespace"
   120  // THEN the namespace  is ignored
   121  func TestToNotMoveSystemNamespacesWhenNoSystemNSLabel(t *testing.T) {
   122  	oldBomPath := config.GetDefaultBOMFilePath()
   123  	config.SetDefaultBomFilePath(testBomFilePath)
   124  	defer config.SetDefaultBomFilePath(oldBomPath)
   125  
   126  	namespace1 := &v1.Namespace{
   127  		ObjectMeta: metav1.ObjectMeta{
   128  			Name: "verrazzano-system",
   129  		},
   130  	}
   131  	enabled := true
   132  	var availability v1alpha1.ComponentAvailability = "Available"
   133  	vzCR := &v1alpha1.Verrazzano{
   134  		ObjectMeta: metav1.ObjectMeta{
   135  			Namespace: "default",
   136  			Name:      "example",
   137  		},
   138  		Spec: v1alpha1.VerrazzanoSpec{
   139  			Components: v1alpha1.ComponentSpec{
   140  				Rancher: &v1alpha1.RancherComponent{
   141  					Enabled: &enabled,
   142  				},
   143  			},
   144  		},
   145  		Status: v1alpha1.VerrazzanoStatus{
   146  			Components: v1alpha1.ComponentStatusMap{
   147  				"rancher": &v1alpha1.ComponentStatusDetails{
   148  					Name:      "rancher",
   149  					State:     "Ready",
   150  					Available: &availability,
   151  				},
   152  			},
   153  		},
   154  	}
   155  
   156  	testScheme.AddKnownTypeWithName(schema.GroupVersionKind{
   157  		Group:   "install.verrazzano.io/v1alpha1",
   158  		Kind:    "Verrazzano",
   159  		Version: "v1alpha1",
   160  	}, &v1alpha1.Verrazzano{})
   161  	config.TestProfilesDir = reldir
   162  	defer func() { config.TestProfilesDir = "" }()
   163  	client := fake.NewClientBuilder().WithScheme(testScheme).WithObjects(vzCR,
   164  		newReplicaSet(rancher.ComponentNamespace, "rancher"),
   165  		newReplicaSet(rancher.ComponentNamespace, "rancher-webhook"),
   166  		newReplicaSet(rancher.FleetSystemNamespace, "gitjob"),
   167  		newReplicaSet(rancher.FleetSystemNamespace, "fleet-controller"),
   168  		newReplicaSet(rancher.FleetLocalSystemNamespace, "fleet-agent"),
   169  		newPod(rancher.ComponentNamespace, "rancher"),
   170  		newPod(rancher.ComponentNamespace, "rancher-webhook"),
   171  		newPod(rancher.FleetSystemNamespace, "gitjob"),
   172  		newPod(rancher.FleetSystemNamespace, "fleet-controller"),
   173  		newPod(rancher.FleetLocalSystemNamespace, "fleet-agent"),
   174  		newReadyDeployment(rancher.ComponentNamespace, "rancher"),
   175  		newReadyDeploymentWithImage(rancher.ComponentNamespace, "rancher-webhook", "rancher-webhook:v0.2.6-20221005161115-fee4a23"),
   176  		newReadyDeployment(rancher.FleetSystemNamespace, "gitjob"),
   177  		newReadyDeployment(rancher.FleetSystemNamespace, "fleet-controller"),
   178  		newReadyDeployment(rancher.FleetLocalSystemNamespace, "fleet-agent"), namespace1).Build()
   179  	ctx := spi.NewFakeContext(client, vzCR, nil, false)
   180  	namespaceWatcher = NewNamespaceWatcher(ctx.Client(), period)
   181  	projectID := "p-47cnm"
   182  	clusterName := "local"
   183  	err := namespaceWatcher.MoveSystemNamespacesToRancherSystemProject(projectID, clusterName)
   184  	asserts.NoError(t, err)
   185  	ns := v1.Namespace{}
   186  	asserts.NoError(t, client.Get(context.Background(), types.NamespacedName{Name: "verrazzano-system"}, &ns))
   187  	asserts.NotEqual(t, ns.Annotations[RancherProjectIDLabelKey], clusterName+":"+projectID)
   188  }
   189  
   190  // TestMoveSystemNamespaces tests the following cases
   191  // GIVEN that rancher component is enabled and in ready state in Verrazzano installation
   192  // When namespaces on the cluster has a label "verrazzano.io/namespace"
   193  // And when namespaces on the cluster does not have a label management.cattle.io/system-namespace
   194  // THEN the method retrieves the System project ID from the rancher
   195  // And updates the namespace annotation and label with the Project ID.
   196  func TestMoveSystemNamespaces(t *testing.T) {
   197  	oldBomPath := config.GetDefaultBOMFilePath()
   198  	config.SetDefaultBomFilePath(testBomFilePath)
   199  	defer config.SetDefaultBomFilePath(oldBomPath)
   200  
   201  	namespace1 := &v1.Namespace{
   202  		ObjectMeta: metav1.ObjectMeta{
   203  			Name: "verrazzano-system",
   204  			Labels: map[string]string{
   205  				constants.VerrazzanoManagedKey: "verrazzano-system",
   206  			},
   207  		},
   208  	}
   209  	enabled := true
   210  	var availability v1alpha1.ComponentAvailability = "Available"
   211  	vzCR := &v1alpha1.Verrazzano{
   212  		ObjectMeta: metav1.ObjectMeta{
   213  			Namespace: "default",
   214  			Name:      "example",
   215  		},
   216  		Spec: v1alpha1.VerrazzanoSpec{
   217  			Components: v1alpha1.ComponentSpec{
   218  				Rancher: &v1alpha1.RancherComponent{
   219  					Enabled: &enabled,
   220  				},
   221  			},
   222  		},
   223  		Status: v1alpha1.VerrazzanoStatus{
   224  			Components: v1alpha1.ComponentStatusMap{
   225  				"rancher": &v1alpha1.ComponentStatusDetails{
   226  					Name:      "rancher",
   227  					State:     "Ready",
   228  					Available: &availability,
   229  				},
   230  			},
   231  		},
   232  	}
   233  
   234  	testScheme.AddKnownTypeWithName(schema.GroupVersionKind{
   235  		Group:   "install.verrazzano.io/v1alpha1",
   236  		Kind:    "Verrazzano",
   237  		Version: "v1alpha1",
   238  	}, &v1alpha1.Verrazzano{})
   239  	config.TestProfilesDir = reldir
   240  	defer func() { config.TestProfilesDir = "" }()
   241  	client := fake.NewClientBuilder().WithScheme(testScheme).WithObjects(vzCR,
   242  		newReplicaSet(rancher.ComponentNamespace, "rancher"),
   243  		newReplicaSet(rancher.ComponentNamespace, "rancher-webhook"),
   244  		newReplicaSet(rancher.FleetSystemNamespace, "gitjob"),
   245  		newReplicaSet(rancher.FleetSystemNamespace, "fleet-controller"),
   246  		newReplicaSet(rancher.FleetLocalSystemNamespace, "fleet-agent"),
   247  		newPod(rancher.ComponentNamespace, "rancher"),
   248  		newPod(rancher.ComponentNamespace, "rancher-webhook"),
   249  		newPod(rancher.FleetSystemNamespace, "gitjob"),
   250  		newPod(rancher.FleetSystemNamespace, "fleet-controller"),
   251  		newPod(rancher.FleetLocalSystemNamespace, "fleet-agent"),
   252  		newReadyDeployment(rancher.ComponentNamespace, "rancher"),
   253  		newReadyDeploymentWithImage(rancher.ComponentNamespace, "rancher-webhook", "rancher-webhook:v0.2.6-20221005161115-fee4a23"),
   254  		newReadyDeployment(rancher.FleetSystemNamespace, "gitjob"),
   255  		newReadyDeployment(rancher.FleetSystemNamespace, "fleet-controller"),
   256  		newReadyDeployment(rancher.FleetLocalSystemNamespace, "fleet-agent"), namespace1).Build()
   257  	ctx := spi.NewFakeContext(client, vzCR, nil, false)
   258  	namespaceWatcher = NewNamespaceWatcher(ctx.Client(), period)
   259  	projectID := "p-47cnm"
   260  	clusterName := "local"
   261  	err := namespaceWatcher.MoveSystemNamespacesToRancherSystemProject(projectID, clusterName)
   262  	asserts.NoError(t, err)
   263  	ns := v1.Namespace{}
   264  	asserts.NoError(t, client.Get(context.Background(), types.NamespacedName{Name: "verrazzano-system"}, &ns))
   265  	asserts.Equal(t, ns.Annotations[RancherProjectIDLabelKey], clusterName+":"+projectID)
   266  }
   267  
   268  func newPod(namespace string, name string) *v1.Pod {
   269  	return &v1.Pod{
   270  		ObjectMeta: metav1.ObjectMeta{
   271  			Namespace: namespace,
   272  			Name:      name,
   273  			Labels: map[string]string{
   274  				"app":               name,
   275  				"pod-template-hash": "95d8c5d97",
   276  			},
   277  		},
   278  	}
   279  }
   280  
   281  func newReplicaSet(namespace string, name string) *appsv1.ReplicaSet {
   282  	return &appsv1.ReplicaSet{
   283  		ObjectMeta: metav1.ObjectMeta{
   284  			Namespace:   namespace,
   285  			Name:        name + "-95d8c5d97",
   286  			Annotations: map[string]string{"deployment.kubernetes.io/revision": "1"},
   287  		},
   288  	}
   289  }
   290  
   291  // Create a new deployment object for testing
   292  func newReadyDeployment(namespace string, name string) *appsv1.Deployment {
   293  	return &appsv1.Deployment{
   294  		ObjectMeta: metav1.ObjectMeta{
   295  			Namespace: namespace,
   296  			Name:      name,
   297  			Labels:    map[string]string{"app": name},
   298  		},
   299  		Spec: appsv1.DeploymentSpec{
   300  			Selector: &metav1.LabelSelector{
   301  				MatchLabels: map[string]string{"app": name},
   302  			},
   303  		},
   304  		Status: appsv1.DeploymentStatus{
   305  			AvailableReplicas: 1,
   306  			Replicas:          1,
   307  			UpdatedReplicas:   1,
   308  		},
   309  	}
   310  }
   311  
   312  func newReadyDeploymentWithImage(namespace string, name string, image string) *appsv1.Deployment {
   313  	return &appsv1.Deployment{
   314  		ObjectMeta: metav1.ObjectMeta{
   315  			Namespace: namespace,
   316  			Name:      name,
   317  			Labels:    map[string]string{"app": name},
   318  		},
   319  		Spec: appsv1.DeploymentSpec{
   320  			Selector: &metav1.LabelSelector{
   321  				MatchLabels: map[string]string{"app": name},
   322  			},
   323  			Template: v1.PodTemplateSpec{
   324  				Spec: v1.PodSpec{
   325  					Containers: []v1.Container{{Image: image}},
   326  				},
   327  			},
   328  		},
   329  		Status: appsv1.DeploymentStatus{
   330  			AvailableReplicas: 1,
   331  			Replicas:          1,
   332  			UpdatedReplicas:   1,
   333  		},
   334  	}
   335  }