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, ®Secret, 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 }