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