github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/metricstrait/metricstrait_controller_test.go (about) 1 // Copyright (c) 2020, 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 metricstrait 5 6 import ( 7 "context" 8 "encoding/json" 9 "fmt" 10 "testing" 11 "time" 12 13 "github.com/crossplane/oam-kubernetes-runtime/pkg/oam" 14 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 15 "k8s.io/apimachinery/pkg/api/errors" 16 "k8s.io/apimachinery/pkg/conversion" 17 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 18 19 promoperapi "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 20 "github.com/verrazzano/verrazzano/application-operator/constants" 21 k8scheme "k8s.io/client-go/kubernetes/scheme" 22 "sigs.k8s.io/controller-runtime/pkg/client/fake" 23 24 oamrt "github.com/crossplane/crossplane-runtime/apis/common/v1" 25 oamcore "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2" 26 "github.com/go-logr/logr" 27 "github.com/golang/mock/gomock" 28 asserts "github.com/stretchr/testify/assert" 29 vzapi "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1" 30 "github.com/verrazzano/verrazzano/application-operator/mocks" 31 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 32 "go.uber.org/zap" 33 k8sapps "k8s.io/api/apps/v1" 34 k8score "k8s.io/api/core/v1" 35 k8smeta "k8s.io/apimachinery/pkg/apis/meta/v1" 36 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 37 "k8s.io/apimachinery/pkg/runtime" 38 "k8s.io/apimachinery/pkg/types" 39 ctrl "sigs.k8s.io/controller-runtime" 40 "sigs.k8s.io/controller-runtime/pkg/client" 41 ) 42 43 const ( 44 foo = "foo" 45 bar = "bar" 46 getError = "get error" 47 updateError = "update error" 48 ) 49 50 type erroringGetClient struct { 51 client.Client 52 } 53 54 type erroringUpdateClient struct { 55 client.Client 56 } 57 58 func (eg *erroringGetClient) Get(_ context.Context, _ client.ObjectKey, _ client.Object, _ ...client.GetOption) error { 59 return errors.NewTooManyRequests(getError, 0) 60 } 61 62 func (eu *erroringUpdateClient) Update(_ context.Context, _ client.Object, _ ...client.UpdateOption) error { 63 return errors.NewTooManyRequests(updateError, 0) 64 } 65 66 // TestReconcilerSetupWithManager test the creation of the metrics trait reconciler. 67 // GIVEN a controller implementation 68 // WHEN the controller is created 69 // THEN verify no error is returned 70 func TestReconcilerSetupWithManager(t *testing.T) { 71 assert := asserts.New(t) 72 73 var mocker *gomock.Controller 74 var mgr *mocks.MockManager 75 var cli *mocks.MockClient 76 var scheme *runtime.Scheme 77 var reconciler Reconciler 78 var err error 79 80 mocker = gomock.NewController(t) 81 mgr = mocks.NewMockManager(mocker) 82 cli = mocks.NewMockClient(mocker) 83 scheme = runtime.NewScheme() 84 _ = vzapi.AddToScheme(scheme) 85 reconciler = Reconciler{Client: cli, Scheme: scheme, Scraper: "istio-system/prometheus"} 86 mgr.EXPECT().GetControllerOptions().AnyTimes() 87 mgr.EXPECT().GetScheme().Return(scheme) 88 mgr.EXPECT().GetLogger().Return(logr.Discard()) 89 mgr.EXPECT().SetFields(gomock.Any()).Return(nil).AnyTimes() 90 mgr.EXPECT().Add(gomock.Any()).Return(nil).AnyTimes() 91 err = reconciler.SetupWithManager(mgr) 92 mocker.Finish() 93 assert.NoError(err) 94 } 95 96 // TestMetricsTraitCreatedForContainerizedWorkload tests the creation of a metrics trait related to a containerized workload. 97 // GIVEN a metrics trait that has been created 98 // AND the metrics trait is related to a containerized workload 99 // WHEN the metrics trait Reconcile method is invoked 100 // THEN verify that metrics trait finalizer is added 101 // AND verify that pod annotations are updated 102 // AND verify that the scraper configmap is updated 103 // AND verify that the scraper pod is restarted 104 func TestMetricsTraitCreatedForContainerizedWorkload(t *testing.T) { 105 assert := asserts.New(t) 106 107 c := containerizedWorkloadClient(false, false, false) 108 109 // Create and make the request 110 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 111 112 reconciler := newMetricsTraitReconciler(c) 113 result, err := reconciler.Reconcile(context.TODO(), request) 114 115 // Validate the results 116 assert.NoError(err) 117 assert.Equal(true, result.Requeue) 118 assert.Equal(time.Duration(0), result.RequeueAfter) 119 trait := vzapi.MetricsTrait{} 120 err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait) 121 assert.NoError(err) 122 assert.Equal("test-namespace", trait.Namespace) 123 assert.Equal("test-trait-name", trait.Name) 124 assert.Len(trait.Finalizers, 1) 125 assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0]) 126 } 127 128 // TestMetricsTraitCreatedForVerrazzanoWorkload tests the creation of a metrics trait related to a Verrazzano workload. 129 // The Verrazzano workload contains the real workload so we need to unwrap it. 130 // GIVEN a metrics trait that has been created 131 // AND the metrics trait is related to a Verrazzano workload 132 // WHEN the metrics trait Reconcile method is invoked 133 // THEN the contained workload should be unwrapped 134 // AND verify that metrics trait finalizer is added 135 // AND verify that pod annotations are updated 136 // AND verify that the scraper configmap is updated 137 // AND verify that the scraper pod is restarted 138 func TestMetricsTraitCreatedForVerrazzanoWorkload(t *testing.T) { 139 assert := asserts.New(t) 140 mocker := gomock.NewController(t) 141 142 c := cohWorkloadClient(false, -1) 143 144 // Create and make the request 145 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 146 147 reconciler := newMetricsTraitReconciler(c) 148 result, err := reconciler.Reconcile(context.TODO(), request) 149 150 // Validate the results 151 mocker.Finish() 152 assert.NoError(err) 153 assert.Equal(true, result.Requeue) 154 assert.Equal(time.Duration(0), result.RequeueAfter) 155 trait := vzapi.MetricsTrait{} 156 err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait) 157 assert.NoError(err) 158 assert.Equal("test-namespace", trait.Namespace) 159 assert.Equal("test-trait-name", trait.Name) 160 assert.Len(trait.Finalizers, 1) 161 assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0]) 162 } 163 164 // TestMetricsTraitCreatedForDeploymentWorkload tests the creation of a metrics trait related to a native Kubernetes Deployment workload. 165 // GIVEN a metrics trait that has been created 166 // AND the metrics trait is related to a k8s deployment workload 167 // WHEN the metrics trait Reconcile method is invoked 168 // THEN verify that metrics trait finalizer is added 169 // AND verify that pod annotations are updated 170 // AND verify that the scraper configmap is updated 171 // AND verify that the scraper pod is restarted 172 func TestMetricsTraitCreatedForDeploymentWorkload(t *testing.T) { 173 assert := asserts.New(t) 174 mocker := gomock.NewController(t) 175 176 c := deploymentWorkloadClient(false) 177 178 // Create and make the request 179 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 180 181 reconciler := newMetricsTraitReconciler(c) 182 result, err := reconciler.Reconcile(context.TODO(), request) 183 184 // Validate the results 185 mocker.Finish() 186 assert.NoError(err) 187 assert.Equal(true, result.Requeue) 188 assert.Equal(time.Duration(0), result.RequeueAfter) 189 trait := vzapi.MetricsTrait{} 190 err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait) 191 assert.NoError(err) 192 assert.Equal("test-namespace", trait.Namespace) 193 assert.Equal("test-trait-name", trait.Name) 194 assert.Len(trait.Finalizers, 1) 195 assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0]) 196 } 197 198 // TestMetricsTraitDeletedForContainerizedWorkload tests deletion of a metrics trait related to a containerized workload. 199 // GIVEN a metrics trait with a non-zero deletion time 200 // WHEN the metrics trait Reconcile method is invoked 201 // THEN verify that metrics trait finalizer is removed 202 // AND verify that pod annotations are cleaned up 203 // AND verify that the scraper configmap is cleanup up 204 // AND verify that the scraper pod is restarted 205 func TestMetricsTraitDeletedForContainerizedWorkload(t *testing.T) { 206 assert := asserts.New(t) 207 208 c := containerizedWorkloadClient(true, false, false) 209 210 // Create and make the request 211 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 212 reconciler := newMetricsTraitReconciler(c) 213 result, err := reconciler.Reconcile(context.TODO(), request) 214 215 // Validate the results 216 assert.NoError(err) 217 assert.Equal(true, result.Requeue) 218 assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0) 219 trait := vzapi.MetricsTrait{} 220 err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait) 221 assert.NoError(err) 222 assert.Len(trait.Finalizers, 0) 223 } 224 225 // TestMetricsTraitDeletedForContainerizedWorkload tests deletion of a metrics trait related to a containerized workload. 226 // GIVEN a metrics trait with a non-zero deletion time 227 // GIVEN the related deployment resource no longer exists 228 // WHEN the metrics trait Reconcile method is invoked 229 // THEN verify that metrics trait finalizer is removed 230 // AND verify that the scraper configmap is cleanup up 231 // AND verify that the scraper pod is restarted 232 // AND verify that the finalizer is removed 233 func TestMetricsTraitDeletedForContainerizedWorkloadWhenDeploymentDeleted(t *testing.T) { 234 assert := asserts.New(t) 235 236 c := containerizedWorkloadClient(true, true, false) 237 238 // Create and make the request 239 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 240 reconciler := newMetricsTraitReconciler(c) 241 result, err := reconciler.Reconcile(context.TODO(), request) 242 243 // Validate the results 244 assert.NoError(err) 245 assert.Equal(true, result.Requeue) 246 assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0) 247 trait := vzapi.MetricsTrait{} 248 err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait) 249 assert.NoError(err) 250 assert.Len(trait.Finalizers, 0) 251 } 252 253 // TestMetricsTraitDeletedForContainerizedWorkload tests deletion of a metrics trait related to a containerized workload. 254 // GIVEN a metrics trait with a non-zero deletion time 255 // WHEN the metrics trait Reconcile method is invoked 256 // THEN verify that metrics trait finalizer is removed 257 // AND verify that pod annotations are cleaned up 258 // AND verify that the scraper configmap is cleanup up 259 // AND verify that the scraper pod is restarted 260 func TestMetricsTraitDeletedForDeploymentWorkload(t *testing.T) { 261 assert := asserts.New(t) 262 263 c := deploymentWorkloadClient(true) 264 265 // Create and make the request 266 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 267 reconciler := newMetricsTraitReconciler(c) 268 result, err := reconciler.Reconcile(context.TODO(), request) 269 270 // Validate the results 271 assert.NoError(err) 272 assert.Equal(true, result.Requeue) 273 assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0) 274 trait := vzapi.MetricsTrait{} 275 err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait) 276 assert.NoError(err) 277 assert.Len(trait.Finalizers, 0) 278 } 279 280 // TestFetchTraitError tests a failure to fetch the trait during reconcile. 281 // GIVEN a valid new metrics trait 282 // WHEN the metrics trait Reconcile method is invoked 283 // AND a failure occurs fetching the metrics trait 284 // THEN verify the metrics trait finalizer is added 285 // AND verify the error is propigated to the caller 286 func TestFetchTraitError(t *testing.T) { 287 assert := asserts.New(t) 288 289 scheme := k8scheme.Scheme 290 _ = vzapi.AddToScheme(scheme) 291 292 c := fake.NewClientBuilder().WithScheme(scheme).Build() 293 294 // Create and make the request 295 reconciler := newMetricsTraitReconciler(c) 296 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 297 result, err := reconciler.Reconcile(context.TODO(), request) 298 299 // Validate the results 300 assert.Nil(err) 301 assert.Equal(false, result.Requeue) 302 } 303 304 // TestWorkloadFetchError tests failing to fetch the workload during reconcile. 305 // GIVEN a valid new metrics trait 306 // WHEN the the metrics trait Reconcile method is invoked 307 // AND a failure occurs fetching the metrics trait 308 // THEN verify the metrics trait finalizer is added 309 // AND verify the error is propigated to the caller 310 func TestWorkloadFetchError(t *testing.T) { 311 assert := asserts.New(t) 312 313 scheme := k8scheme.Scheme 314 _ = vzapi.AddToScheme(scheme) 315 316 c := fake.NewClientBuilder().WithScheme(scheme).WithObjects( 317 &vzapi.MetricsTrait{ 318 TypeMeta: k8smeta.TypeMeta{ 319 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 320 Kind: vzapi.MetricsTraitKind, 321 }, 322 ObjectMeta: k8smeta.ObjectMeta{ 323 Namespace: "test-namespace", 324 Name: "test-trait-name", 325 Labels: map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"}, 326 }, 327 Spec: vzapi.MetricsTraitSpec{ 328 WorkloadReference: oamrt.TypedReference{ 329 APIVersion: oamcore.SchemeGroupVersion.Identifier(), 330 Kind: oamcore.ContainerizedWorkloadKind, 331 Name: "test-workload-name", 332 }, 333 }, 334 }, 335 ).Build() 336 337 // Create and make the request 338 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 339 reconciler := newMetricsTraitReconciler(c) 340 result, err := reconciler.Reconcile(context.TODO(), request) 341 342 // Validate the results 343 assert.NotNil(err) 344 assert.Equal(true, result.Requeue) 345 } 346 347 // TestDeploymentUpdateError testing failing to update a workload child deployment during reconcile. 348 // GIVEN a metrics trait that has been updated 349 // WHEN the metrics trait Reconcile method is invoked 350 // AND an error occurs updating the scraper deployment 351 // THEN verify an error is recorded in the status 352 func TestDeploymentUpdateError(t *testing.T) { 353 assert := asserts.New(t) 354 mocker := gomock.NewController(t) 355 mock := mocks.NewMockClient(mocker) 356 mockStatus := mocks.NewMockStatusWriter(mocker) 357 testDeployment := k8sapps.Deployment{ 358 TypeMeta: k8smeta.TypeMeta{ 359 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 360 Kind: "Deployment", 361 }, 362 ObjectMeta: k8smeta.ObjectMeta{ 363 Name: "test-deployment-name", 364 Namespace: "test-namespace", 365 CreationTimestamp: k8smeta.Now(), 366 OwnerReferences: []k8smeta.OwnerReference{{ 367 APIVersion: oamcore.SchemeGroupVersion.Identifier(), 368 Kind: oamcore.ContainerizedWorkloadKind, 369 Name: "test-workload-name", 370 UID: "test-workload-uid"}}}} 371 // Expect a call to get the trait resource. 372 mock.EXPECT(). 373 Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}, gomock.Not(gomock.Nil()), gomock.Any()). 374 DoAndReturn(func(ctx context.Context, name types.NamespacedName, trait *vzapi.MetricsTrait, opts ...client.GetOption) error { 375 trait.TypeMeta = k8smeta.TypeMeta{ 376 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 377 Kind: vzapi.MetricsTraitKind} 378 trait.ObjectMeta = k8smeta.ObjectMeta{ 379 Namespace: name.Namespace, 380 Name: name.Name, 381 Labels: map[string]string{ 382 oam.LabelAppName: "test-app", 383 oam.LabelAppComponent: "test-comp", 384 }} 385 trait.Spec.WorkloadReference = oamrt.TypedReference{ 386 APIVersion: oamcore.SchemeGroupVersion.Identifier(), 387 Kind: oamcore.ContainerizedWorkloadKind, 388 Name: "test-workload-name"} 389 return nil 390 }).Times(2) 391 // Expect a call to update the trait resource with a finalizer. 392 mock.EXPECT(). 393 Update(gomock.Any(), gomock.Any(), gomock.Any()). 394 DoAndReturn(func(ctx context.Context, trait *vzapi.MetricsTrait, opts ...client.UpdateOption) error { 395 assert.Equal("test-namespace", trait.Namespace) 396 assert.Equal("test-trait-name", trait.Name) 397 assert.Len(trait.Finalizers, 1) 398 assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0]) 399 return nil 400 }) 401 // Expect a call to get the containerized workload resource 402 mock.EXPECT(). 403 Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-workload-name"}, gomock.Not(gomock.Nil()), gomock.Any()). 404 DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *unstructured.Unstructured, opts ...client.GetOption) error { 405 workload.SetGroupVersionKind(oamcore.ContainerizedWorkloadGroupVersionKind) 406 workload.SetNamespace(name.Namespace) 407 workload.SetName(name.Name) 408 workload.SetUID("test-workload-uid") 409 return nil 410 }).Times(2) 411 // Expect a call to get the prometheus configuration. 412 mock.EXPECT(). 413 Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). 414 DoAndReturn(func(ctx context.Context, name types.NamespacedName, deployment *k8sapps.Deployment, opts ...client.GetOption) error { 415 assert.Equal("istio-system", name.Namespace) 416 assert.Equal("prometheus", name.Name) 417 deployment.APIVersion = k8sapps.SchemeGroupVersion.Identifier() 418 deployment.Kind = deploymentKind 419 deployment.Namespace = name.Namespace 420 deployment.Name = name.Name 421 return nil 422 }) 423 // Expect a call to get the containerized workload resource definition 424 mock.EXPECT(). 425 Get(gomock.Any(), types.NamespacedName{Namespace: "", Name: "containerizedworkloads.core.oam.dev"}, gomock.Not(gomock.Nil()), gomock.Any()). 426 DoAndReturn(func(ctx context.Context, name types.NamespacedName, workloadDef *oamcore.WorkloadDefinition, opts ...client.GetOption) error { 427 workloadDef.Namespace = name.Namespace 428 workloadDef.Name = name.Name 429 workloadDef.Spec.ChildResourceKinds = []oamcore.ChildResourceKind{ 430 {APIVersion: "apps/v1", Kind: "Deployment", Selector: nil}, 431 {APIVersion: "v1", Kind: "Service", Selector: nil}, 432 } 433 return nil 434 }) 435 // Expect a call to list the child Deployment resources of the containerized workload definition 436 mock.EXPECT(). 437 List(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 438 DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error { 439 assert.True(list.GetKind() == deploymentKind || list.GetKind() == serviceKind) 440 if list.GetKind() == deploymentKind { 441 return appendAsUnstructured(list, testDeployment) 442 } 443 return nil 444 }).Times(2) 445 // Expect a call to get the deployment definition 446 mock.EXPECT(). 447 Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-deployment-name"}, gomock.Not(gomock.Nil()), gomock.Any()). 448 DoAndReturn(func(ctx context.Context, name types.NamespacedName, deployment *k8sapps.Deployment, opts ...client.GetOption) error { 449 deployment.ObjectMeta = testDeployment.ObjectMeta 450 deployment.Spec = testDeployment.Spec 451 return nil 452 }) 453 // Expect a call to get the Replicasets for deployment 454 mock.EXPECT(). 455 List(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 456 DoAndReturn(func(ctx context.Context, list *k8sapps.ReplicaSetList, opts ...client.ListOption) error { 457 list.Items = []k8sapps.ReplicaSet{{ObjectMeta: k8smeta.ObjectMeta{Name: "test-rs", OwnerReferences: []k8smeta.OwnerReference{{APIVersion: testDeployment.APIVersion, Kind: testDeployment.Kind, Name: testDeployment.Name}}}, TypeMeta: k8smeta.TypeMeta{Kind: "ReplicSet", APIVersion: "v1"}}} 458 return nil 459 }) 460 // Expect a call to get the Podlist for deployment 461 mock.EXPECT(). 462 List(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 463 DoAndReturn(func(ctx context.Context, list *k8score.PodList, opts ...client.ListOption) error { 464 list.Items = []k8score.Pod{{ObjectMeta: k8smeta.ObjectMeta{OwnerReferences: []k8smeta.OwnerReference{{APIVersion: "v1", Kind: "ReplicSet", Name: "test-rs"}}}}} 465 return nil 466 }) 467 // Expect a call to get the Pod to update 468 mock.EXPECT(). 469 Get(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 470 DoAndReturn(func(ctx context.Context, name types.NamespacedName, pod *k8score.Pod, opts ...client.GetOption) error { 471 pod.ObjectMeta = k8smeta.ObjectMeta{OwnerReferences: []k8smeta.OwnerReference{{APIVersion: "v1", Kind: "ReplicSet", Name: "test-rs"}}} 472 pod.CreationTimestamp = k8smeta.Now() 473 return nil 474 }) 475 // Expect a call to update the child with annotations but return an error. 476 mock.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("test-error")) 477 // Expect a call to get the status writer and return a mock. 478 mock.EXPECT().Status().Return(mockStatus).AnyTimes() 479 // Expect a call to update the status of the ingress trait. 480 // The status should include the error updating the deployment 481 mockStatus.EXPECT(). 482 Update(gomock.Any(), gomock.Any(), gomock.Any()). 483 DoAndReturn(func(ctx context.Context, trait *vzapi.MetricsTrait, opts ...client.UpdateOption) error { 484 assert.Len(trait.Status.Conditions, 1) 485 assert.Equal(oamrt.ReasonReconcileError, trait.Status.Conditions[0].Reason) 486 return nil 487 }) 488 489 // Create and make the request 490 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 491 492 reconciler := newMetricsTraitReconciler(mock) 493 result, err := reconciler.Reconcile(context.TODO(), request) 494 495 // Validate the results 496 mocker.Finish() 497 assert.NoError(err) 498 assert.Equal(true, result.Requeue) 499 assert.Equal(time.Duration(0), result.RequeueAfter) 500 } 501 502 // TestUnsupportedWorkloadType tests a metrics trait with an unsupported workload type 503 // GIVEN a metrics trait has an unsupported workload type of ConfigMap 504 // WHEN the metrics trait Reconcile method is invoked 505 // THEN verify the trait is deleted 506 func TestUnsupportedWorkloadType(t *testing.T) { 507 assert := asserts.New(t) 508 509 scheme := k8scheme.Scheme 510 _ = vzapi.AddToScheme(scheme) 511 512 workload := unstructured.Unstructured{} 513 workload.SetAPIVersion("fakeAPIVersion") 514 workload.SetKind("fakeKind") 515 workload.SetName("test-workload-name") 516 workload.SetNamespace("test-namespace") 517 518 c := fake.NewClientBuilder().WithScheme(scheme).WithObjects( 519 &vzapi.MetricsTrait{ 520 TypeMeta: k8smeta.TypeMeta{ 521 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 522 Kind: vzapi.MetricsTraitKind, 523 }, 524 ObjectMeta: k8smeta.ObjectMeta{ 525 Namespace: "test-namespace", 526 Name: "test-trait-name", 527 Labels: map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"}, 528 }, 529 Spec: vzapi.MetricsTraitSpec{ 530 WorkloadReference: oamrt.TypedReference{ 531 APIVersion: "fakeAPIVersion", 532 Kind: "fakeKind", 533 Name: "test-workload-name", 534 }, 535 }, 536 }, 537 &workload, 538 ).Build() 539 540 // Create and make the request 541 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 542 543 reconciler := newMetricsTraitReconciler(c) 544 result, err := reconciler.Reconcile(context.TODO(), request) 545 546 // Validate the results 547 assert.NoError(err) 548 assert.Equal(false, result.Requeue) 549 } 550 551 // TestMetricsTraitCreatedForWLSWorkload tests creation of a metrics trait related to a WLS workload. 552 // GIVEN a metrics trait that has been created 553 // WHEN the metrics trait Reconcile method is invoked 554 // THEN verify that metrics trait finalizer is added 555 // AND verify that pod annotations are updated 556 // AND verify that the scraper configmap is updated 557 // AND verify that the scraper pod is restarted 558 func TestMetricsTraitCreatedForWLSWorkload(t *testing.T) { 559 assert := asserts.New(t) 560 561 c := wlsWorkloadClient(false) 562 563 // Create and make the request 564 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 565 reconciler := newMetricsTraitReconciler(c) 566 result, err := reconciler.Reconcile(context.TODO(), request) 567 568 // Validate the results 569 assert.NoError(err) 570 assert.Equal(true, result.Requeue) 571 assert.Equal(time.Duration(0), result.RequeueAfter) 572 } 573 574 // TestMetricsTraitDeletedForWLSWorkload tests reconciling a deleted metrics trait related to a WLS workload. 575 // GIVEN a metrics trait with a non-zero deletion time 576 // WHEN the metrics trait Reconcile method is invoked 577 // THEN verify that metrics trait finalizer is removed 578 // AND verify that pod annotations are cleaned up 579 // AND verify that the scraper configmap is cleanup up 580 // AND verify that the scraper pod is restarted 581 func TestMetricsTraitDeletedForWLSWorkload(t *testing.T) { 582 assert := asserts.New(t) 583 584 c := wlsWorkloadClient(true) 585 586 // Create and make the request 587 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 588 reconciler := newMetricsTraitReconciler(c) 589 result, err := reconciler.Reconcile(context.TODO(), request) 590 591 // Validate the results 592 assert.NoError(err) 593 assert.Equal(true, result.Requeue) 594 assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0) 595 } 596 597 // TestMetricsTraitCreatedWithMultiplePorts tests the creation of a metrics trait related to a Coherence workload. 598 // GIVEN a metrics trait that has been created that specifies multiple metrics ports 599 // AND the metrics trait is related to a Coherence workload 600 // WHEN the metrics trait Reconcile method is invoked 601 // THEN verify that metrics trait finalizer is added 602 // AND verify that pod annotations are updated 603 // AND verify that the scraper configmap is updated 604 // AND verify that the scraper pod is restarted 605 func TestMetricsTraitCreatedWithMultiplePorts(t *testing.T) { 606 assert := asserts.New(t) 607 608 c := cohWorkloadClient(false, -1, 8080, 8081, 8082) 609 610 // Create and make the request 611 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 612 613 reconciler := newMetricsTraitReconciler(c) 614 result, err := reconciler.Reconcile(context.TODO(), request) 615 616 // Validate the results 617 assert.NoError(err) 618 assert.Equal(true, result.Requeue) 619 assert.Equal(time.Duration(0), result.RequeueAfter) 620 } 621 622 // TestMetricsTraitCreatedWithMultiplePortsAndPort tests the creation of a metrics trait related to a Coherence workload. 623 // GIVEN a metrics trait that has been created that specifies multiple metrics ports and a single port 624 // AND the metrics trait is related to a Coherence workload 625 // WHEN the metrics trait Reconcile method is invoked 626 // THEN verify that metrics trait finalizer is added 627 // AND verify that pod annotations are updated 628 // AND verify that the scraper configmap is updated 629 // AND verify that the scraper pod is restarted 630 func TestMetricsTraitCreatedWithMultiplePortsAndPort(t *testing.T) { 631 assert := asserts.New(t) 632 633 c := cohWorkloadClient(false, 8080, 8081, 8082, 8083) 634 635 // Create and make the request 636 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 637 638 reconciler := newMetricsTraitReconciler(c) 639 result, err := reconciler.Reconcile(context.TODO(), request) 640 641 // Validate the results 642 assert.NoError(err) 643 assert.Equal(true, result.Requeue) 644 assert.Equal(time.Duration(0), result.RequeueAfter) 645 } 646 647 // TestMetricsTraitDeletedForCOHWorkload tests deletion of a metrics trait related to a coherence workload. 648 // GIVEN a metrics trait with a non-zero deletion time 649 // WHEN the metrics trait Reconcile method is invoked 650 // THEN verify that metrics trait finalizer is removed 651 // AND verify that pod annotations are cleaned up 652 // AND verify that the scraper configmap is cleanup up 653 // AND verify that the scraper pod is restarted 654 func TestMetricsTraitDeletedForCOHWorkload(t *testing.T) { 655 assert := asserts.New(t) 656 657 c := cohWorkloadClient(true, -1) 658 659 // Create and make the request 660 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 661 reconciler := newMetricsTraitReconciler(c) 662 result, err := reconciler.Reconcile(context.TODO(), request) 663 664 // Validate the results 665 assert.NoError(err) 666 assert.Equal(true, result.Requeue) 667 assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0) 668 } 669 670 // TestUseHTTPSForScrapeTargetFalseConditions tests that false is returned for the following conditions 671 // GIVEN a unlabeled Istio namespace or a workload of kind VerrazzanoCoherenceWorkload or a workload of kind Coherence 672 // WHEN the useHttpsForScrapeTarget method is invoked 673 // THEN verify that the false boolean value is returned since all those conditions require an http scrape target 674 func TestUseHTTPSForScrapeTargetFalseConditions(t *testing.T) { 675 assert := asserts.New(t) 676 mocker := gomock.NewController(t) 677 mock := mocks.NewMockClient(mocker) 678 679 mtrait := vzapi.MetricsTrait{ 680 TypeMeta: k8smeta.TypeMeta{ 681 Kind: "VerrazzanoCoherenceWorkload", 682 }, 683 } 684 685 testNamespace := k8score.Namespace{ 686 TypeMeta: k8smeta.TypeMeta{ 687 Kind: "Namespace", 688 }, 689 ObjectMeta: k8smeta.ObjectMeta{ 690 Name: "test-namespace", 691 }, 692 } 693 694 // Expect a call to get the namespace definition 695 mock.EXPECT(). 696 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 697 DoAndReturn(func(ctx context.Context, name types.NamespacedName, namespace *k8score.Namespace, opts ...client.GetOption) error { 698 namespace.TypeMeta = testNamespace.TypeMeta 699 namespace.ObjectMeta = testNamespace.ObjectMeta 700 return nil 701 }) 702 703 mtrait.Spec.WorkloadReference.Kind = "VerrazzanoCoherenceWorkload" 704 https, _ := useHTTPSForScrapeTarget(context.TODO(), nil, &mtrait) 705 // Expect https to be false for scrape target of Kind VerrazzanoCoherenceWorkload 706 assert.False(https, "Expected https to be false for Workload of Kind VerrazzanoCoherenceWorkload") 707 708 mtrait.Spec.WorkloadReference.Kind = "Coherence" 709 https, _ = useHTTPSForScrapeTarget(context.TODO(), nil, &mtrait) 710 // Expect https to be false for scrape target of Kind Coherence 711 assert.False(https, "Expected https to be false for Workload of Kind Coherence") 712 713 reconciler := newMetricsTraitReconciler(mock) 714 715 mtrait.Spec.WorkloadReference.Kind = "" 716 https, _ = useHTTPSForScrapeTarget(context.TODO(), reconciler.Client, &mtrait) 717 // Expect https to be false for namespaces NOT labeled for istio-injection 718 assert.False(https, "Expected https to be false for namespace NOT labeled for istio injection") 719 mocker.Finish() 720 } 721 722 // TestUseHTTPSForScrapeTargetTrueCondition tests that true is returned for namespaces marked for Istio injection 723 // GIVEN a labeled Istio namespace 724 // WHEN the useHttpsForScrapeTarget method is invoked 725 // THEN verify that the true boolean value is returned since pods in Istio namespaces require an https scrape target because of MTLS 726 func TestUseHTTPSForScrapeTargetTrueCondition(t *testing.T) { 727 assert := asserts.New(t) 728 mocker := gomock.NewController(t) 729 mock := mocks.NewMockClient(mocker) 730 731 mtrait := vzapi.MetricsTrait{ 732 TypeMeta: k8smeta.TypeMeta{ 733 Kind: "ContainerizedWorkload", 734 }, 735 } 736 737 testNamespace := k8score.Namespace{ 738 TypeMeta: k8smeta.TypeMeta{ 739 Kind: "Namespace", 740 }, 741 ObjectMeta: k8smeta.ObjectMeta{ 742 Name: "test-namespace", 743 }, 744 } 745 746 // Expect a call to get the namespace definition 747 mock.EXPECT(). 748 Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()). 749 DoAndReturn(func(ctx context.Context, name types.NamespacedName, namespace *k8score.Namespace, opts ...client.GetOption) error { 750 namespace.TypeMeta = testNamespace.TypeMeta 751 namespace.ObjectMeta = testNamespace.ObjectMeta 752 return nil 753 }) 754 755 reconciler := newMetricsTraitReconciler(mock) 756 757 labels := map[string]string{ 758 "istio-injection": "enabled", 759 } 760 testNamespace.ObjectMeta.Labels = labels 761 https, _ := useHTTPSForScrapeTarget(context.TODO(), reconciler.Client, &mtrait) 762 // Expect https to be true for namespaces labeled for istio-injection 763 assert.True(https, "Expected https to be true for namespaces labeled for Istio injection") 764 mocker.Finish() 765 } 766 767 // newMetricsTraitReconciler creates a new reconciler for testing 768 // cli - The Kerberos client to inject into the reconciler 769 func newMetricsTraitReconciler(cli client.Client) Reconciler { 770 scheme := runtime.NewScheme() 771 vzapi.AddToScheme(scheme) 772 reconciler := Reconciler{ 773 Client: cli, 774 Log: zap.S(), 775 Scheme: scheme, 776 Scraper: "istio-system/prometheus", 777 } 778 return reconciler 779 } 780 781 // convertToUnstructured converts an object to an Unstructured version 782 // object - The object to convert to Unstructured 783 func convertToUnstructured(object interface{}) (unstructured.Unstructured, error) { 784 bytes, err := json.Marshal(object) 785 if err != nil { 786 return unstructured.Unstructured{}, err 787 } 788 var u map[string]interface{} 789 json.Unmarshal(bytes, &u) 790 return unstructured.Unstructured{Object: u}, nil 791 } 792 793 // appendAsUnstructured appends an object to the list after converting it to an Unstructured 794 // list - The list to append to. 795 // object - The object to convert to Unstructured and append to the list 796 func appendAsUnstructured(list *unstructured.UnstructuredList, object interface{}) error { 797 u, err := convertToUnstructured(object) 798 if err != nil { 799 return err 800 } 801 list.Items = append(list.Items, u) 802 return nil 803 } 804 805 // TestReconcileKubeSystem tests to make sure we do not reconcile 806 // Any resource that belong to the kube-system namespace 807 func TestReconcileKubeSystem(t *testing.T) { 808 assert := asserts.New(t) 809 810 var mocker = gomock.NewController(t) 811 var cli = mocks.NewMockClient(mocker) 812 813 // create a request and reconcile it 814 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: vzconst.KubeSystem, Name: "test-trait-name"}} 815 reconciler := newMetricsTraitReconciler(cli) 816 result, err := reconciler.Reconcile(context.TODO(), request) 817 818 // Validate the results 819 mocker.Finish() 820 assert.Nil(err) 821 assert.True(result.IsZero()) 822 } 823 824 // TestMetricsTraitDisabledForContainerizedWorkload tests the creation of a metrics trait related to a containerized workload. 825 // GIVEN a metrics trait that has been disabled 826 // WHEN the metrics trait Reconcile method is invoked 827 // THEN verify that metrics trait finalizer is added 828 // AND verify that the scraper configmap is updated with the scrape job being removed 829 func TestMetricsTraitDisabledForContainerizedWorkload(t *testing.T) { 830 assert := asserts.New(t) 831 832 c := containerizedWorkloadClient(false, false, true) 833 834 // Create and make the request 835 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 836 reconciler := newMetricsTraitReconciler(c) 837 result, err := reconciler.Reconcile(context.TODO(), request) 838 // Validate the results 839 assert.NoError(err) 840 assert.Equal(true, result.Requeue) 841 } 842 843 // TestLegacyPrometheusScraper tests the case where the scraper is the default (legacy) VMO Prometheus. 844 func TestLegacyPrometheusScraper(t *testing.T) { 845 assert := asserts.New(t) 846 847 // GIVEN a containerized workload and the reconciler scraper is configured to use the default (legacy) Prometheus scraper 848 // WHEN we reconcile metrics traits 849 // THEN the trait is updated with a finalizer and a ServiceMonitor has been created 850 c := containerizedWorkloadClient(false, false, false) 851 852 // Create and make the request 853 request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}} 854 855 reconciler := newMetricsTraitReconciler(c) 856 reconciler.Scraper = constants.DefaultScraperName 857 858 result, err := reconciler.Reconcile(context.TODO(), request) 859 860 // Validate the results 861 assert.NoError(err) 862 assert.Equal(true, result.Requeue) 863 assert.True(result.RequeueAfter > 0) 864 865 trait := vzapi.MetricsTrait{} 866 err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait) 867 assert.NoError(err) 868 assert.Equal("test-namespace", trait.Namespace) 869 assert.Equal("test-trait-name", trait.Name) 870 assert.Len(trait.Finalizers, 1) 871 assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0]) 872 873 monitor := &promoperapi.ServiceMonitor{} 874 err = c.Get(context.TODO(), types.NamespacedName{Namespace: "test-namespace", Name: "test-app-test-namespace-test-comp"}, monitor) 875 assert.NoError(err) 876 } 877 878 // TestCreateScrapeConfigFromTrait tests the createScrapeConfigFromTrait func calls 879 func TestCreateScrapeConfigFromTrait(t *testing.T) { 880 scheme := k8scheme.Scheme 881 _ = promoperapi.AddToScheme(scheme) 882 _ = vzapi.AddToScheme(scheme) 883 _ = oamcore.SchemeBuilder.AddToScheme(scheme) 884 c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&k8score.Namespace{}).Build() 885 testWorkLoad := unstructured.Unstructured{} 886 testWorkLoad.SetGroupVersionKind(oamcore.ContainerizedWorkloadGroupVersionKind) 887 888 testWLSWorkload := unstructured.Unstructured{} 889 testWLSWorkload.SetAPIVersion("weblogic.oracle") 890 testWLSWorkload.SetKind("Domain") 891 tests := []struct { 892 name string 893 trait vzapi.MetricsTrait 894 workload *unstructured.Unstructured 895 wantErr bool 896 errContains string 897 isConfigNil bool 898 secret *k8score.Secret 899 }{ 900 // GIVEN a metricstrait and workload with default values 901 // WHEN createScrapeConfigFromTrait method is called 902 // THEN a non nil scrape config is created and no error is returned 903 { 904 "Create scrape config from a trait correctly", 905 vzapi.MetricsTrait{ 906 ObjectMeta: k8smeta.ObjectMeta{ 907 Labels: map[string]string{ 908 oam.LabelAppName: foo, 909 oam.LabelAppComponent: bar, 910 }, 911 }, 912 }, 913 &testWorkLoad, 914 false, 915 "", 916 false, 917 nil, 918 }, 919 // GIVEN a metricstrait missing app name label 920 // WHEN createScrapeConfigFromTrait is called 921 // THEN an error is returned 922 { 923 "Trait missing app name label", 924 vzapi.MetricsTrait{ 925 ObjectMeta: k8smeta.ObjectMeta{ 926 Labels: map[string]string{ 927 oam.LabelAppComponent: bar, 928 }, 929 }, 930 }, 931 nil, 932 true, 933 "metrics trait missing application name label", 934 true, 935 nil, 936 }, 937 // GIVEN a metricstrait with missing component label 938 // WHEN createScrapeConfigFromTrait is called 939 // THEN an error is returned 940 { 941 "Trait missing component label", 942 vzapi.MetricsTrait{ 943 ObjectMeta: k8smeta.ObjectMeta{ 944 Labels: map[string]string{ 945 oam.LabelAppName: foo, 946 }, 947 }, 948 }, 949 &testWorkLoad, 950 true, 951 "metrics trait missing component name label", 952 true, 953 nil, 954 }, 955 // GIVEN a nil workload and correct metricstrait 956 // WHEN createScrapeConfigFromTrait is called 957 // THEN no error is returned along with a nil config 958 { 959 "Nil workload", 960 vzapi.MetricsTrait{ 961 ObjectMeta: k8smeta.ObjectMeta{ 962 Labels: map[string]string{ 963 oam.LabelAppName: foo, 964 oam.LabelAppComponent: bar, 965 }, 966 }, 967 }, 968 nil, 969 false, 970 "", 971 true, 972 nil, 973 }, 974 // GIVEN a wls workload and correct metricstrait 975 // WHEN createScrapeConfigFromTrait is called 976 // THEN no error is returned along with a non nil config 977 { 978 "WLS Workload", 979 vzapi.MetricsTrait{ 980 ObjectMeta: k8smeta.ObjectMeta{ 981 Labels: map[string]string{ 982 oam.LabelAppName: foo, 983 oam.LabelAppComponent: bar, 984 }, 985 }, 986 }, 987 &testWLSWorkload, 988 false, 989 "", 990 false, 991 nil, 992 }, 993 { 994 "Basic auth", 995 vzapi.MetricsTrait{ 996 ObjectMeta: k8smeta.ObjectMeta{ 997 Labels: map[string]string{ 998 oam.LabelAppName: foo, 999 oam.LabelAppComponent: bar, 1000 }, 1001 }, 1002 }, 1003 &testWLSWorkload, 1004 false, 1005 "", 1006 false, 1007 &k8score.Secret{ 1008 Data: map[string][]byte{ 1009 "username": []byte(foo), 1010 "password": []byte(bar), 1011 }, 1012 }, 1013 }, 1014 } 1015 1016 for _, tt := range tests { 1017 t.Run(tt.name, func(t *testing.T) { 1018 _, job, err := createScrapeConfigFromTrait(context.Background(), &tt.trait, 0, tt.secret, tt.workload, c) 1019 if tt.wantErr { 1020 asserts.ErrorContains(t, err, tt.errContains) 1021 } else { 1022 asserts.NoError(t, err) 1023 if tt.isConfigNil { 1024 asserts.Nil(t, job) 1025 } else { 1026 asserts.NotNil(t, job) 1027 asserts.True(t, job.Exists("scheme")) 1028 asserts.Equal(t, tt.secret != nil, job.Exists(basicAuthLabel)) 1029 } 1030 } 1031 }) 1032 } 1033 } 1034 1035 // TestRemovedTraitReferencesFromOwner tests the removedTraitReferencesFromOwner reconcile method 1036 func TestRemovedTraitReferencesFromOwner(t *testing.T) { 1037 testTrait := vzapi.MetricsTrait{ 1038 ObjectMeta: k8smeta.ObjectMeta{ 1039 Labels: map[string]string{ 1040 oam.LabelAppName: foo, 1041 oam.LabelAppComponent: bar, 1042 }, 1043 }, 1044 } 1045 testTrait.APIVersion = foo 1046 testTrait.Kind = bar 1047 ttro := runtime.Object(&testTrait) 1048 var scope conversion.Scope 1049 testTraitRawExtension := &runtime.RawExtension{} 1050 asserts.NoError(t, runtime.Convert_runtime_Object_To_runtime_RawExtension(&ttro, testTraitRawExtension, scope)) 1051 1052 scheme := k8scheme.Scheme 1053 _ = vzapi.AddToScheme(scheme) 1054 _ = oamcore.SchemeBuilder.AddToScheme(scheme) 1055 cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects( 1056 &k8score.Namespace{}, 1057 &oamcore.ApplicationConfiguration{ 1058 ObjectMeta: k8smeta.ObjectMeta{ 1059 Name: foo, 1060 }, 1061 Spec: oamcore.ApplicationConfigurationSpec{ 1062 Components: []oamcore.ApplicationConfigurationComponent{ 1063 { 1064 ComponentName: bar, 1065 Traits: []oamcore.ComponentTrait{ 1066 { 1067 Trait: *testTraitRawExtension, 1068 }, 1069 }, 1070 }, 1071 }, 1072 }, 1073 }, 1074 ).Build() 1075 1076 badGetClient := &erroringGetClient{cli} 1077 badUpdateClient := &erroringUpdateClient{cli} 1078 1079 tests := []struct { 1080 name string 1081 client client.Client 1082 wantErr bool 1083 errContains string 1084 operationResult controllerutil.OperationResult 1085 }{ 1086 // GIVEN a scenario where client is unable to make Get call to the api server 1087 // WHEN a call is made to removedTraitReferencesFromOwner func by the reconciler 1088 // then the method returns an error 1089 { 1090 "Unsuccessful get", 1091 badGetClient, 1092 true, 1093 getError, 1094 controllerutil.OperationResultNone, 1095 }, 1096 // GIVEN a scenario where client is unable to make Update call to the api server 1097 // WHEN a call is made to removedTraitReferencesFromOwner func by the reconciler 1098 // then the method returns an error 1099 { 1100 "Unsuccessful Update", 1101 badUpdateClient, 1102 true, 1103 updateError, 1104 controllerutil.OperationResultNone, 1105 }, 1106 // GIVEN an appconfig with the given metricstrait as a component 1107 // WHEN a call is made to removedTraitReferencesFromOwner func by the reconciler 1108 // THEN the appconfig is updated to remove the trait from the appconfig 1109 { 1110 "Successful update", 1111 cli, 1112 false, 1113 "", 1114 controllerutil.OperationResultUpdated, 1115 }, 1116 } 1117 1118 for _, tt := range tests { 1119 t.Run(tt.name, func(t *testing.T) { 1120 reconciler := newMetricsTraitReconciler(tt.client) 1121 _, op, err := reconciler.removedTraitReferencesFromOwner(context.Background(), &k8smeta.OwnerReference{Name: foo}, &testTrait, vzlog.DefaultLogger()) 1122 asserts.Equal(t, op, tt.operationResult) 1123 if tt.wantErr { 1124 asserts.ErrorContains(t, err, tt.errContains) 1125 } else { 1126 asserts.NoError(t, err) 1127 } 1128 }) 1129 } 1130 } 1131 1132 // TestDeleteServiceMonitor tests the deleteServiceMonitor func call 1133 func TestDeleteServiceMonitor(t *testing.T) { 1134 scheme := k8scheme.Scheme 1135 _ = vzapi.AddToScheme(scheme) 1136 _ = promoperapi.AddToScheme(scheme) 1137 _ = oamcore.SchemeBuilder.AddToScheme(scheme) 1138 tests := []struct { 1139 name string 1140 trait vzapi.MetricsTrait 1141 result controllerutil.OperationResult 1142 }{ 1143 // GIVEN a metricstrait 1144 // WHEN the trait is enabled and not scheduled for deletion 1145 // THEN the ServiceMonitor resource is not deleted 1146 { 1147 "Trait is enabled", 1148 vzapi.MetricsTrait{ 1149 Spec: vzapi.MetricsTraitSpec{ 1150 Enabled: getBoolPtr(true), 1151 }, 1152 }, 1153 controllerutil.OperationResultNone, 1154 }, 1155 // GIVEN a metricstrait 1156 // WHEN the trait is scheduled for deletion 1157 // THEN the associated ServiceMonitor is deleted 1158 { 1159 "Successful Delete", 1160 vzapi.MetricsTrait{ 1161 ObjectMeta: k8smeta.ObjectMeta{ 1162 DeletionTimestamp: &k8smeta.Time{Time: time.Now()}, 1163 }, 1164 }, 1165 controllerutil.OperationResultUpdated, 1166 }, 1167 // GIVEN a metricstrait 1168 // WHEN the trait is disabled for deletion 1169 // THEN the associated ServiceMonitor is deleted 1170 { 1171 "Trait is disabled", 1172 vzapi.MetricsTrait{ 1173 Spec: vzapi.MetricsTraitSpec{ 1174 Enabled: getBoolPtr(false), 1175 }, 1176 }, 1177 controllerutil.OperationResultUpdated, 1178 }, 1179 { 1180 "Enabled but scheduled for deletion", 1181 vzapi.MetricsTrait{ 1182 ObjectMeta: k8smeta.ObjectMeta{ 1183 DeletionTimestamp: &k8smeta.Time{Time: time.Now()}, 1184 }, 1185 Spec: vzapi.MetricsTraitSpec{ 1186 Enabled: getBoolPtr(true), 1187 }, 1188 }, 1189 controllerutil.OperationResultUpdated, 1190 }, 1191 } 1192 for _, tt := range tests { 1193 t.Run(tt.name, func(t *testing.T) { 1194 cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects( 1195 &promoperapi.ServiceMonitor{ 1196 ObjectMeta: k8smeta.ObjectMeta{ 1197 Name: foo, 1198 Namespace: bar, 1199 }, 1200 }, 1201 &k8score.Namespace{ObjectMeta: k8smeta.ObjectMeta{Name: bar}}, 1202 ).Build() 1203 reconciler := newMetricsTraitReconciler(cli) 1204 res, _ := reconciler.deleteServiceMonitor(context.TODO(), bar, foo, &tt.trait, vzlog.DefaultLogger()) 1205 asserts.Equal(t, tt.result, res) 1206 }) 1207 } 1208 } 1209 1210 // TestUpdateRelatedStatefulSet tests the updateRelatedStatefulSet func call 1211 // GIVEN metricstrait, workload and child resources 1212 // WHEN updateRelatedPod func call is made by the reconciler 1213 // THEN the related workload StatefulSets are mutated 1214 func TestUpdateRelatedStatefulSet(t *testing.T) { 1215 scheme := runtime.NewScheme() 1216 _ = k8score.AddToScheme(scheme) 1217 _ = k8sapps.AddToScheme(scheme) 1218 port := 443 1219 path := foo 1220 trait := vzapi.MetricsTrait{ 1221 Spec: vzapi.MetricsTraitSpec{ 1222 Port: &port, 1223 Path: &path, 1224 }, 1225 } 1226 child := unstructured.Unstructured{} 1227 child.SetAPIVersion(foo) 1228 child.SetKind(bar) 1229 child.SetName(foo) 1230 child.SetKind(bar) 1231 cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects( 1232 &k8sapps.StatefulSet{ 1233 TypeMeta: k8smeta.TypeMeta{APIVersion: child.GetAPIVersion(), Kind: child.GetKind()}, 1234 ObjectMeta: k8smeta.ObjectMeta{Namespace: child.GetNamespace(), Name: child.GetName(), CreationTimestamp: k8smeta.Now()}, 1235 }, 1236 &k8score.Namespace{ObjectMeta: k8smeta.ObjectMeta{Name: child.GetNamespace()}}, 1237 &k8score.Pod{ObjectMeta: k8smeta.ObjectMeta{OwnerReferences: []k8smeta.OwnerReference{{Kind: child.GetKind(), APIVersion: child.GetAPIVersion(), Name: child.GetName()}}}}, 1238 ).Build() 1239 reconciler0 := newMetricsTraitReconciler(cli) 1240 _, res0, err0 := reconciler0.updateRelatedStatefulSet(context.Background(), &trait, nil, nil, &child, vzlog.DefaultLogger()) 1241 asserts.Equal(t, res0, controllerutil.OperationResultUpdated) 1242 asserts.NoError(t, err0) 1243 1244 badGetClient := &erroringGetClient{cli} 1245 reconciler1 := newMetricsTraitReconciler(badGetClient) 1246 _, res1, err1 := reconciler1.updateRelatedStatefulSet(context.Background(), &trait, nil, nil, &child, vzlog.DefaultLogger()) 1247 asserts.Equal(t, res1, controllerutil.OperationResultNone) 1248 asserts.ErrorContains(t, err1, getError) 1249 } 1250 1251 // TestUpdateRelatedPod tests the updateRelatedPod func call 1252 // GIVEN metricstrait, workload and child resources 1253 // WHEN updateRelatedPod func call is made by the reconciler 1254 // THEN the related workload pods are mutated 1255 func TestUpdateRelatedPod(t *testing.T) { 1256 scheme := runtime.NewScheme() 1257 _ = k8score.AddToScheme(scheme) 1258 1259 port := 443 1260 path := foo 1261 trait := vzapi.MetricsTrait{ 1262 Spec: vzapi.MetricsTraitSpec{ 1263 Port: &port, 1264 Path: &path, 1265 }, 1266 } 1267 1268 child := unstructured.Unstructured{} 1269 child.SetAPIVersion(foo) 1270 child.SetKind(bar) 1271 child.SetName(foo) 1272 child.SetKind(bar) 1273 1274 cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects( 1275 &k8score.Pod{ 1276 TypeMeta: k8smeta.TypeMeta{APIVersion: child.GetAPIVersion(), Kind: child.GetKind()}, 1277 ObjectMeta: k8smeta.ObjectMeta{Namespace: child.GetNamespace(), Name: child.GetName(), CreationTimestamp: k8smeta.Now()}, 1278 }, 1279 &k8score.Namespace{ObjectMeta: k8smeta.ObjectMeta{Name: child.GetNamespace()}}, 1280 ).Build() 1281 1282 reconciler0 := newMetricsTraitReconciler(cli) 1283 _, res0, err0 := reconciler0.updateRelatedPod(context.Background(), &trait, nil, nil, &child, vzlog.DefaultLogger()) 1284 asserts.Equal(t, res0, controllerutil.OperationResultUpdated) 1285 asserts.NoError(t, err0) 1286 1287 badGetClient := &erroringGetClient{cli} 1288 reconciler1 := newMetricsTraitReconciler(badGetClient) 1289 1290 _, res1, err1 := reconciler1.updateRelatedPod(context.Background(), &trait, nil, nil, &child, vzlog.DefaultLogger()) 1291 asserts.Equal(t, res1, controllerutil.OperationResultNone) 1292 asserts.ErrorContains(t, err1, getError) 1293 } 1294 1295 // TestUpdatePrometheusScraperConfigMap tests the updatePrometheusScraperConfigMap func call 1296 func TestUpdatePrometheusScraperConfigMap(t *testing.T) { 1297 scheme := runtime.NewScheme() 1298 _ = k8sapps.AddToScheme(scheme) 1299 _ = vzapi.AddToScheme(scheme) 1300 _ = k8score.AddToScheme(scheme) 1301 1302 port := 443 1303 path := foo 1304 testWorkLoad := unstructured.Unstructured{} 1305 testWorkLoad.SetGroupVersionKind(oamcore.ContainerizedWorkloadGroupVersionKind) 1306 1307 testTrait := vzapi.MetricsTrait{ 1308 ObjectMeta: k8smeta.ObjectMeta{ 1309 Labels: map[string]string{ 1310 oam.LabelAppName: foo, 1311 oam.LabelAppComponent: bar, 1312 }, 1313 }, 1314 Spec: vzapi.MetricsTraitSpec{Port: &port, Path: &path}, 1315 } 1316 1317 testDeployment0 := k8sapps.Deployment{ 1318 ObjectMeta: k8smeta.ObjectMeta{Name: foo}, 1319 Spec: k8sapps.DeploymentSpec{ 1320 Template: k8score.PodTemplateSpec{ 1321 Spec: k8score.PodSpec{ 1322 Volumes: []k8score.Volume{ 1323 { 1324 Name: "config-volume", 1325 VolumeSource: k8score.VolumeSource{ 1326 ConfigMap: &k8score.ConfigMapVolumeSource{LocalObjectReference: k8score.LocalObjectReference{Name: foo}}, 1327 }, 1328 }, 1329 }, 1330 }, 1331 }, 1332 }, 1333 } 1334 1335 // Deployment with an empty volume spec 1336 testDeployment1 := testDeployment0 1337 testDeployment1.Spec.Template.Spec.Volumes = []k8score.Volume{} 1338 1339 cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects( 1340 &k8score.ConfigMap{ 1341 ObjectMeta: k8smeta.ObjectMeta{Name: foo}, 1342 }, 1343 // &k8score.Namespace{ObjectMeta: k8smeta.ObjectMeta{Name: bar}}, 1344 &k8score.Namespace{}, 1345 ).Build() 1346 badGetClient := &erroringGetClient{cli} 1347 badUpdateClient := &erroringUpdateClient{cli} 1348 1349 tests := []struct { 1350 name string 1351 client client.Client 1352 deployment k8sapps.Deployment 1353 wantErr bool 1354 errContains string 1355 }{ 1356 // GIVEN a call to update the scraper ConfigMap 1357 // WHEN the resources are correctly configured 1358 // THEN the ConfigMap is updated with the scraper config 1359 { 1360 "Successful update", 1361 cli, 1362 testDeployment0, 1363 false, 1364 "", 1365 }, 1366 // GIVEN a call to update the scraper config 1367 // WHEN the client is unable to make a Get call 1368 // THEN an error is returned 1369 { 1370 "Unsuccessful Get", 1371 badGetClient, 1372 testDeployment0, 1373 true, 1374 getError, 1375 }, 1376 // GIVEN a call to update the scraper config 1377 // WHEN the client is unable to make an Update call 1378 // THEN an error is returned 1379 { 1380 "Unsuccessful Update", 1381 badUpdateClient, 1382 testDeployment0, 1383 true, 1384 updateError, 1385 }, 1386 // GIVEN a call to update the scraper config 1387 // WHEN the deployment does not contain the ConfigMap name 1388 // THEN an error is returned 1389 { 1390 "Missing cm name", 1391 cli, 1392 testDeployment1, 1393 true, 1394 "failed to find Prometheus configmap name from deployment", 1395 }, 1396 } 1397 1398 for _, tt := range tests { 1399 t.Run(tt.name, func(t *testing.T) { 1400 reconciler := newMetricsTraitReconciler(tt.client) 1401 _, res, err := reconciler.updatePrometheusScraperConfigMap(context.Background(), &testTrait, &testWorkLoad, nil, &tt.deployment, vzlog.DefaultLogger()) 1402 if tt.wantErr { 1403 asserts.ErrorContains(t, err, tt.errContains) 1404 } else { 1405 asserts.Equal(t, controllerutil.OperationResultUpdated, res) 1406 asserts.NoError(t, err) 1407 cm := k8score.ConfigMap{} 1408 asserts.NoError(t, tt.client.Get(context.Background(), types.NamespacedName{Name: foo}, &cm)) 1409 _, ok := cm.Data[prometheusConfigKey] 1410 asserts.True(t, ok) 1411 } 1412 }) 1413 } 1414 } 1415 1416 // deploymentWorkloadClient returns a fake client with a containerized workload target in the trait 1417 func containerizedWorkloadClient(deleting, deploymentDeleted, traitDisabled bool) client.WithWatch { 1418 scheme := k8scheme.Scheme 1419 _ = promoperapi.AddToScheme(scheme) 1420 _ = vzapi.AddToScheme(scheme) 1421 _ = oamcore.SchemeBuilder.AddToScheme(scheme) 1422 1423 testNamespace := "test-namespace" 1424 testWorkloadName := "test-workload-name" 1425 testWorkloadUID := types.UID("test-workload-uid") 1426 1427 trait := vzapi.MetricsTrait{ 1428 TypeMeta: k8smeta.TypeMeta{ 1429 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1430 Kind: vzapi.MetricsTraitKind, 1431 }, 1432 ObjectMeta: k8smeta.ObjectMeta{ 1433 Namespace: testNamespace, 1434 Name: "test-trait-name", 1435 Labels: map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"}, 1436 }, 1437 Spec: vzapi.MetricsTraitSpec{ 1438 WorkloadReference: oamrt.TypedReference{ 1439 APIVersion: oamcore.SchemeGroupVersion.Identifier(), 1440 Kind: oamcore.ContainerizedWorkloadKind, 1441 Name: testWorkloadName, 1442 }, 1443 }, 1444 } 1445 if deleting { 1446 trait.DeletionTimestamp = &k8smeta.Time{Time: time.Now()} 1447 } 1448 trueVal := true 1449 if traitDisabled { 1450 trait.Spec.Enabled = &trueVal 1451 } 1452 1453 objects := []client.Object{ 1454 &k8sapps.Deployment{ 1455 TypeMeta: k8smeta.TypeMeta{ 1456 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1457 Kind: "Deployment", 1458 }, 1459 ObjectMeta: k8smeta.ObjectMeta{ 1460 Name: "prometheus", 1461 Namespace: "istio-system", 1462 }, 1463 }, 1464 &k8score.Service{ 1465 TypeMeta: k8smeta.TypeMeta{ 1466 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1467 Kind: "Service", 1468 }, 1469 ObjectMeta: k8smeta.ObjectMeta{ 1470 Name: "test-service", 1471 Namespace: testNamespace, 1472 }, 1473 }, 1474 &trait, 1475 &oamcore.ContainerizedWorkload{ 1476 TypeMeta: k8smeta.TypeMeta{ 1477 APIVersion: oamcore.SchemeGroupVersion.Identifier(), 1478 Kind: oamcore.ContainerizedWorkloadKind, 1479 }, 1480 ObjectMeta: k8smeta.ObjectMeta{ 1481 Namespace: testNamespace, 1482 Name: testWorkloadName, 1483 UID: testWorkloadUID, 1484 }, 1485 }, 1486 &oamcore.WorkloadDefinition{ 1487 ObjectMeta: k8smeta.ObjectMeta{ 1488 Name: "containerizedworkloads.core.oam.dev", 1489 }, 1490 Spec: oamcore.WorkloadDefinitionSpec{ 1491 ChildResourceKinds: []oamcore.ChildResourceKind{ 1492 {APIVersion: "apps/v1", Kind: "Deployment", Selector: nil}, 1493 {APIVersion: "v1", Kind: "Service", Selector: nil}, 1494 }, 1495 }, 1496 }, 1497 &k8score.Namespace{ 1498 ObjectMeta: k8smeta.ObjectMeta{ 1499 Name: "test-namespace", 1500 }, 1501 }, 1502 } 1503 1504 if !deploymentDeleted { 1505 objects = append(objects, &k8sapps.Deployment{ 1506 TypeMeta: k8smeta.TypeMeta{ 1507 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1508 Kind: "Deployment", 1509 }, 1510 ObjectMeta: k8smeta.ObjectMeta{ 1511 Name: "test-deployment-name", 1512 Namespace: testNamespace, 1513 CreationTimestamp: k8smeta.Now(), 1514 OwnerReferences: []k8smeta.OwnerReference{{ 1515 APIVersion: oamcore.SchemeGroupVersion.Identifier(), 1516 Kind: oamcore.ContainerizedWorkloadKind, 1517 Name: testWorkloadName, 1518 UID: testWorkloadUID}, 1519 }, 1520 }, 1521 }) 1522 } 1523 1524 return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build() 1525 } 1526 1527 // deploymentWorkloadClient returns a fake client with a deployment target in the trait 1528 func deploymentWorkloadClient(deleting bool) client.WithWatch { 1529 scheme := k8scheme.Scheme 1530 _ = promoperapi.AddToScheme(scheme) 1531 _ = vzapi.AddToScheme(scheme) 1532 _ = oamcore.SchemeBuilder.AddToScheme(scheme) 1533 1534 testNamespace := "test-namespace" 1535 testWorkloadName := "test-workload-name" 1536 1537 trait := vzapi.MetricsTrait{ 1538 TypeMeta: k8smeta.TypeMeta{ 1539 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1540 Kind: vzapi.MetricsTraitKind, 1541 }, 1542 ObjectMeta: k8smeta.ObjectMeta{ 1543 Namespace: testNamespace, 1544 Name: "test-trait-name", 1545 Labels: map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"}, 1546 }, 1547 Spec: vzapi.MetricsTraitSpec{ 1548 WorkloadReference: oamrt.TypedReference{ 1549 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1550 Kind: "Deployment", 1551 Name: testWorkloadName, 1552 }, 1553 }, 1554 } 1555 if deleting { 1556 trait.DeletionTimestamp = &k8smeta.Time{Time: time.Now()} 1557 } 1558 1559 return fake.NewClientBuilder().WithScheme(scheme).WithObjects( 1560 &k8sapps.Deployment{ 1561 TypeMeta: k8smeta.TypeMeta{ 1562 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1563 Kind: "Deployment", 1564 }, 1565 ObjectMeta: k8smeta.ObjectMeta{ 1566 Name: testWorkloadName, 1567 Namespace: testNamespace, 1568 }, 1569 }, 1570 &k8sapps.Deployment{ 1571 TypeMeta: k8smeta.TypeMeta{ 1572 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1573 Kind: "Deployment", 1574 }, 1575 ObjectMeta: k8smeta.ObjectMeta{ 1576 Name: "prometheus", 1577 Namespace: "istio-system", 1578 }, 1579 }, 1580 &k8score.Service{ 1581 TypeMeta: k8smeta.TypeMeta{ 1582 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1583 Kind: "Service", 1584 }, 1585 ObjectMeta: k8smeta.ObjectMeta{ 1586 Name: "test-service", 1587 Namespace: testNamespace, 1588 }, 1589 }, 1590 &trait, 1591 &oamcore.WorkloadDefinition{ 1592 ObjectMeta: k8smeta.ObjectMeta{ 1593 Name: "deployments.apps", 1594 }, 1595 Spec: oamcore.WorkloadDefinitionSpec{ 1596 ChildResourceKinds: []oamcore.ChildResourceKind{ 1597 {APIVersion: "v1", Kind: "Service", Selector: nil}, 1598 }, 1599 }, 1600 }, 1601 &k8score.Namespace{ 1602 ObjectMeta: k8smeta.ObjectMeta{ 1603 Name: "test-namespace", 1604 }, 1605 }, 1606 ).Build() 1607 } 1608 1609 // wlsWorkloadClient returns a fake client with a WebLogic Workload target in the trait 1610 func wlsWorkloadClient(deleting bool) client.WithWatch { 1611 scheme := k8scheme.Scheme 1612 _ = promoperapi.AddToScheme(scheme) 1613 _ = vzapi.AddToScheme(scheme) 1614 _ = oamcore.SchemeBuilder.AddToScheme(scheme) 1615 1616 testNamespace := "test-namespace" 1617 testWorkloadName := "test-workload-name" 1618 testWorkloadUID := types.UID("test-workload-uid") 1619 1620 trait := vzapi.MetricsTrait{ 1621 TypeMeta: k8smeta.TypeMeta{ 1622 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1623 Kind: vzapi.MetricsTraitKind, 1624 }, 1625 ObjectMeta: k8smeta.ObjectMeta{ 1626 Namespace: testNamespace, 1627 Name: "test-trait-name", 1628 Labels: map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"}, 1629 }, 1630 Spec: vzapi.MetricsTraitSpec{ 1631 WorkloadReference: oamrt.TypedReference{ 1632 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1633 Kind: vzconst.VerrazzanoWebLogicWorkloadKind, 1634 Name: testWorkloadName, 1635 }, 1636 }, 1637 } 1638 if deleting { 1639 trait.DeletionTimestamp = &k8smeta.Time{Time: time.Now()} 1640 } 1641 1642 domain := unstructured.Unstructured{} 1643 domain.SetKind("Domain") 1644 domain.SetAPIVersion("weblogic.oracle/v8") 1645 domain.SetName("test-domain") 1646 domain.SetNamespace(testNamespace) 1647 1648 objects := []client.Object{ 1649 &k8sapps.Deployment{ 1650 TypeMeta: k8smeta.TypeMeta{ 1651 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1652 Kind: "Deployment", 1653 }, 1654 ObjectMeta: k8smeta.ObjectMeta{ 1655 Name: "prometheus", 1656 Namespace: "istio-system", 1657 }, 1658 }, 1659 &k8score.Service{ 1660 TypeMeta: k8smeta.TypeMeta{ 1661 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1662 Kind: "Service", 1663 }, 1664 ObjectMeta: k8smeta.ObjectMeta{ 1665 Name: "test-service", 1666 Namespace: testNamespace, 1667 }, 1668 }, 1669 &trait, 1670 &vzapi.VerrazzanoWebLogicWorkload{ 1671 TypeMeta: k8smeta.TypeMeta{ 1672 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1673 Kind: vzconst.VerrazzanoWebLogicWorkloadKind, 1674 }, 1675 ObjectMeta: k8smeta.ObjectMeta{ 1676 Namespace: testNamespace, 1677 Name: testWorkloadName, 1678 UID: testWorkloadUID, 1679 }, 1680 Spec: vzapi.VerrazzanoWebLogicWorkloadSpec{ 1681 Template: vzapi.VerrazzanoWebLogicWorkloadTemplate{ 1682 APIVersion: "weblogic.oracle/v8", 1683 Metadata: runtime.RawExtension{ 1684 Raw: []byte(`{"name": "test-domain"}`), 1685 Object: &unstructured.Unstructured{}, 1686 }, 1687 }, 1688 }, 1689 }, 1690 &oamcore.WorkloadDefinition{ 1691 ObjectMeta: k8smeta.ObjectMeta{ 1692 Name: "domains.weblogic.oracle", 1693 }, 1694 Spec: oamcore.WorkloadDefinitionSpec{ 1695 ChildResourceKinds: []oamcore.ChildResourceKind{ 1696 {APIVersion: "apps/v1", Kind: "Deployment", Selector: nil}, 1697 {APIVersion: "v1", Kind: "Service", Selector: nil}, 1698 }, 1699 }, 1700 }, 1701 &k8sapps.Deployment{ 1702 TypeMeta: k8smeta.TypeMeta{ 1703 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1704 Kind: "Deployment", 1705 }, 1706 ObjectMeta: k8smeta.ObjectMeta{ 1707 Name: "test-deployment-name", 1708 Namespace: testNamespace, 1709 CreationTimestamp: k8smeta.Now(), 1710 OwnerReferences: []k8smeta.OwnerReference{{ 1711 APIVersion: oamcore.SchemeGroupVersion.Identifier(), 1712 Kind: oamcore.ContainerizedWorkloadKind, 1713 Name: testWorkloadName, 1714 UID: testWorkloadUID}, 1715 }, 1716 }, 1717 }, 1718 &k8score.Namespace{ 1719 ObjectMeta: k8smeta.ObjectMeta{ 1720 Name: "test-namespace", 1721 }, 1722 }, 1723 &domain, 1724 } 1725 1726 return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build() 1727 } 1728 1729 // cohWorkloadClient returns a fake client with a Coherence Workload target in the trait 1730 func cohWorkloadClient(deleting bool, portNum int, ports ...int) client.WithWatch { 1731 scheme := k8scheme.Scheme 1732 _ = promoperapi.AddToScheme(scheme) 1733 _ = vzapi.AddToScheme(scheme) 1734 _ = oamcore.SchemeBuilder.AddToScheme(scheme) 1735 1736 testNamespace := "test-namespace" 1737 testWorkloadName := "test-workload-name" 1738 testWorkloadUID := types.UID("test-workload-uid") 1739 1740 coherence := unstructured.Unstructured{} 1741 coherence.SetNamespace(testNamespace) 1742 coherence.SetName("test-coherence") 1743 coherence.SetAPIVersion("coherence.oracle.com/v1") 1744 coherence.SetKind("Coherence") 1745 1746 trait := vzapi.MetricsTrait{ 1747 TypeMeta: k8smeta.TypeMeta{ 1748 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1749 Kind: vzapi.MetricsTraitKind, 1750 }, 1751 ObjectMeta: k8smeta.ObjectMeta{ 1752 Namespace: testNamespace, 1753 Name: "test-trait-name", 1754 Labels: map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"}, 1755 }, 1756 Spec: vzapi.MetricsTraitSpec{ 1757 WorkloadReference: oamrt.TypedReference{ 1758 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1759 Kind: vzconst.VerrazzanoCoherenceWorkloadKind, 1760 Name: testWorkloadName, 1761 }, 1762 }, 1763 } 1764 if deleting { 1765 trait.DeletionTimestamp = &k8smeta.Time{Time: time.Now()} 1766 } 1767 if portNum >= 0 { 1768 trait.Spec.Port = &portNum 1769 } 1770 path := "/metrics" 1771 if len(ports) > 0 { 1772 for i := range ports { 1773 port := vzapi.PortSpec{Port: &ports[i], Path: &path} 1774 trait.Spec.Ports = append(trait.Spec.Ports, port) 1775 } 1776 } 1777 1778 return fake.NewClientBuilder().WithScheme(scheme).WithObjects( 1779 &k8sapps.Deployment{ 1780 TypeMeta: k8smeta.TypeMeta{ 1781 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1782 Kind: "Deployment", 1783 }, 1784 ObjectMeta: k8smeta.ObjectMeta{ 1785 Name: "test-deployment-name", 1786 Namespace: testNamespace, 1787 CreationTimestamp: k8smeta.Now(), 1788 OwnerReferences: []k8smeta.OwnerReference{{ 1789 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1790 Kind: "Coherence", 1791 Name: testWorkloadName, 1792 UID: testWorkloadUID}, 1793 }, 1794 }, 1795 }, 1796 &k8sapps.Deployment{ 1797 TypeMeta: k8smeta.TypeMeta{ 1798 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1799 Kind: "Deployment", 1800 }, 1801 ObjectMeta: k8smeta.ObjectMeta{ 1802 Name: "prometheus", 1803 Namespace: "istio-system", 1804 }, 1805 }, 1806 &k8score.Service{ 1807 TypeMeta: k8smeta.TypeMeta{ 1808 APIVersion: k8sapps.SchemeGroupVersion.Identifier(), 1809 Kind: "Service", 1810 }, 1811 ObjectMeta: k8smeta.ObjectMeta{ 1812 Name: "test-service", 1813 Namespace: testNamespace, 1814 }, 1815 }, 1816 &trait, 1817 &vzapi.VerrazzanoCoherenceWorkload{ 1818 TypeMeta: k8smeta.TypeMeta{ 1819 APIVersion: vzapi.SchemeGroupVersion.Identifier(), 1820 Kind: vzconst.VerrazzanoCoherenceWorkloadKind, 1821 }, 1822 ObjectMeta: k8smeta.ObjectMeta{ 1823 Namespace: testNamespace, 1824 Name: testWorkloadName, 1825 UID: testWorkloadUID, 1826 }, 1827 Spec: vzapi.VerrazzanoCoherenceWorkloadSpec{ 1828 Template: runtime.RawExtension{ 1829 Raw: []byte(`{"metadata":{"name": "test-coherence"}}`), 1830 Object: &unstructured.Unstructured{}, 1831 }, 1832 }, 1833 }, 1834 &coherence, 1835 &oamcore.WorkloadDefinition{ 1836 ObjectMeta: k8smeta.ObjectMeta{ 1837 Name: "coherences.coherence.oracle.com", 1838 }, 1839 Spec: oamcore.WorkloadDefinitionSpec{ 1840 ChildResourceKinds: []oamcore.ChildResourceKind{ 1841 {APIVersion: "apps/v1", Kind: "Deployment", Selector: nil}, 1842 {APIVersion: "v1", Kind: "Service", Selector: nil}, 1843 }, 1844 }, 1845 }, 1846 ).Build() 1847 } 1848 1849 func getBoolPtr(b bool) *bool { 1850 return &b 1851 }