github.com/verrazzano/verrazzano@v1.7.1/application-operator/controllers/appconfig/appconfig_controller_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 appconfig 5 6 import ( 7 "context" 8 "fmt" 9 "testing" 10 11 oamrt "github.com/crossplane/crossplane-runtime/apis/common/v1" 12 oamcore "github.com/crossplane/oam-kubernetes-runtime/apis/core" 13 oamv1 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2" 14 "github.com/golang/mock/gomock" 15 "github.com/prometheus/client_golang/prometheus/testutil" 16 "github.com/stretchr/testify/assert" 17 "github.com/verrazzano/verrazzano/application-operator/metricsexporter" 18 "github.com/verrazzano/verrazzano/application-operator/mocks" 19 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 20 "go.uber.org/zap" 21 appsv1 "k8s.io/api/apps/v1" 22 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/types" 26 k8scheme "k8s.io/client-go/kubernetes/scheme" 27 ctrl "sigs.k8s.io/controller-runtime" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 "sigs.k8s.io/controller-runtime/pkg/client/fake" 30 ) 31 32 const ( 33 testNamespace = "test-ns" 34 testAppConfigName = "test-appconfig" 35 testComponentName = "test-component" 36 testNewRestartVersion = "test-new-restart" 37 testDeploymentName = "test-deployment" 38 testStatefulSetName = "test-statefulset" 39 testDaemonSetName = "test-daemonset" 40 ) 41 42 // newScheme creates a new scheme that includes this package's object to use for testing 43 func newScheme() *runtime.Scheme { 44 scheme := runtime.NewScheme() 45 _ = oamcore.AddToScheme(scheme) 46 return scheme 47 } 48 49 // newReconciler creates a new reconciler for testing 50 func newReconciler(c client.Client) Reconciler { 51 return Reconciler{ 52 Client: c, 53 Log: zap.S().With("test"), 54 Scheme: newScheme(), 55 } 56 } 57 58 // newRequest creates a new reconciler request for testing 59 func newRequest(namespace string, name string) ctrl.Request { 60 return ctrl.Request{ 61 NamespacedName: types.NamespacedName{ 62 Namespace: namespace, 63 Name: name, 64 }, 65 } 66 } 67 68 // newAppConfig creates a minimal ApplicationConfiguration struct 69 func newAppConfig() *oamv1.ApplicationConfiguration { 70 return &oamv1.ApplicationConfiguration{ 71 ObjectMeta: v1.ObjectMeta{ 72 Name: testAppConfigName, 73 Namespace: testNamespace, 74 Annotations: make(map[string]string), 75 }, 76 } 77 } 78 79 func TestReconcileApplicationConfigurationNotFound(t *testing.T) { 80 81 assertions := assert.New(t) 82 _ = oamcore.AddToScheme(k8scheme.Scheme) 83 c := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build() 84 85 reconciler := newReconciler(c) 86 request := newRequest(testNamespace, testAppConfigName) 87 88 _, err := reconciler.Reconcile(context.TODO(), request) 89 assertions.NoError(err) 90 } 91 func TestReconcileNoRestartVersion(t *testing.T) { 92 93 assertions := assert.New(t) 94 _ = oamcore.AddToScheme(k8scheme.Scheme) 95 c := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build() 96 97 reconciler := newReconciler(c) 98 request := newRequest(testNamespace, testAppConfigName) 99 100 err := c.Create(context.TODO(), newAppConfig()) 101 assertions.NoError(err) 102 103 _, err = reconciler.Reconcile(context.TODO(), request) 104 assertions.NoError(err) 105 } 106 107 func TestReconcileRestartVersion(t *testing.T) { 108 109 assertions := assert.New(t) 110 _ = oamcore.AddToScheme(k8scheme.Scheme) 111 c := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build() 112 113 reconciler := newReconciler(c) 114 request := newRequest(testNamespace, testAppConfigName) 115 116 appConfig := newAppConfig() 117 appConfig.Annotations[vzconst.RestartVersionAnnotation] = "1" 118 err := c.Create(context.TODO(), appConfig) 119 assertions.NoError(err) 120 121 _, err = reconciler.Reconcile(context.TODO(), request) 122 assertions.NoError(err) 123 124 err = c.Get(context.TODO(), request.NamespacedName, appConfig) 125 assertions.NoError(err) 126 } 127 128 func TestReconcileEmptyRestartVersion(t *testing.T) { 129 130 assertions := assert.New(t) 131 _ = oamcore.AddToScheme(k8scheme.Scheme) 132 c := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build() 133 134 reconciler := newReconciler(c) 135 request := newRequest(testNamespace, testAppConfigName) 136 137 appConfig := newAppConfig() 138 appConfig.Annotations[vzconst.RestartVersionAnnotation] = "" 139 err := c.Create(context.TODO(), appConfig) 140 assertions.NoError(err) 141 142 _, err = reconciler.Reconcile(context.TODO(), request) 143 assertions.NoError(err) 144 145 err = c.Get(context.TODO(), request.NamespacedName, appConfig) 146 assertions.NoError(err) 147 } 148 149 func TestReconcileRestartWeblogic(t *testing.T) { 150 151 assertions := assert.New(t) 152 153 var mocker = gomock.NewController(t) 154 var cli = mocks.NewMockClient(mocker) 155 156 // expect a call to fetch the ApplicationConfiguration 157 cli.EXPECT(). 158 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 159 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 160 appConfig.Namespace = testNamespace 161 appConfig.Name = testAppConfigName 162 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 163 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 164 ComponentName: testComponentName, 165 Reference: oamrt.TypedReference{ 166 APIVersion: "v1", 167 Kind: vzconst.VerrazzanoWebLogicWorkloadKind, 168 Name: testComponentName, 169 }, 170 }} 171 return nil 172 }) 173 174 // expect a call to fetch the workload 175 cli.EXPECT(). 176 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 177 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 178 return nil 179 }).Times(2) 180 181 // expect a call to update the workload 182 cli.EXPECT(). 183 Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 184 DoAndReturn(func(ctx context.Context, component *unstructured.Unstructured, options ...client.UpdateOption) error { 185 return nil 186 }) 187 188 // create a request and reconcile it 189 request := newRequest(testNamespace, testAppConfigName) 190 reconciler := newReconciler(cli) 191 result, err := reconciler.Reconcile(context.TODO(), request) 192 193 mocker.Finish() 194 assertions.NoError(err) 195 assertions.Equal(false, result.Requeue) 196 } 197 198 func TestReconcileRestartCoherence(t *testing.T) { 199 200 assertions := assert.New(t) 201 202 var mocker = gomock.NewController(t) 203 var cli = mocks.NewMockClient(mocker) 204 205 // expect a call to fetch the ApplicationConfiguration 206 cli.EXPECT(). 207 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 208 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 209 appConfig.Namespace = testNamespace 210 appConfig.Name = testAppConfigName 211 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 212 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 213 ComponentName: testComponentName, 214 Reference: oamrt.TypedReference{ 215 APIVersion: "v1", 216 Kind: vzconst.VerrazzanoCoherenceWorkloadKind, 217 Name: testComponentName, 218 }, 219 }} 220 return nil 221 }) 222 223 // expect a call to fetch the workload 224 cli.EXPECT(). 225 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 226 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 227 return nil 228 }).Times(2) 229 230 // expect a call to update the workload 231 cli.EXPECT(). 232 Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 233 DoAndReturn(func(ctx context.Context, component *unstructured.Unstructured, options ...client.UpdateOption) error { 234 return nil 235 }) 236 237 // create a request and reconcile it 238 request := newRequest(testNamespace, testAppConfigName) 239 reconciler := newReconciler(cli) 240 result, err := reconciler.Reconcile(context.TODO(), request) 241 242 mocker.Finish() 243 assertions.NoError(err) 244 assertions.Equal(false, result.Requeue) 245 } 246 247 func TestReconcileRestartHelidon(t *testing.T) { 248 249 assertions := assert.New(t) 250 251 var mocker = gomock.NewController(t) 252 var cli = mocks.NewMockClient(mocker) 253 254 // expect a call to fetch the ApplicationConfiguration 255 cli.EXPECT(). 256 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 257 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 258 appConfig.Namespace = testNamespace 259 appConfig.Name = testAppConfigName 260 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 261 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 262 ComponentName: testComponentName, 263 Reference: oamrt.TypedReference{ 264 APIVersion: "v1", 265 Kind: vzconst.VerrazzanoHelidonWorkloadKind, 266 Name: testComponentName, 267 }, 268 }} 269 return nil 270 }) 271 272 // Expect a call to fetch the workload 273 cli.EXPECT(). 274 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 275 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 276 return nil 277 }).Times(2) 278 279 // expect a call to update the workload 280 cli.EXPECT(). 281 Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 282 DoAndReturn(func(ctx context.Context, component *unstructured.Unstructured, options ...client.UpdateOption) error { 283 return nil 284 }) 285 286 // create a request and reconcile it 287 request := newRequest(testNamespace, testAppConfigName) 288 reconciler := newReconciler(cli) 289 result, err := reconciler.Reconcile(context.TODO(), request) 290 291 mocker.Finish() 292 assertions.NoError(err) 293 assertions.Equal(false, result.Requeue) 294 } 295 296 func TestReconcileDeploymentRestart(t *testing.T) { 297 298 assertions := assert.New(t) 299 300 var mocker = gomock.NewController(t) 301 var cli = mocks.NewMockClient(mocker) 302 303 // expect a call to fetch the ApplicationConfiguration 304 cli.EXPECT(). 305 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 306 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 307 appConfig.Namespace = testNamespace 308 appConfig.Name = testAppConfigName 309 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 310 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 311 ComponentName: testDeploymentName, 312 Reference: oamrt.TypedReference{ 313 APIVersion: "v1", 314 Kind: vzconst.DeploymentWorkloadKind, 315 Name: testDeploymentName, 316 }, 317 }} 318 return nil 319 }) 320 321 // expect a call to fetch the workload 322 cli.EXPECT(). 323 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 324 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 325 return nil 326 }) 327 328 // expect a call to fetch the deployment 329 cli.EXPECT(). 330 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()). 331 DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error { 332 deploy.Name = testDeploymentName 333 deploy.Namespace = testNamespace 334 return nil 335 }) 336 // expect a call to fetch the deployment 337 cli.EXPECT(). 338 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()). 339 DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error { 340 deploy.Name = testDeploymentName 341 deploy.Namespace = testNamespace 342 return nil 343 }) 344 // expect a call to update the deployment 345 cli.EXPECT(). 346 Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 347 DoAndReturn(func(ctx context.Context, deploy *appsv1.Deployment, options ...client.UpdateOption) error { 348 assertions.Equal(testNewRestartVersion, deploy.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation]) 349 return nil 350 }) 351 // create a request and reconcile it 352 request := newRequest(testNamespace, testAppConfigName) 353 reconciler := newReconciler(cli) 354 result, err := reconciler.Reconcile(context.TODO(), request) 355 356 mocker.Finish() 357 assertions.NoError(err) 358 assertions.Equal(false, result.Requeue) 359 } 360 361 func TestFailedReconcileDeploymentRestart(t *testing.T) { 362 363 assertions := assert.New(t) 364 365 var mocker = gomock.NewController(t) 366 var cli = mocks.NewMockClient(mocker) 367 368 // expect a call to fetch the ApplicationConfiguration 369 cli.EXPECT(). 370 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 371 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 372 appConfig.Namespace = testNamespace 373 appConfig.Name = testAppConfigName 374 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 375 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 376 ComponentName: testDeploymentName, 377 Reference: oamrt.TypedReference{ 378 APIVersion: "v1", 379 Kind: vzconst.DeploymentWorkloadKind, 380 Name: testDeploymentName, 381 }, 382 }} 383 return nil 384 }) 385 386 // expect a call to fetch the workload 387 cli.EXPECT(). 388 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 389 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 390 return nil 391 }) 392 393 // expect a call to fetch the deployment 394 cli.EXPECT(). 395 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()). 396 DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error { 397 return fmt.Errorf("Could not return %s in namespace %s", testDeploymentName, testNamespace) 398 }) 399 400 // create a request and reconcile it 401 request := newRequest(testNamespace, testAppConfigName) 402 reconciler := newReconciler(cli) 403 result, err := reconciler.Reconcile(context.TODO(), request) 404 405 mocker.Finish() 406 assertions.NoError(err) 407 assertions.Equal(true, result.Requeue) 408 } 409 410 func TestReconcileDeploymentNoRestart(t *testing.T) { 411 412 assertions := assert.New(t) 413 414 var mocker = gomock.NewController(t) 415 var cli = mocks.NewMockClient(mocker) 416 417 // expect a call to fetch the ApplicationConfiguration 418 cli.EXPECT(). 419 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 420 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 421 appConfig.Namespace = testNamespace 422 appConfig.Name = testAppConfigName 423 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 424 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 425 ComponentName: testDeploymentName, 426 Reference: oamrt.TypedReference{ 427 APIVersion: "v1", 428 Kind: vzconst.DeploymentWorkloadKind, 429 Name: testDeploymentName, 430 }, 431 }} 432 return nil 433 }) 434 435 // expect a call to fetch the workload 436 cli.EXPECT(). 437 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 438 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 439 return nil 440 }) 441 442 // expect a call to fetch the deployment 443 cli.EXPECT(). 444 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()). 445 DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error { 446 deploy.Name = testDeploymentName 447 deploy.Namespace = testNamespace 448 deploy.Spec.Template.ObjectMeta.Annotations = make(map[string]string) 449 deploy.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion 450 return nil 451 }) 452 // expect a call to fetch the deployment 453 cli.EXPECT(). 454 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()). 455 DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error { 456 deploy.Name = testDeploymentName 457 deploy.Namespace = testNamespace 458 deploy.Spec.Template.ObjectMeta.Annotations = make(map[string]string) 459 deploy.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion 460 return nil 461 }) 462 463 // create a request and reconcile it 464 request := newRequest(testNamespace, testAppConfigName) 465 reconciler := newReconciler(cli) 466 result, err := reconciler.Reconcile(context.TODO(), request) 467 468 mocker.Finish() 469 assertions.NoError(err) 470 assertions.Equal(false, result.Requeue) 471 } 472 473 func TestReconcileDaemonSetRestartDaemonSet(t *testing.T) { 474 475 assertions := assert.New(t) 476 477 var mocker = gomock.NewController(t) 478 var cli = mocks.NewMockClient(mocker) 479 480 // expect a call to fetch the ApplicationConfiguration 481 cli.EXPECT(). 482 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 483 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 484 appConfig.Namespace = testNamespace 485 appConfig.Name = testAppConfigName 486 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 487 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 488 ComponentName: testDaemonSetName, 489 Reference: oamrt.TypedReference{ 490 APIVersion: "v1", 491 Kind: vzconst.DaemonSetWorkloadKind, 492 Name: testDaemonSetName, 493 }, 494 }} 495 return nil 496 }) 497 498 // expect a call to fetch the workload 499 cli.EXPECT(). 500 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 501 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 502 return nil 503 }) 504 // expect a call to fetch the daemonset 505 cli.EXPECT(). 506 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDaemonSetName}, gomock.Not(gomock.Nil()), gomock.Any()). 507 DoAndReturn(func(ctx context.Context, name types.NamespacedName, daemonset *appsv1.DaemonSet, opt ...client.GetOption) error { 508 daemonset.Name = testDaemonSetName 509 daemonset.Namespace = testNamespace 510 return nil 511 }) 512 // expect a call to fetch the daemonset 513 cli.EXPECT(). 514 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDaemonSetName}, gomock.Not(gomock.Nil()), gomock.Any()). 515 DoAndReturn(func(ctx context.Context, name types.NamespacedName, daemonset *appsv1.DaemonSet, opt ...client.GetOption) error { 516 daemonset.Name = testDaemonSetName 517 daemonset.Namespace = testNamespace 518 return nil 519 }) 520 // expect a call to update the daemonset 521 cli.EXPECT(). 522 Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 523 DoAndReturn(func(ctx context.Context, daemonset *appsv1.DaemonSet, options ...client.UpdateOption) error { 524 assertions.Equal(testNewRestartVersion, daemonset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation]) 525 return nil 526 }) 527 528 // create a request and reconcile it 529 request := newRequest(testNamespace, testAppConfigName) 530 reconciler := newReconciler(cli) 531 result, err := reconciler.Reconcile(context.TODO(), request) 532 533 mocker.Finish() 534 assertions.NoError(err) 535 assertions.Equal(false, result.Requeue) 536 } 537 538 func TestReconcileDaemonSetNoRestartDaemonSet(t *testing.T) { 539 540 assertions := assert.New(t) 541 542 var mocker = gomock.NewController(t) 543 var cli = mocks.NewMockClient(mocker) 544 545 // expect a call to fetch the ApplicationConfiguration 546 cli.EXPECT(). 547 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 548 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 549 appConfig.Namespace = testNamespace 550 appConfig.Name = testAppConfigName 551 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 552 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 553 ComponentName: testDaemonSetName, 554 Reference: oamrt.TypedReference{ 555 APIVersion: "v1", 556 Kind: vzconst.DaemonSetWorkloadKind, 557 Name: testDaemonSetName, 558 }, 559 }} 560 return nil 561 }) 562 563 // expect a call to fetch the workload 564 cli.EXPECT(). 565 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 566 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 567 return nil 568 }) 569 // expect a call to fetch the daemonset 570 cli.EXPECT(). 571 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDaemonSetName}, gomock.Not(gomock.Nil()), gomock.Any()). 572 DoAndReturn(func(ctx context.Context, name types.NamespacedName, daemonset *appsv1.DaemonSet, opt ...client.GetOption) error { 573 daemonset.Name = testDaemonSetName 574 daemonset.Namespace = testNamespace 575 daemonset.Spec.Template.ObjectMeta.Annotations = make(map[string]string) 576 daemonset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion 577 return nil 578 }) 579 // expect a call to fetch the daemonset 580 cli.EXPECT(). 581 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDaemonSetName}, gomock.Not(gomock.Nil()), gomock.Any()). 582 DoAndReturn(func(ctx context.Context, name types.NamespacedName, daemonset *appsv1.DaemonSet, opt ...client.GetOption) error { 583 daemonset.Name = testDaemonSetName 584 daemonset.Namespace = testNamespace 585 daemonset.Spec.Template.ObjectMeta.Annotations = make(map[string]string) 586 daemonset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion 587 return nil 588 }) 589 590 // create a request and reconcile it 591 request := newRequest(testNamespace, testAppConfigName) 592 reconciler := newReconciler(cli) 593 result, err := reconciler.Reconcile(context.TODO(), request) 594 595 mocker.Finish() 596 assertions.NoError(err) 597 assertions.Equal(false, result.Requeue) 598 } 599 600 func TestReconcileStatefulSetRestart(t *testing.T) { 601 602 assertions := assert.New(t) 603 604 var mocker = gomock.NewController(t) 605 var cli = mocks.NewMockClient(mocker) 606 607 // expect a call to fetch the ApplicationConfiguration 608 cli.EXPECT(). 609 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 610 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 611 appConfig.Namespace = testNamespace 612 appConfig.Name = testAppConfigName 613 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 614 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 615 ComponentName: testStatefulSetName, 616 Reference: oamrt.TypedReference{ 617 APIVersion: "v1", 618 Kind: vzconst.StatefulSetWorkloadKind, 619 Name: testStatefulSetName, 620 }, 621 }} 622 return nil 623 }) 624 625 // expect a call to fetch the workload 626 cli.EXPECT(). 627 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil())). 628 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 629 return nil 630 }) 631 // expect a call to fetch the statefulset 632 cli.EXPECT(). 633 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testStatefulSetName}, gomock.Not(gomock.Nil()), gomock.Any()). 634 DoAndReturn(func(ctx context.Context, name types.NamespacedName, statefulset *appsv1.StatefulSet, opt ...client.GetOption) error { 635 statefulset.Name = testStatefulSetName 636 statefulset.Namespace = testNamespace 637 return nil 638 }) 639 // expect a call to fetch the statefulset 640 cli.EXPECT(). 641 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testStatefulSetName}, gomock.Not(gomock.Nil()), gomock.Any()). 642 DoAndReturn(func(ctx context.Context, name types.NamespacedName, statefulset *appsv1.StatefulSet, opt ...client.GetOption) error { 643 statefulset.Name = testStatefulSetName 644 statefulset.Namespace = testNamespace 645 return nil 646 }) 647 // expect a call to update the statefulset 648 cli.EXPECT(). 649 Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 650 DoAndReturn(func(ctx context.Context, statefulset *appsv1.StatefulSet, options ...client.UpdateOption) error { 651 assertions.Equal(testNewRestartVersion, statefulset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation]) 652 return nil 653 }) 654 655 // create a request and reconcile it 656 request := newRequest(testNamespace, testAppConfigName) 657 reconciler := newReconciler(cli) 658 result, err := reconciler.Reconcile(context.TODO(), request) 659 660 mocker.Finish() 661 assertions.NoError(err) 662 assertions.Equal(false, result.Requeue) 663 } 664 665 func TestReconcileStatefulSetNoRestart(t *testing.T) { 666 667 assertions := assert.New(t) 668 669 var mocker = gomock.NewController(t) 670 var cli = mocks.NewMockClient(mocker) 671 672 // expect a call to fetch the ApplicationConfiguration 673 cli.EXPECT(). 674 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()). 675 DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error { 676 appConfig.Namespace = testNamespace 677 appConfig.Name = testAppConfigName 678 appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion} 679 appConfig.Status.Workloads = []oamv1.WorkloadStatus{{ 680 ComponentName: testStatefulSetName, 681 Reference: oamrt.TypedReference{ 682 APIVersion: "v1", 683 Kind: vzconst.StatefulSetWorkloadKind, 684 Name: testStatefulSetName, 685 }, 686 }} 687 return nil 688 }) 689 690 // expect a call to fetch the workload 691 cli.EXPECT(). 692 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 693 DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error { 694 return nil 695 }) 696 // expect a call to fetch the statefulset 697 cli.EXPECT(). 698 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testStatefulSetName}, gomock.Not(gomock.Nil()), gomock.Any()). 699 DoAndReturn(func(ctx context.Context, name types.NamespacedName, statefulset *appsv1.StatefulSet, opt ...client.GetOption) error { 700 statefulset.Name = testStatefulSetName 701 statefulset.Namespace = testNamespace 702 statefulset.Spec.Template.ObjectMeta.Annotations = make(map[string]string) 703 statefulset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion 704 return nil 705 }) 706 // expect a call to fetch the statefulset 707 cli.EXPECT(). 708 Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testStatefulSetName}, gomock.Not(gomock.Nil()), gomock.Any()). 709 DoAndReturn(func(ctx context.Context, name types.NamespacedName, statefulset *appsv1.StatefulSet, opt ...client.GetOption) error { 710 statefulset.Name = testStatefulSetName 711 statefulset.Namespace = testNamespace 712 statefulset.Spec.Template.ObjectMeta.Annotations = make(map[string]string) 713 statefulset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion 714 return nil 715 }) 716 717 // create a request and reconcile it 718 request := newRequest(testNamespace, testAppConfigName) 719 reconciler := newReconciler(cli) 720 result, err := reconciler.Reconcile(context.TODO(), request) 721 722 mocker.Finish() 723 assertions.NoError(err) 724 assertions.Equal(false, result.Requeue) 725 } 726 727 // TestReconcileKubeSystem tests to make sure we do not reconcile 728 // Any resource that belong to the kube-system namespace 729 func TestReconcileKubeSystem(t *testing.T) { 730 731 assertions := assert.New(t) 732 mocker := gomock.NewController(t) 733 cli := mocks.NewMockClient(mocker) 734 735 // create a request and reconcile it 736 request := newRequest(vzconst.KubeSystem, testAppConfigName) 737 reconciler := newReconciler(cli) 738 result, err := reconciler.Reconcile(context.TODO(), request) 739 740 // Validate the results 741 mocker.Finish() 742 assertions.Nil(err) 743 assertions.True(result.IsZero()) 744 } 745 746 // TestReconcileFailed tests to make sure the failure metric is being exposed 747 func TestReconcileFailed(t *testing.T) { 748 749 assertions := assert.New(t) 750 clientBuilder := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build() 751 // Create a request and reconcile it 752 reconciler := newReconciler(clientBuilder) 753 request := newRequest(testNamespace, testAppConfigName) 754 reconcileerrorCounterObject, err := metricsexporter.GetSimpleCounterMetric(metricsexporter.AppconfigReconcileError) 755 assertions.NoError(err) 756 // Expect a call to fetch the error 757 reconcileFailedCounterBefore := testutil.ToFloat64(reconcileerrorCounterObject.Get()) 758 reconcileerrorCounterObject.Get().Inc() 759 reconciler.Reconcile(context.TODO(), request) 760 reconcileFailedCounterAfter := testutil.ToFloat64(reconcileerrorCounterObject.Get()) 761 assertions.Equal(reconcileFailedCounterBefore, reconcileFailedCounterAfter-1) 762 }