github.com/verrazzano/verrazzano@v1.7.1/cluster-operator/controllers/vmc/push_manifest_objects_test.go (about)

     1  // Copyright (c) 2022, 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 vmc
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"io"
    10  	"net/http"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/golang/mock/gomock"
    15  	asserts "github.com/stretchr/testify/assert"
    16  	"github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1"
    17  	pkgconst "github.com/verrazzano/verrazzano/pkg/constants"
    18  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    19  	"github.com/verrazzano/verrazzano/pkg/rancherutil"
    20  	"github.com/verrazzano/verrazzano/pkg/test/mockmatchers"
    21  	"github.com/verrazzano/verrazzano/platform-operator/constants"
    22  	"github.com/verrazzano/verrazzano/platform-operator/mocks"
    23  	corev1 "k8s.io/api/core/v1"
    24  	networkv1 "k8s.io/api/networking/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/util/wait"
    27  	"sigs.k8s.io/controller-runtime/pkg/client"
    28  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    29  )
    30  
    31  // TestPushManifestObjects tests the push of manifest objects to a managed cluster
    32  // GIVEN a call to push manifest objects
    33  //
    34  //	WHEN the status of the VMC does not contain the condition update
    35  //	THEN the manifest objects should get pushed to the managed cluster
    36  func TestPushManifestObjects(t *testing.T) {
    37  	a := asserts.New(t)
    38  	c := generateClientObjects()
    39  
    40  	savedRancherHTTPClient := rancherutil.RancherHTTPClient
    41  	defer func() {
    42  		rancherutil.RancherHTTPClient = savedRancherHTTPClient
    43  	}()
    44  
    45  	savedRetry := rancherutil.DefaultRetry
    46  	defer func() {
    47  		rancherutil.DefaultRetry = savedRetry
    48  	}()
    49  	rancherutil.DefaultRetry = wait.Backoff{
    50  		Steps:    1,
    51  		Duration: 1 * time.Millisecond,
    52  		Factor:   1.0,
    53  		Jitter:   0.1,
    54  	}
    55  
    56  	vmc := &v1alpha1.VerrazzanoManagedCluster{
    57  		ObjectMeta: metav1.ObjectMeta{
    58  			Namespace: rancherNamespace,
    59  			Name:      "cluster",
    60  		},
    61  		Status: v1alpha1.VerrazzanoManagedClusterStatus{
    62  			RancherRegistration: v1alpha1.RancherRegistration{
    63  				ClusterID: "cluster-id",
    64  			},
    65  		},
    66  	}
    67  	r := &VerrazzanoManagedClusterReconciler{
    68  		Client: c,
    69  		log:    vzlog.DefaultLogger(),
    70  	}
    71  
    72  	statusTrueVMC := vmc.DeepCopy()
    73  	statusTrueVMC.Status.Conditions = append(statusTrueVMC.Status.Conditions, v1alpha1.Condition{
    74  		Type:   v1alpha1.ConditionManifestPushed,
    75  		Status: corev1.ConditionTrue,
    76  	})
    77  
    78  	tests := []struct {
    79  		name       string
    80  		vmc        *v1alpha1.VerrazzanoManagedCluster
    81  		updated    bool
    82  		active     bool
    83  		vzNsExists bool
    84  		mockerFunc func(*mocks.MockRequestSender, *asserts.Assertions, *v1alpha1.VerrazzanoManagedCluster, *VerrazzanoManagedClusterReconciler, string) *mocks.MockRequestSender
    85  		mock       *mocks.MockRequestSender
    86  	}{
    87  		{
    88  			name:       "test not active",
    89  			vmc:        vmc,
    90  			updated:    false,
    91  			active:     false,
    92  			vzNsExists: false,
    93  		},
    94  		{
    95  			name:       "test no verrazzano-system namespace",
    96  			vmc:        vmc,
    97  			updated:    false,
    98  			active:     true,
    99  			vzNsExists: false,
   100  		},
   101  		{
   102  			name:       "test active",
   103  			vmc:        vmc,
   104  			updated:    true,
   105  			active:     true,
   106  			vzNsExists: true,
   107  		},
   108  	}
   109  	for _, tt := range tests {
   110  		t.Run(tt.name, func(t *testing.T) {
   111  			// clear any cached user auth tokens when the test completes
   112  			defer rancherutil.DeleteStoredTokens()
   113  			mocker := gomock.NewController(t)
   114  			sender := addManagedClusterMocks(mocker, tt.active, tt.vzNsExists, a, vmc, r)
   115  			rancherutil.RancherHTTPClient = sender
   116  			updated, err := r.pushManifestObjects(context.TODO(), true, tt.vmc)
   117  			a.Equal(tt.updated, updated)
   118  			a.NoError(err)
   119  			mocker.Finish()
   120  		})
   121  	}
   122  }
   123  
   124  func addManagedClusterMocks(mocker *gomock.Controller, clusterIsActive bool, vzNsExists bool, a *asserts.Assertions, vmc *v1alpha1.VerrazzanoManagedCluster, r *VerrazzanoManagedClusterReconciler) *mocks.MockRequestSender {
   125  	sender := mocks.NewMockRequestSender(mocker)
   126  	clusterID := vmc.Status.RancherRegistration.ClusterID
   127  	if clusterIsActive && vzNsExists {
   128  		return addActiveClusterNSExistsMock(sender, a, vmc, r, clusterID)
   129  	}
   130  	if !clusterIsActive {
   131  		return addInactiveClusterMock(sender, clusterID)
   132  	}
   133  	return addNoVerrazzanoSystemNSMock(sender, a, vmc, r, clusterID)
   134  }
   135  
   136  func generateClientObjects() client.WithWatch {
   137  	return fake.NewClientBuilder().WithRuntimeObjects(
   138  		&networkv1.Ingress{
   139  			ObjectMeta: metav1.ObjectMeta{
   140  				Namespace: rancherNamespace,
   141  				Name:      rancherIngressName,
   142  			},
   143  			Spec: networkv1.IngressSpec{
   144  				Rules: []networkv1.IngressRule{
   145  					{
   146  						Host: "test-rancher.com",
   147  					},
   148  				},
   149  			},
   150  		},
   151  		&corev1.Secret{
   152  			ObjectMeta: metav1.ObjectMeta{
   153  				Namespace: rancherNamespace,
   154  				Name:      rancherTLSSecret,
   155  			},
   156  			Data: map[string][]byte{
   157  				"ca.crt": []byte(""),
   158  			},
   159  		},
   160  		&corev1.Secret{
   161  			ObjectMeta: metav1.ObjectMeta{
   162  				Namespace: constants.VerrazzanoMultiClusterNamespace,
   163  				Name:      pkgconst.VerrazzanoClusterRancherName,
   164  			},
   165  			Data: map[string][]byte{
   166  				"password": []byte(""),
   167  			},
   168  		},
   169  		&corev1.Secret{
   170  			ObjectMeta: metav1.ObjectMeta{
   171  				Namespace: rancherNamespace,
   172  				Name:      GetAgentSecretName("cluster"),
   173  			},
   174  		},
   175  		&corev1.Secret{
   176  			ObjectMeta: metav1.ObjectMeta{
   177  				Namespace: rancherNamespace,
   178  				Name:      GetRegistrationSecretName("cluster"),
   179  			},
   180  		},
   181  		&networkv1.Ingress{
   182  			ObjectMeta: metav1.ObjectMeta{
   183  				Name:      rancherNamespace,
   184  				Namespace: rancherIngressName,
   185  			},
   186  			Spec: networkv1.IngressSpec{Rules: []networkv1.IngressRule{{Host: "rancher.unit-test.com"}}},
   187  		},
   188  		&corev1.Secret{
   189  			ObjectMeta: metav1.ObjectMeta{
   190  				Namespace: rancherNamespace,
   191  				Name:      rancherAdminSecret,
   192  			},
   193  			Data: map[string][]byte{
   194  				"password": []byte("super-secret"),
   195  			},
   196  		},
   197  	).Build()
   198  }
   199  
   200  func addInactiveClusterMock(httpMock *mocks.MockRequestSender, clusterID string) *mocks.MockRequestSender {
   201  	addTokenMock(httpMock)
   202  
   203  	httpMock.EXPECT().
   204  		Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clustersPath+"/"+clusterID)).
   205  		DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) {
   206  			var resp *http.Response
   207  			r := io.NopCloser(bytes.NewReader([]byte(`{"state":"inactive", "agentImage":"test"}`)))
   208  			resp = &http.Response{
   209  				StatusCode: http.StatusOK,
   210  				Body:       r,
   211  			}
   212  			return resp, nil
   213  		}).Times(2)
   214  	return httpMock
   215  }
   216  
   217  func addNoVerrazzanoSystemNSMock(httpMock *mocks.MockRequestSender, a *asserts.Assertions, vmc *v1alpha1.VerrazzanoManagedCluster, r *VerrazzanoManagedClusterReconciler, clusterID string) *mocks.MockRequestSender {
   218  	addTokenMock(httpMock)
   219  	addActiveClusterMock(httpMock, 2)
   220  	addVerrazzanoSystemNamespaceMock(httpMock, clusterID, false)
   221  	return httpMock
   222  }
   223  
   224  func addActiveClusterNSExistsMock(httpMock *mocks.MockRequestSender, a *asserts.Assertions, vmc *v1alpha1.VerrazzanoManagedCluster, r *VerrazzanoManagedClusterReconciler, clusterID string) *mocks.MockRequestSender {
   225  	addActiveClusterMock(httpMock, 2)
   226  	addVerrazzanoSystemNamespaceMock(httpMock, clusterID, true)
   227  	agentSecret, err := r.getSecret(vmc.Namespace, GetAgentSecretName(vmc.Name), true)
   228  	a.NoError(err)
   229  	agentSecret.Namespace = constants.VerrazzanoSystemNamespace
   230  	agentSecret.Name = constants.MCAgentSecret
   231  	httpMock = addNotFoundMock(httpMock, &agentSecret, clusterID)
   232  
   233  	regSecret, err := r.getSecret(vmc.Namespace, GetRegistrationSecretName(vmc.Name), true)
   234  	a.NoError(err)
   235  	regSecret.Namespace = constants.VerrazzanoSystemNamespace
   236  	regSecret.Name = constants.MCRegistrationSecret
   237  	httpMock = addNotFoundMock(httpMock, &regSecret, clusterID)
   238  	return httpMock
   239  }
   240  
   241  func addActiveClusterMock(httpMock *mocks.MockRequestSender, times int) *mocks.MockRequestSender {
   242  	httpMock = addTokenMock(httpMock)
   243  	expectActiveCluster(httpMock, times)
   244  	return httpMock
   245  }
   246  
   247  func expectActiveCluster(httpMock *mocks.MockRequestSender, times int) {
   248  	httpMock.EXPECT().
   249  		Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clustersPath+"/"+clusterID)).
   250  		DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) {
   251  			var resp *http.Response
   252  			r := io.NopCloser(bytes.NewReader([]byte(`{"state":"active", "agentImage":"test"}`)))
   253  			resp = &http.Response{
   254  				StatusCode: http.StatusOK,
   255  				Body:       r,
   256  			}
   257  			return resp, nil
   258  		}).Times(times)
   259  }
   260  
   261  func addTokenMock(httpMock *mocks.MockRequestSender) *mocks.MockRequestSender {
   262  	httpMock.EXPECT().
   263  		Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(loginURIPath)).
   264  		DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) {
   265  			r := io.NopCloser(bytes.NewReader([]byte(`{"token":"unit-test-token"}`)))
   266  			resp := &http.Response{
   267  				StatusCode: http.StatusCreated,
   268  				Body:       r,
   269  				Request:    &http.Request{Method: http.MethodPost},
   270  			}
   271  			return resp, nil
   272  		}).AnyTimes()
   273  	return httpMock
   274  }
   275  
   276  func addVerrazzanoSystemNamespaceMock(httpMock *mocks.MockRequestSender, clusterID string, exists bool) *mocks.MockRequestSender {
   277  	status := http.StatusOK
   278  	if !exists {
   279  		status = http.StatusNotFound
   280  	}
   281  	httpMock.EXPECT().
   282  		Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(k8sClustersPath+clusterID+"/api/v1/namespaces/verrazzano-system")).
   283  		DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) {
   284  			var resp *http.Response
   285  			r := io.NopCloser(bytes.NewReader([]byte(`{"kind":"table", "apiVersion":"meta.k8s.io/v1"}`)))
   286  			resp = &http.Response{
   287  				StatusCode: status,
   288  				Body:       r,
   289  			}
   290  			return resp, nil
   291  		})
   292  
   293  	return httpMock
   294  }