Skip to content

Commit c3ec13e

Browse files
authored
Simplify nstemplatetiers.GenerateTiers (#486)
* simplify nstemplatetiers.GenerateTiers to not distiniguish between create/update in the EnsureObject func. This is in preparation for using the SSA client that doesn't do that either. * be clever about detecting the generation change and try to detect possible test problems with the test templates
1 parent 109b36e commit c3ec13e

4 files changed

Lines changed: 35 additions & 77 deletions

File tree

pkg/template/nstemplatetiers/nstemplatetier_generator.go

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121

2222
var log = logf.Log.WithName("templates")
2323

24-
type EnsureObject func(toEnsure runtimeclient.Object, canUpdate bool, tierName string) (bool, error)
24+
type EnsureObject func(toEnsure runtimeclient.Object, tierName string) error
2525

2626
type TierGenerator struct {
2727
ensureObject EnsureObject
@@ -76,7 +76,6 @@ func GenerateTiers(s *runtime.Scheme, ensureObject EnsureObject, namespace strin
7676

7777
// newNSTemplateTierGenerator loads templates from the provided assets and processes the tierTemplates and NSTemplateTiers
7878
func newNSTemplateTierGenerator(s *runtime.Scheme, ensureObject EnsureObject, namespace string, metadata map[string]string, files map[string][]byte) (*TierGenerator, error) {
79-
8079
templatesByTier, err := loadTemplatesByTiers(metadata, files)
8180
if err != nil {
8281
return nil, err
@@ -141,7 +140,6 @@ type BasedOnTier struct {
141140
// an optional `template` for the cluster resources (`clusterTemplate`) and the NSTemplateTier resource object.
142141
// Each `template` object contains a `revision` (`string`) and the `content` of the template to apply (`[]byte`)
143142
func loadTemplatesByTiers(metadata map[string]string, files map[string][]byte) (map[string]*tierData, error) {
144-
145143
results := make(map[string]*tierData)
146144
for name, content := range files {
147145
// split the name using the `/` separator
@@ -203,7 +201,6 @@ func loadTemplatesByTiers(metadata map[string]string, files map[string][]byte) (
203201

204202
// initTierTemplates generates all TierTemplate resources, and adds them to the tier map indexed by tier name
205203
func (t *TierGenerator) initTierTemplates() error {
206-
207204
// process tiers in alphabetical order
208205
tiers := make([]string, 0, len(t.templatesByTier))
209206
for tier := range t.templatesByTier {
@@ -278,8 +275,7 @@ func (t *TierGenerator) createTierTemplates() error {
278275
for tierName, tierTmpls := range t.templatesByTier {
279276
for _, tierTmpl := range tierTmpls.tierTemplates {
280277
log.Info("creating TierTemplate", "namespace", tierTmpl.Namespace, "name", tierTmpl.Name)
281-
// using the "standard" client since we don't need to support updates on such resources, they should be immutable
282-
if _, err := t.ensureObject(tierTmpl, false, tierName); err != nil {
278+
if err := t.ensureObject(tierTmpl, tierName); err != nil {
283279
return errors.Wrapf(err, "unable to create the '%s' TierTemplate in namespace '%s'", tierTmpl.Name, tierTmpl.Namespace)
284280
}
285281
log.Info("TierTemplate resource created", "namespace", tierTmpl.Namespace, "name", tierTmpl.Name)
@@ -336,7 +332,6 @@ func newTierTemplateName(tier, kind, revision string) string {
336332

337333
// newNSTemplateTiers generates all NSTemplateTier resources and adds them to the tier map
338334
func (t *TierGenerator) initNSTemplateTiers() error {
339-
340335
for tierName, tierData := range t.templatesByTier {
341336
nsTemplateTier := tierData.rawTemplates.nsTemplateTier
342337
tierTemplates := tierData.tierTemplates
@@ -360,7 +355,6 @@ func (t *TierGenerator) initNSTemplateTiers() error {
360355

361356
// createNSTemplateTiers creates the NSTemplateTier resources from the tier map
362357
func (t *TierGenerator) createNSTemplateTiers() error {
363-
364358
for tierName, tierData := range t.templatesByTier {
365359
if len(tierData.objects) != 1 {
366360
return fmt.Errorf("there is an unexpected number of NSTemplateTier object to be applied for tier name '%s'; expected: 1; actual: %d", tierName, len(tierData.objects))
@@ -380,7 +374,7 @@ func (t *TierGenerator) createNSTemplateTiers() error {
380374
labels = make(map[string]string)
381375
}
382376
labels[toolchainv1alpha1.ProviderLabelKey] = toolchainv1alpha1.ProviderLabelValue
383-
updated, err := t.ensureObject(tier, true, tierName)
377+
err := t.ensureObject(tier, tierName)
384378
if err != nil {
385379
return errors.Wrapf(err, "unable to create or update the '%s' NSTemplateTier", tierName)
386380
}
@@ -394,11 +388,7 @@ func (t *TierGenerator) createNSTemplateTiers() error {
394388
for role, nsTemplate := range tier.Spec.SpaceRoles {
395389
tierLog = tierLog.WithValues(fmt.Sprintf("spaceRoleTemplate-%s", role), nsTemplate.TemplateRef)
396390
}
397-
if updated {
398-
tierLog.Info("NSTemplateTier was either updated or created")
399-
} else {
400-
tierLog.Info("NSTemplateTier wasn't updated nor created: the spec was already set as expected")
401-
}
391+
tierLog.Info("NSTemplateTier was patched")
402392
}
403393
return nil
404394
}

pkg/template/nstemplatetiers/nstemplatetier_generator_test.go

Lines changed: 30 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io/fs"
99
"path/filepath"
10+
"reflect"
1011
"regexp"
1112
"testing"
1213
texttemplate "text/template"
@@ -17,8 +18,6 @@ import (
1718
commonclient "github.com/codeready-toolchain/toolchain-common/pkg/client"
1819
"github.com/codeready-toolchain/toolchain-common/pkg/test"
1920
"github.com/pkg/errors"
20-
apierrors "k8s.io/apimachinery/pkg/api/errors"
21-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2221
"k8s.io/apimachinery/pkg/types"
2322
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
2423

@@ -141,12 +140,10 @@ func getTestTemplates(t *testing.T) map[string][]byte {
141140
}
142141

143142
func TestGenerateTiers(t *testing.T) {
144-
145143
s := addToScheme(t)
146144
logf.SetLogger(zap.New(zap.UseDevMode(true)))
147145

148146
t.Run("ok", func(t *testing.T) {
149-
150147
expectedTemplateRefs := map[string]map[string]interface{}{
151148
"advanced": {
152149
"clusterresources": "advanced-clusterresources-abcd123-654321a",
@@ -280,22 +277,35 @@ func TestGenerateTiers(t *testing.T) {
280277
// when
281278
err := GenerateTiers(s, ensureObjectFuncForClient(clt), namespace, getTestMetadata(), getTestTemplates(t))
282279
require.NoError(t, err)
280+
// here we're just capturing the state after the first call so that we can compare with the state after the second call
281+
tierTmpls := toolchainv1alpha1.TierTemplateList{}
282+
err = clt.List(context.TODO(), &tierTmpls, runtimeclient.InNamespace(namespace))
283+
require.NoError(t, err)
284+
require.Len(t, tierTmpls.Items, 16) // 4 items for advanced and base tiers + 3 for nocluster tier + 4 for appstudio
285+
origTemplates := map[string]*toolchainv1alpha1.TierTemplate{}
286+
for _, tmpl := range tierTmpls.Items {
287+
origTemplates[tmpl.Name] = tmpl.DeepCopy()
288+
}
283289

284290
// when calling CreateOrUpdateResources a second time
285291
err = GenerateTiers(s, ensureObjectFuncForClient(clt), namespace, getTestMetadata(), getTestTemplates(t))
286292

287293
// then
288294
require.NoError(t, err)
289295
// verify that all TierTemplate CRs were updated
290-
tierTmpls := toolchainv1alpha1.TierTemplateList{}
296+
tierTmpls = toolchainv1alpha1.TierTemplateList{}
291297
err = clt.List(context.TODO(), &tierTmpls, runtimeclient.InNamespace(namespace))
292298
require.NoError(t, err)
293299
require.Len(t, tierTmpls.Items, 16) // 4 items for advanced and base tiers + 3 for nocluster tier + 4 for appstudio
300+
// check that the tier templates are unchanged
294301
for _, tierTmpl := range tierTmpls.Items {
295-
assert.Equal(t, int64(1), tierTmpl.Generation) // unchanged
296-
}
297-
298-
// verify that 4 NSTemplateTier CRs were created:
302+
// these two should always mean the same thing. If they don't, it means there's an issue with serde of
303+
// the templates where template json -> object in cluster -> template json doesn't yield the same thing.
304+
// (the json reprentation is used to detect generation change in our test.Client).
305+
// This is usually caused by e.g explicitly using a zero value of some property in the template file.
306+
assert.True(t, reflect.DeepEqual(origTemplates[tierTmpl.Name].Spec, tierTmpl.Spec), "deep equal different on %T %s", tierTmpl, tierTmpl.Name)
307+
assert.Equal(t, int64(1), tierTmpl.Generation, "generation different on %T %s", tierTmpl, tierTmpl.Name) // unchanged
308+
} // verify that 4 NSTemplateTier CRs were created:
299309
for _, tierName := range []string{"advanced", "base", "nocluster", "appstudio"} {
300310
tier := toolchainv1alpha1.NSTemplateTier{}
301311
err = clt.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: tierName}, &tier)
@@ -446,27 +456,25 @@ func TestGenerateTiers(t *testing.T) {
446456
})
447457

448458
t.Run("failures", func(t *testing.T) {
449-
450459
namespace := "host-operator" + uuid.NewString()[:7]
451460

452461
t.Run("nstemplatetiers", func(t *testing.T) {
453-
454-
t.Run("failed to create nstemplatetiers", func(t *testing.T) {
462+
t.Run("failed to patch nstemplatetiers", func(t *testing.T) {
455463
// given
456464
clt := test.NewFakeClient(t)
457-
clt.MockCreate = func(ctx context.Context, obj runtimeclient.Object, opts ...runtimeclient.CreateOption) error {
458-
if obj.GetObjectKind().GroupVersionKind().Kind == "NSTemplateTier" {
465+
clt.MockPatch = func(ctx context.Context, obj runtimeclient.Object, patch runtimeclient.Patch, opts ...runtimeclient.PatchOption) error {
466+
if _, ok := obj.(*toolchainv1alpha1.NSTemplateTier); ok {
459467
// simulate a client/server error
460468
return errors.Errorf("an error")
461469
}
462-
return clt.Client.Create(ctx, obj, opts...)
470+
return test.Patch(ctx, clt, obj, patch, opts...)
463471
}
464472

465473
// when
466474
err := GenerateTiers(s, ensureObjectFuncForClient(clt), namespace, getTestMetadata(), getTestTemplates(t))
467475
// then
468476
require.Error(t, err)
469-
assert.Regexp(t, "unable to create or update the '\\w+' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: an error", err.Error())
477+
assert.Regexp(t, "unable to create NSTemplateTiers: unable to create or update the '\\w+' NSTemplateTier: unable to patch 'toolchain.dev.openshift.com/v1alpha1, Kind=NSTemplateTier' called '\\w+' in namespace '[a-zA-Z0-9-]+': an error", err.Error())
470478
})
471479

472480
t.Run("missing tier.yaml file", func(t *testing.T) {
@@ -481,40 +489,15 @@ func TestGenerateTiers(t *testing.T) {
481489
require.EqualError(t, err, "unable to init NSTemplateTier generator: tier appstudio is missing a tier.yaml file")
482490
})
483491

484-
t.Run("failed to update nstemplatetiers", func(t *testing.T) {
485-
// given
486-
// initialize the client with an existing `advanced` NSTemplatetier
487-
clt := test.NewFakeClient(t, &toolchainv1alpha1.NSTemplateTier{
488-
ObjectMeta: metav1.ObjectMeta{
489-
Namespace: namespace,
490-
Name: "advanced",
491-
},
492-
})
493-
clt.MockUpdate = func(ctx context.Context, obj runtimeclient.Object, opts ...runtimeclient.UpdateOption) error {
494-
if obj.GetObjectKind().GroupVersionKind().Kind == "NSTemplateTier" {
495-
// simulate a client/server error
496-
return errors.Errorf("an error")
497-
}
498-
return clt.Client.Update(ctx, obj, opts...)
499-
}
500-
501-
// when
502-
err := GenerateTiers(s, ensureObjectFuncForClient(clt), namespace, getTestMetadata(), getTestTemplates(t))
503-
504-
// then
505-
require.Error(t, err)
506-
assert.Contains(t, err.Error(), "unable to create NSTemplateTiers: unable to create or update the 'advanced' NSTemplateTier: unable to create resource of kind: NSTemplateTier, version: v1alpha1: unable to update the resource")
507-
})
508-
509-
t.Run("failed to create nstemplatetiers", func(t *testing.T) {
492+
t.Run("failed to patch nstemplatetiers", func(t *testing.T) {
510493
// given
511494
clt := test.NewFakeClient(t)
512-
clt.MockCreate = func(ctx context.Context, obj runtimeclient.Object, opts ...runtimeclient.CreateOption) error {
495+
clt.MockPatch = func(ctx context.Context, obj runtimeclient.Object, patch runtimeclient.Patch, opts ...runtimeclient.PatchOption) error {
513496
if _, ok := obj.(*toolchainv1alpha1.TierTemplate); ok {
514497
// simulate a client/server error
515498
return errors.Errorf("an error")
516499
}
517-
return clt.Client.Create(ctx, obj, opts...)
500+
return test.Patch(ctx, clt, obj, patch, opts...)
518501
}
519502

520503
// when
@@ -529,7 +512,6 @@ func TestGenerateTiers(t *testing.T) {
529512
}
530513

531514
func TestLoadTemplatesByTiers(t *testing.T) {
532-
533515
logf.SetLogger(zap.New(zap.UseDevMode(true)))
534516

535517
t.Run("ok", func(t *testing.T) {
@@ -591,7 +573,6 @@ func TestLoadTemplatesByTiers(t *testing.T) {
591573
})
592574

593575
t.Run("failures", func(t *testing.T) {
594-
595576
t.Run("unparseable content", func(t *testing.T) {
596577
// given
597578
testTemplates := getTestTemplates(t)
@@ -663,13 +644,11 @@ func TestLoadTemplatesByTiers(t *testing.T) {
663644
}
664645

665646
func TestNewNSTemplateTier(t *testing.T) {
666-
667647
s := scheme.Scheme
668648
err := toolchainv1alpha1.AddToScheme(s)
669649
require.NoError(t, err)
670650

671651
t.Run("ok", func(t *testing.T) {
672-
673652
t.Run("with test assets", func(t *testing.T) {
674653
// given
675654
namespace := "host-operator-" + uuid.NewString()[:7]
@@ -740,7 +719,6 @@ func TestNewTierTemplate(t *testing.T) {
740719
namespace := "host-operator-" + uuid.NewString()[:7]
741720

742721
t.Run("ok", func(t *testing.T) {
743-
744722
t.Run("with test assets", func(t *testing.T) {
745723
// given
746724

@@ -780,7 +758,6 @@ func TestNewTierTemplate(t *testing.T) {
780758
})
781759

782760
t.Run("failures", func(t *testing.T) {
783-
784761
t.Run("invalid template", func(t *testing.T) {
785762
// given
786763
testTemplates := getTestTemplates(t)
@@ -796,15 +773,9 @@ func TestNewTierTemplate(t *testing.T) {
796773
}
797774

798775
func ensureObjectFuncForClient(cl runtimeclient.Client) EnsureObject {
799-
return func(toEnsure runtimeclient.Object, canUpdate bool, _ string) (bool, error) {
800-
if !canUpdate {
801-
if err := cl.Create(context.TODO(), toEnsure); err != nil && !apierrors.IsAlreadyExists(err) {
802-
return false, err
803-
}
804-
return true, nil
805-
}
806-
applyCl := commonclient.NewApplyClient(cl)
807-
return applyCl.ApplyObject(context.TODO(), toEnsure, commonclient.ForceUpdate(true))
776+
return func(toEnsure runtimeclient.Object, _ string) error {
777+
applyCl := commonclient.NewSSAApplyClient(cl, "testFieldManager")
778+
return applyCl.ApplyObject(context.TODO(), toEnsure)
808779
}
809780
}
810781

@@ -862,7 +833,6 @@ func expectedTemplateFromBasedOnTierConfig(t *testing.T, tier, templateFileName
862833
}
863834

864835
func TestNewNSTemplateTiers(t *testing.T) {
865-
866836
// given
867837
s := addToScheme(t)
868838

pkg/template/nstemplatetiers/testdata/nstemplatetiers/appstudio/cluster.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ objects:
1919
requests.ephemeral-storage: 7Gi
2020
count/persistentvolumeclaims: "5"
2121
selector:
22-
annotations: null
2322
labels:
2423
matchLabels:
2524
toolchain.dev.openshift.com/space: ${SPACE_NAME}

pkg/template/nstemplatetiers/testdata/nstemplatetiers/base/cluster.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ objects:
1717
requests.storage: 7Gi
1818
persistentvolumeclaims: "5"
1919
selector:
20-
annotations: null
2120
labels:
2221
matchLabels:
2322
toolchain.dev.openshift.com/space: ${SPACE_NAME}
2423
parameters:
2524
- name: SPACE_NAME
2625
required: true
2726
- name: CPU_LIMIT
28-
value: 4000m
27+
value: 4000m

0 commit comments

Comments
 (0)