github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/navigation/trait_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 navigation 5 6 import ( 7 "context" 8 "fmt" 9 "testing" 10 11 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 12 13 oamrt "github.com/crossplane/crossplane-runtime/apis/common/v1" 14 "github.com/golang/mock/gomock" 15 asserts "github.com/stretchr/testify/assert" 16 vzapi "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1" 17 "github.com/verrazzano/verrazzano/application-operator/mocks" 18 "go.uber.org/zap" 19 k8serrors "k8s.io/apimachinery/pkg/api/errors" 20 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 22 "k8s.io/apimachinery/pkg/runtime/schema" 23 "k8s.io/apimachinery/pkg/types" 24 "sigs.k8s.io/controller-runtime/pkg/client" 25 ) 26 27 // TestFetchTrait tests various usages of FetchTrait 28 func TestFetchTrait(t *testing.T) { 29 assert := asserts.New(t) 30 31 var mocker *gomock.Controller 32 var cli *mocks.MockClient 33 var trait *vzapi.MetricsTrait 34 var err error 35 var name types.NamespacedName 36 37 // GIVEN a name for a trait that does exists 38 // WHEN the trait is fetched 39 // THEN verify that the returned trait has correct content 40 mocker = gomock.NewController(t) 41 cli = mocks.NewMockClient(mocker) 42 name = types.NamespacedName{Namespace: "test-namespace", Name: "test-name"} 43 cli.EXPECT().Get(gomock.Eq(context.TODO()), gomock.Eq(name), gomock.Not(gomock.Nil()), gomock.Any()). 44 DoAndReturn(func(ctx context.Context, key client.ObjectKey, trait *vzapi.MetricsTrait, opts ...client.GetOption) error { 45 trait.Name = "test-name" 46 return nil 47 }) 48 trait, err = FetchTrait(context.TODO(), cli, zap.S(), name) 49 mocker.Finish() 50 assert.NoError(err) 51 assert.Equal("test-name", trait.Name) 52 53 // GIVEN a name for a trait that does not exist 54 // WHEN the trait is fetched 55 // THEN verify that both the returned trait and error are nil 56 mocker = gomock.NewController(t) 57 cli = mocks.NewMockClient(mocker) 58 name = types.NamespacedName{Namespace: "test-namespace", Name: "test-name"} 59 cli.EXPECT().Get(gomock.Eq(context.TODO()), gomock.Eq(name), gomock.Not(gomock.Nil()), gomock.Any()). 60 DoAndReturn(func(ctx context.Context, key client.ObjectKey, trait *vzapi.MetricsTrait, opts ...client.GetOption) error { 61 return k8serrors.NewNotFound(schema.GroupResource{Group: trait.APIVersion, Resource: trait.Kind}, key.Name) 62 }) 63 trait, err = FetchTrait(context.TODO(), cli, zap.S(), name) 64 mocker.Finish() 65 assert.Nil(trait) 66 assert.NoError(err) 67 68 // GIVEN a name for a trait that should exist 69 // WHEN the trait is fetched and there is an underlying error 70 // THEN verify that the error is propagated 71 mocker = gomock.NewController(t) 72 cli = mocks.NewMockClient(mocker) 73 cli.EXPECT().Get(gomock.Eq(context.TODO()), gomock.Eq(name), gomock.Not(gomock.Nil()), gomock.Any()). 74 DoAndReturn(func(ctx context.Context, key client.ObjectKey, trait *vzapi.MetricsTrait, opts ...client.GetOption) error { 75 return fmt.Errorf("test-error") 76 }) 77 name = types.NamespacedName{Namespace: "test-namespace", Name: "test-name"} 78 trait, err = FetchTrait(context.TODO(), cli, zap.S(), name) 79 mocker.Finish() 80 assert.Nil(trait) 81 assert.Error(err) 82 assert.Equal("test-error", err.Error()) 83 } 84 85 // TestFetchWorkloadFromTrait tests various usages of FetchWorkloadFromTrait 86 func TestFetchWorkloadFromTrait(t *testing.T) { 87 assert := asserts.New(t) 88 89 var mocker *gomock.Controller 90 var cli *mocks.MockClient 91 var ctx = context.TODO() 92 var trait *vzapi.IngressTrait 93 var err error 94 var uns *unstructured.Unstructured 95 96 // GIVEN a trait with a reference to a workload that can be found 97 // WHEN the workload is fetched via the trait 98 // THEN verify the workload content is correct 99 mocker = gomock.NewController(t) 100 cli = mocks.NewMockClient(mocker) 101 trait = &vzapi.IngressTrait{ 102 TypeMeta: metav1.TypeMeta{Kind: "IngressTrait", APIVersion: "oam.verrazzano.io/v1alpha1"}, 103 ObjectMeta: metav1.ObjectMeta{Name: "test-trait-name", Namespace: "test-trait-namespace"}, 104 Spec: vzapi.IngressTraitSpec{WorkloadReference: oamrt.TypedReference{ 105 APIVersion: "core.oam.dev/v1alpha2", Kind: "ContainerizedWorkload", Name: "test-workload-name"}}} 106 cli.EXPECT(). 107 Get(gomock.Eq(ctx), gomock.Eq(client.ObjectKey{Namespace: "test-trait-namespace", Name: "test-workload-name"}), gomock.Not(gomock.Nil()), gomock.Any()). 108 DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj *unstructured.Unstructured, opts ...client.GetOption) error { 109 obj.SetNamespace(key.Namespace) 110 obj.SetName(key.Name) 111 return nil 112 }) 113 uns, err = FetchWorkloadFromTrait(ctx, cli, vzlog.DefaultLogger(), trait) 114 mocker.Finish() 115 assert.NoError(err) 116 assert.NotNil(uns) 117 assert.Equal("test-trait-namespace", uns.GetNamespace()) 118 assert.Equal("test-workload-name", uns.GetName()) 119 120 // GIVEN a trait with a reference to a workload 121 // WHEN a failure occurs attempting to fetch the trait's workload 122 // THEN verify the error is propagated 123 mocker = gomock.NewController(t) 124 cli = mocks.NewMockClient(mocker) 125 trait = &vzapi.IngressTrait{ 126 TypeMeta: metav1.TypeMeta{Kind: "IngressTrait", APIVersion: "oam.verrazzano.io/v1alpha1"}, 127 ObjectMeta: metav1.ObjectMeta{Name: "test-trait-name", Namespace: "test-trait-namespace"}, 128 Spec: vzapi.IngressTraitSpec{WorkloadReference: oamrt.TypedReference{ 129 APIVersion: "core.oam.dev/v1alpha2", Kind: "ContainerizedWorkload", Name: "test-workload-name"}}} 130 cli.EXPECT(). 131 Get(gomock.Eq(ctx), gomock.Eq(client.ObjectKey{Namespace: "test-trait-namespace", Name: "test-workload-name"}), gomock.Not(gomock.Nil()), gomock.Any()). 132 DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj *unstructured.Unstructured, opts ...client.GetOption) error { 133 return fmt.Errorf("test-error") 134 }) 135 uns, err = FetchWorkloadFromTrait(ctx, cli, vzlog.DefaultLogger(), trait) 136 mocker.Finish() 137 assert.Nil(uns) 138 assert.Error(err) 139 assert.Equal("test-error", err.Error()) 140 141 // GIVEN a trait with a reference to a Verrazzano workload type 142 // WHEN the workload is fetched via the trait 143 // THEN verify that the contained workload is unwrapped and returned 144 mocker = gomock.NewController(t) 145 cli = mocks.NewMockClient(mocker) 146 147 workloadAPIVersion := "oam.verrazzano.io/v1alpha1" 148 workloadKind := "VerrazzanoCoherenceWorkload" 149 150 containedAPIVersion := "coherence.oracle.com/v1" 151 containedKind := "Coherence" 152 containedName := "unit-test-resource" 153 154 containedResource := map[string]interface{}{ 155 "metadata": map[string]interface{}{ 156 "name": containedName, 157 }, 158 } 159 160 trait = &vzapi.IngressTrait{ 161 TypeMeta: metav1.TypeMeta{Kind: "IngressTrait", APIVersion: "oam.verrazzano.io/v1alpha1"}, 162 ObjectMeta: metav1.ObjectMeta{Name: "test-trait-name", Namespace: "test-trait-namespace"}, 163 Spec: vzapi.IngressTraitSpec{WorkloadReference: oamrt.TypedReference{ 164 APIVersion: workloadAPIVersion, Kind: workloadKind, Name: "test-workload-name"}}} 165 166 // expect a call to fetch the referenced workload 167 cli.EXPECT(). 168 Get(gomock.Eq(ctx), gomock.Eq(client.ObjectKey{Namespace: "test-trait-namespace", Name: "test-workload-name"}), gomock.Not(gomock.Nil()), gomock.Any()). 169 DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj *unstructured.Unstructured, opts ...client.GetOption) error { 170 obj.SetNamespace(key.Namespace) 171 obj.SetName(key.Name) 172 obj.SetAPIVersion(workloadAPIVersion) 173 obj.SetKind(workloadKind) 174 unstructured.SetNestedMap(obj.Object, containedResource, "spec", "template") 175 return nil 176 }) 177 // expect a call to fetch the contained workload that is wrapped by the Verrazzano workload 178 cli.EXPECT(). 179 Get(gomock.Eq(ctx), gomock.Eq(client.ObjectKey{Namespace: "test-trait-namespace", Name: containedName}), gomock.Not(gomock.Nil()), gomock.Any()). 180 DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj *unstructured.Unstructured, opts ...client.GetOption) error { 181 obj.SetUnstructuredContent(containedResource) 182 obj.SetAPIVersion(containedAPIVersion) 183 obj.SetKind(containedKind) 184 return nil 185 }) 186 187 uns, err = FetchWorkloadFromTrait(ctx, cli, vzlog.DefaultLogger(), trait) 188 189 mocker.Finish() 190 assert.NoError(err) 191 assert.NotNil(uns) 192 assert.Equal(containedAPIVersion, uns.GetAPIVersion()) 193 assert.Equal(containedKind, uns.GetKind()) 194 assert.Equal(containedName, uns.GetName()) 195 196 // GIVEN a trait with a reference to a VerrazzanoHelidonWorkload that can be found 197 // WHEN the workload is fetched via the trait 198 // THEN verify the workload content is correct 199 mocker = gomock.NewController(t) 200 cli = mocks.NewMockClient(mocker) 201 trait = &vzapi.IngressTrait{ 202 TypeMeta: metav1.TypeMeta{Kind: "IngressTrait", APIVersion: "oam.verrazzano.io/v1alpha1"}, 203 ObjectMeta: metav1.ObjectMeta{Name: "test-trait-name", Namespace: "test-trait-namespace"}, 204 Spec: vzapi.IngressTraitSpec{WorkloadReference: oamrt.TypedReference{ 205 APIVersion: "oam.verrazzano.io/v1alpha1", Kind: "VerrazzanoHelidonWorkload", Name: "test-helidon-workload"}}} 206 cli.EXPECT(). 207 Get(gomock.Eq(ctx), gomock.Eq(client.ObjectKey{Namespace: "test-trait-namespace", Name: "test-helidon-workload"}), gomock.Not(gomock.Nil()), gomock.Any()). 208 DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj *unstructured.Unstructured, opts ...client.GetOption) error { 209 obj.SetNamespace(key.Namespace) 210 obj.SetName(key.Name) 211 return nil 212 }) 213 uns, err = FetchWorkloadFromTrait(ctx, cli, vzlog.DefaultLogger(), trait) 214 mocker.Finish() 215 assert.NoError(err) 216 assert.NotNil(uns) 217 218 assert.Equal("test-trait-namespace", uns.GetNamespace()) 219 assert.Equal("test-helidon-workload", uns.GetName()) 220 } 221 222 // TestFetchWorkloadResource tests various usages of FetchWorkloadResource 223 func TestFetchWorkloadResource(t *testing.T) { 224 assert := asserts.New(t) 225 226 var mocker *gomock.Controller 227 var cli *mocks.MockClient 228 var ctx = context.TODO() 229 var err error 230 var uns *unstructured.Unstructured 231 232 // GIVEN a non Verrazzano specific workload 233 // WHEN the workload resource is fetched 234 // THEN verify the workload is returned as is 235 mocker = gomock.NewController(t) 236 cli = mocks.NewMockClient(mocker) 237 uns = &unstructured.Unstructured{} 238 containerizedWorkloadName := "container-workload" 239 containerizedWorkloadNamespace := "default" 240 containerizedWorkloadAPIVersion := "core.oam.dev/v1alpha2" 241 containerizedWorkloadKind := "ContainerizedWorkload" 242 uns.SetNamespace(containerizedWorkloadNamespace) 243 uns.SetName(containerizedWorkloadName) 244 uns.SetAPIVersion(containerizedWorkloadAPIVersion) 245 uns.SetKind(containerizedWorkloadKind) 246 uns, err = FetchWorkloadResource(ctx, cli, vzlog.DefaultLogger(), uns) 247 assert.NoError(err) 248 assert.Equal(containerizedWorkloadNamespace, uns.GetNamespace()) 249 assert.Equal(containerizedWorkloadName, uns.GetName()) 250 assert.Equal(containerizedWorkloadAPIVersion, uns.GetAPIVersion()) 251 assert.Equal(containerizedWorkloadKind, uns.GetKind()) 252 253 // GIVEN a Verrazzano specific workload 254 // WHEN the workload resource is fetched 255 // THEN verify the contained workload is returned 256 mocker = gomock.NewController(t) 257 cli = mocks.NewMockClient(mocker) 258 uns = &unstructured.Unstructured{} 259 workloadName := "coherence-workload" 260 workloadNamespace := "default" 261 workloadAPIVersion := "oam.verrazzano.io/v1alpha1" 262 workloadKind := "VerrazzanoCoherenceWorkload" 263 264 containedAPIVersion := "coherence.oracle.com/v1" 265 containedKind := "Coherence" 266 containedName := "unit-test-resource" 267 containedNamespace := "default" 268 269 containedResource := map[string]interface{}{ 270 "metadata": map[string]interface{}{ 271 "name": containedName, 272 }, 273 } 274 275 uns.SetNamespace(workloadNamespace) 276 uns.SetName(workloadName) 277 uns.SetAPIVersion(workloadAPIVersion) 278 uns.SetKind(workloadKind) 279 unstructured.SetNestedMap(uns.Object, containedResource, "spec", "template") 280 // expect a call to fetch the contained workload that is wrapped by the Verrazzano workload 281 cli.EXPECT(). 282 Get(gomock.Eq(ctx), gomock.Eq(client.ObjectKey{Namespace: containedNamespace, Name: containedName}), gomock.Not(gomock.Nil()), gomock.Any()). 283 DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj *unstructured.Unstructured, opts ...client.GetOption) error { 284 obj.SetUnstructuredContent(containedResource) 285 obj.SetAPIVersion(containedAPIVersion) 286 obj.SetKind(containedKind) 287 obj.SetNamespace(containedNamespace) 288 return nil 289 }) 290 291 uns, err = FetchWorkloadResource(ctx, cli, vzlog.DefaultLogger(), uns) 292 assert.NoError(err) 293 assert.Equal(containedNamespace, uns.GetNamespace()) 294 assert.Equal(containedName, uns.GetName()) 295 assert.Equal(containedAPIVersion, uns.GetAPIVersion()) 296 assert.Equal(containedKind, uns.GetKind()) 297 298 // GIVEN a Verrazzano specific workload 299 // WHEN a failure occurs attempting to fetch the workload resource 300 // THEN verify the error is propagated 301 mocker = gomock.NewController(t) 302 cli = mocks.NewMockClient(mocker) 303 uns = &unstructured.Unstructured{} 304 workloadName = "coherence-workload" 305 workloadNamespace = "default" 306 workloadAPIVersion = "oam.verrazzano.io/v1alpha1" 307 workloadKind = "VerrazzanoCoherenceWorkload" 308 309 containedAPIVersion = "coherence.oracle.com/v1" 310 containedKind = "Coherence" 311 containedName = "unit-test-resource" 312 containedNamespace = "default" 313 314 containedResource = map[string]interface{}{ 315 "metadata": map[string]interface{}{ 316 "name": containedName, 317 }, 318 } 319 320 uns.SetNamespace(workloadNamespace) 321 uns.SetName(workloadName) 322 uns.SetAPIVersion(workloadAPIVersion) 323 uns.SetKind(workloadKind) 324 unstructured.SetNestedMap(uns.Object, containedResource, "spec", "template") 325 // expect a call to fetch the contained workload that is wrapped by the Verrazzano workload and return error 326 cli.EXPECT(). 327 Get(gomock.Eq(ctx), gomock.Eq(client.ObjectKey{Namespace: containedNamespace, Name: containedName}), gomock.Not(gomock.Nil()), gomock.Any()). 328 DoAndReturn(func(ctx context.Context, key client.ObjectKey, obj *unstructured.Unstructured, opts ...client.GetOption) error { 329 return fmt.Errorf("test-error") 330 }) 331 332 uns, err = FetchWorkloadResource(ctx, cli, vzlog.DefaultLogger(), uns) 333 mocker.Finish() 334 assert.Nil(uns) 335 assert.Error(err) 336 assert.Equal("test-error", err.Error()) 337 } 338 339 // TestIsWeblogicWorkloadKind tests the IsWeblogicWorkloadKind function 340 // GIVEN a trait with a WorkloadReference 341 // WHEN a call to IsWeblogicWorkloadKind is made 342 // THEN return true if the trait references a VerrazzanoWebLogic workload kind, false otherwise 343 func TestIsWeblogicWorkloadKind(t *testing.T) { 344 assert := asserts.New(t) 345 346 trait := &vzapi.IngressTrait{} 347 assert.False(IsWeblogicWorkloadKind(trait)) 348 349 trait = &vzapi.IngressTrait{ 350 Spec: vzapi.IngressTraitSpec{ 351 WorkloadReference: oamrt.TypedReference{Kind: "VerrazzanoCoherenceWorkload"}, 352 }, 353 } 354 assert.False(IsWeblogicWorkloadKind(trait)) 355 356 trait = &vzapi.IngressTrait{ 357 Spec: vzapi.IngressTraitSpec{ 358 WorkloadReference: oamrt.TypedReference{Kind: "VerrazzanoWebLogicWorkload"}, 359 }, 360 } 361 assert.True(IsWeblogicWorkloadKind(trait)) 362 }