github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_secret_test.go (about) 1 // Copyright (c) 2021, 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 mcagent 5 6 import ( 7 "context" 8 "encoding/json" 9 "path/filepath" 10 "testing" 11 12 "github.com/golang/mock/gomock" 13 asserts "github.com/stretchr/testify/assert" 14 clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1" 15 clusterstest "github.com/verrazzano/verrazzano/application-operator/controllers/clusters/test" 16 "github.com/verrazzano/verrazzano/application-operator/mocks" 17 "go.uber.org/zap" 18 "k8s.io/apimachinery/pkg/api/errors" 19 "k8s.io/apimachinery/pkg/runtime/schema" 20 "k8s.io/apimachinery/pkg/types" 21 "sigs.k8s.io/controller-runtime/pkg/client" 22 ) 23 24 const testClusterName = "managed1" 25 const testMCSecretNamespace = "unit-mcsecret-namespace" //nolint:gosec //#gosec G101 26 const testMCSecretName = "unit-mcsecret" 27 28 var mcSecretTestLabels = map[string]string{"label1": "test1"} 29 var mcSecretTestUpdatedLabels = map[string]string{"label1": "test1updated"} 30 31 // TestCreateMCSecret tests the synchronization method for the following use case. 32 // GIVEN a request to sync MultiClusterSecret objects 33 // WHEN the a new object exists 34 // THEN ensure that the MultiClusterSecret is created. 35 func TestCreateMCSecret(t *testing.T) { 36 assert := asserts.New(t) 37 log := zap.S().With("test") 38 39 // Managed cluster mocks 40 mcMocker := gomock.NewController(t) 41 mcMock := mocks.NewMockClient(mcMocker) 42 43 // Admin cluster mocks 44 adminMocker := gomock.NewController(t) 45 adminMock := mocks.NewMockClient(adminMocker) 46 47 // Test data 48 testMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml") 49 assert.NoError(err, "failed to get sample secret data") 50 51 // Admin Cluster - expect call to list MultiClusterSecret objects - return list with one object 52 adminMock.EXPECT(). 53 List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())). 54 DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error { 55 assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace") 56 mcSecretList.Items = append(mcSecretList.Items, testMCSecret) 57 return nil 58 }) 59 60 // Managed Cluster - expect call to get a MultiClusterSecret secret from the list returned by the admin cluster 61 // Return the resource does not exist 62 mcMock.EXPECT(). 63 Get(gomock.Any(), types.NamespacedName{Namespace: testMCSecretNamespace, Name: testMCSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 64 Return(errors.NewNotFound(schema.GroupResource{Group: "clusters.verrazzano.io", Resource: "MultiClusterSecret"}, testMCSecretName)) 65 66 // Managed Cluster - expect call to create a MultiClusterSecret 67 mcMock.EXPECT(). 68 Create(gomock.Any(), gomock.Any()). 69 DoAndReturn(func(ctx context.Context, mcSecret *clustersv1alpha1.MultiClusterSecret, opts ...client.CreateOption) error { 70 assert.Equal(testMCSecretNamespace, mcSecret.Namespace, "mcsecret namespace did not match") 71 assert.Equal(testMCSecretName, mcSecret.Name, "mcsecret name did not match") 72 assert.Equal(mcSecretTestLabels, mcSecret.Labels, "mcsecret labels did not match") 73 assert.Equal(testClusterName, mcSecret.Spec.Placement.Clusters[0].Name, "mcsecret does not contain expected placement") 74 assert.Equal([]byte("verrazzano"), mcSecret.Spec.Template.Data["username"], "mcsecret does not contain expected template data") 75 assert.Equal("test-stringdata", mcSecret.Spec.Template.StringData["test"], "mcsecret does not contain expected string data") 76 return nil 77 }) 78 79 // Managed Cluster - expect call to list MultiClusterSecret objects - return same list as admin cluster 80 mcMock.EXPECT(). 81 List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())). 82 DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error { 83 assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace") 84 mcSecretList.Items = append(mcSecretList.Items, testMCSecret) 85 return nil 86 }) 87 88 // Make the request 89 s := &Syncer{ 90 AdminClient: adminMock, 91 LocalClient: mcMock, 92 Log: log, 93 ManagedClusterName: testClusterName, 94 Context: context.TODO(), 95 } 96 err = s.syncMCSecretObjects(testMCSecretNamespace) 97 98 // Validate the results 99 adminMocker.Finish() 100 mcMocker.Finish() 101 assert.NoError(err) 102 } 103 104 // TestUpdateMCSecret tests the synchronization method for the following use case. 105 // GIVEN a request to sync MultiClusterSecret objects 106 // WHEN the a object exists 107 // THEN ensure that the MultiClusterSecret is updated. 108 func TestUpdateMCSecret(t *testing.T) { 109 assert := asserts.New(t) 110 log := zap.S().With("test") 111 112 // Managed cluster mocks 113 mcMocker := gomock.NewController(t) 114 mcMock := mocks.NewMockClient(mcMocker) 115 116 // Admin cluster mocks 117 adminMocker := gomock.NewController(t) 118 adminMock := mocks.NewMockClient(adminMocker) 119 120 // Test data 121 testMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml") 122 assert.NoError(err, "failed to get sample secret data") 123 testMCSecretUpdate, err := getSampleMCSecret("testdata/multicluster-secret-update.yaml") 124 assert.NoError(err, "failed to get sample secret data") 125 126 // Admin Cluster - expect call to list MultiClusterSecret objects - return list with one object 127 adminMock.EXPECT(). 128 List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())). 129 DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error { 130 assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace") 131 mcSecretList.Items = append(mcSecretList.Items, testMCSecretUpdate) 132 return nil 133 }) 134 135 // Managed Cluster - expect call to get a MultiClusterSecret secret from the list returned by the admin cluster 136 // Return the resource with some values different than what the admin cluster returned 137 mcMock.EXPECT(). 138 Get(gomock.Any(), types.NamespacedName{Namespace: testMCSecretNamespace, Name: testMCSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 139 DoAndReturn(func(ctx context.Context, name types.NamespacedName, mcSecret *clustersv1alpha1.MultiClusterSecret, opts ...client.GetOption) error { 140 testMCSecret.DeepCopyInto(mcSecret) 141 return nil 142 }) 143 144 // Managed Cluster - expect call to update a MultiClusterSecret 145 // Verify request had the updated values 146 mcMock.EXPECT(). 147 Update(gomock.Any(), gomock.Any()). 148 DoAndReturn(func(ctx context.Context, mcSecret *clustersv1alpha1.MultiClusterSecret, opts ...client.UpdateOption) error { 149 assert.Equal(testMCSecretNamespace, mcSecret.Namespace, "mcsecret namespace did not match") 150 assert.Equal(testMCSecretName, mcSecret.Name, "mcsecret name did not match") 151 assert.Equal(mcSecretTestUpdatedLabels, mcSecret.Labels, "mcsecret labels did not match") 152 assert.Equal("test-stringdata2", mcSecret.Spec.Template.StringData["test"], "mcsecret does not contain expected string data") 153 assert.Equal([]byte("test"), mcSecret.Spec.Template.Data["username"], "mcsecret does not contain expected data") 154 return nil 155 }) 156 157 // Managed Cluster - expect call to list MultiClusterSecret objects - return same list as admin cluster 158 mcMock.EXPECT(). 159 List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())). 160 DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error { 161 assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace") 162 mcSecretList.Items = append(mcSecretList.Items, testMCSecret) 163 return nil 164 }) 165 166 // Make the request 167 s := &Syncer{ 168 AdminClient: adminMock, 169 LocalClient: mcMock, 170 Log: log, 171 ManagedClusterName: testClusterName, 172 Context: context.TODO(), 173 } 174 err = s.syncMCSecretObjects(testMCSecretNamespace) 175 176 // Validate the results 177 adminMocker.Finish() 178 mcMocker.Finish() 179 assert.NoError(err) 180 } 181 182 // TestMCSecretPlacement tests the synchronization method for the following use case. 183 // GIVEN a request to sync MultiClusterSecret objects 184 // WHEN the a object exists that is not targeted for the cluster 185 // THEN ensure that the MultiClusterSecret is not created or updated 186 func TestMCSecretPlacement(t *testing.T) { 187 assert := asserts.New(t) 188 log := zap.S().With("test") 189 190 // Managed cluster mocks 191 mcMocker := gomock.NewController(t) 192 mcMock := mocks.NewMockClient(mcMocker) 193 194 // Admin cluster mocks 195 adminMocker := gomock.NewController(t) 196 adminMock := mocks.NewMockClient(adminMocker) 197 198 // Test data 199 adminMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml") 200 if err != nil { 201 assert.NoError(err, "failed to read sample data for MultiClusterSecret") 202 } 203 adminMCSecret.Spec.Placement.Clusters[0].Name = "managed2" 204 localMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml") 205 if err != nil { 206 assert.NoError(err, "failed to read sample data for MultiClusterSecret") 207 } 208 209 // Admin Cluster - expect call to list MultiClusterSecret objects - return list with one object 210 adminMock.EXPECT(). 211 List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())). 212 DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error { 213 assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace") 214 mcSecretList.Items = append(mcSecretList.Items, adminMCSecret) 215 return nil 216 }) 217 218 // Managed Cluster - expect call to list MultiClusterSecret objects - return list including a currently locally placed secret 219 mcMock.EXPECT(). 220 List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())). 221 DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error { 222 assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace") 223 mcSecretList.Items = append(mcSecretList.Items, localMCSecret) 224 return nil 225 }) 226 227 // Managed Cluster - expect a call to delete a MultiClusterSecret object 228 mcMock.EXPECT(). 229 Delete(gomock.Any(), gomock.Eq(&localMCSecret), gomock.Any()). 230 Return(nil) 231 232 // Make the request 233 s := &Syncer{ 234 AdminClient: adminMock, 235 LocalClient: mcMock, 236 Log: log, 237 ManagedClusterName: testClusterName, 238 Context: context.TODO(), 239 } 240 err = s.syncMCSecretObjects(testMCSecretNamespace) 241 242 // Validate the results 243 adminMocker.Finish() 244 mcMocker.Finish() 245 assert.NoError(err) 246 } 247 248 // TestDeleteMCSecret tests the synchronization method for the following use case. 249 // GIVEN a request to sync MultiClusterSecret objects 250 // WHEN the object exists on the local cluster but not on the admin cluster 251 // THEN ensure that the MultiClusterSecret is deleted. 252 func TestDeleteMCSecret(t *testing.T) { 253 assert := asserts.New(t) 254 log := zap.S().With("test") 255 256 // Managed cluster mocks 257 mcMocker := gomock.NewController(t) 258 mcMock := mocks.NewMockClient(mcMocker) 259 260 // Admin cluster mocks 261 adminMocker := gomock.NewController(t) 262 adminMock := mocks.NewMockClient(adminMocker) 263 264 // Test data 265 testMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml") 266 if err != nil { 267 assert.NoError(err, "failed to read sample data for MultiClusterSecret") 268 } 269 testMCSecretOrphan, err := getSampleMCSecret("testdata/multicluster-secret.yaml") 270 if err != nil { 271 assert.NoError(err, "failed to read sample data for MultiClusterSecret") 272 } 273 testMCSecretOrphan.Name = "orphaned-resource" 274 275 // Admin Cluster - expect call to list MultiClusterSecret objects - return list with one object 276 adminMock.EXPECT(). 277 List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())). 278 DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error { 279 assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace") 280 mcSecretList.Items = append(mcSecretList.Items, testMCSecret) 281 return nil 282 }) 283 284 // Managed Cluster - expect call to get a MultiClusterSecret from the list returned by the admin cluster 285 // Return the resource 286 mcMock.EXPECT(). 287 Get(gomock.Any(), types.NamespacedName{Namespace: testMCSecretNamespace, Name: testMCSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 288 DoAndReturn(func(ctx context.Context, name types.NamespacedName, mcSecret *clustersv1alpha1.MultiClusterSecret, opts ...client.GetOption) error { 289 testMCSecret.DeepCopyInto(mcSecret) 290 return nil 291 }) 292 293 // Managed Cluster - expect call to list MultiClusterSecret objects - return list including an orphaned object 294 mcMock.EXPECT(). 295 List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())). 296 DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error { 297 assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace") 298 mcSecretList.Items = append(mcSecretList.Items, testMCSecret) 299 mcSecretList.Items = append(mcSecretList.Items, testMCSecretOrphan) 300 return nil 301 }) 302 303 // Managed Cluster - expect a call to delete a MultiClusterSecret object 304 mcMock.EXPECT(). 305 Delete(gomock.Any(), gomock.Eq(&testMCSecretOrphan), gomock.Any()). 306 Return(nil) 307 308 // Make the request 309 s := &Syncer{ 310 AdminClient: adminMock, 311 LocalClient: mcMock, 312 Log: log, 313 ManagedClusterName: testClusterName, 314 Context: context.TODO(), 315 } 316 err = s.syncMCSecretObjects(testMCSecretNamespace) 317 318 // Validate the results 319 adminMocker.Finish() 320 mcMocker.Finish() 321 assert.NoError(err) 322 } 323 324 // getSampleMCSecret creates and returns a sample MultiClusterSecret used in tests 325 func getSampleMCSecret(filePath string) (clustersv1alpha1.MultiClusterSecret, error) { 326 mcSecret := clustersv1alpha1.MultiClusterSecret{} 327 sampleSecretFile, err := filepath.Abs(filePath) 328 if err != nil { 329 return mcSecret, err 330 } 331 332 rawMcSecret, err := clusterstest.ReadYaml2Json(sampleSecretFile) 333 if err != nil { 334 return mcSecret, err 335 } 336 337 err = json.Unmarshal(rawMcSecret, &mcSecret) 338 return mcSecret, err 339 }