@@ -13,6 +13,7 @@ import (
1313 "k8s.io/apimachinery/pkg/runtime"
1414
1515 "github.com/gofrs/uuid"
16+ "k8s.io/client-go/kubernetes/scheme"
1617
1718 toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
1819 "github.com/codeready-toolchain/toolchain-common/pkg/states"
@@ -21,8 +22,6 @@ import (
2122 . "github.com/codeready-toolchain/toolchain-e2e/testsupport/space"
2223 "github.com/codeready-toolchain/toolchain-e2e/testsupport/tiers"
2324 "github.com/codeready-toolchain/toolchain-e2e/testsupport/wait"
24- apiwait "k8s.io/apimachinery/pkg/util/wait"
25-
2625 "github.com/stretchr/testify/assert"
2726 "github.com/stretchr/testify/require"
2827 "k8s.io/apimachinery/pkg/labels"
@@ -378,76 +377,125 @@ func TestTierTemplateRevision(t *testing.T) {
378377 require .NoError (t , err )
379378 // for the tiertemplaterevisions to be created the tiertemplates need to have template objects populated
380379 // we add the RawExtension objects to the TemplateObjects field
380+ crq := getTestCRQ ("600" )
381+ rawTemplateObjects := []runtime.RawExtension {{Object : & crq }}
382+ updateTierTemplateObjects := func (template * toolchainv1alpha1.TierTemplate ) error {
383+ template .Spec .TemplateObjects = rawTemplateObjects
384+ return nil
385+ }
386+ // for simplicity, we add the CRQ to all types of templates (both cluster scope and namespace scoped),
387+ // even if the CRQ is cluster scoped.
388+ // WARNING: thus THIS NSTemplateTier should NOT be used to provision a user!!!
389+ customTier := tiers .CreateCustomNSTemplateTier (t , hostAwait , "ttr" , baseTier ,
390+ tiers .WithNamespaceResources (t , baseTier , updateTierTemplateObjects ),
391+ tiers .WithClusterResources (t , baseTier , updateTierTemplateObjects ),
392+ tiers .WithSpaceRoles (t , baseTier , updateTierTemplateObjects ),
393+ tiers .WithParameter ("DEPLOYMENT_QUOTA" , "60" ))
394+ // when
395+ // we verify that TierTemplateRevision CRs were created, since all the tiertemplates now have templateObjects field populated
396+ tier , err := hostAwait .WaitForNSTemplateTierAndCheckTemplates (t , "ttr" ,
397+ wait .HasStatusTierTemplateRevisions (tiers .GetTemplateRefs (t , hostAwait , "ttr" ).Flatten ()))
398+ require .NoError (t , err )
399+ customTier .NSTemplateTier = tier
400+
401+ // then
402+ // check the expected total number of ttr matches,
403+ // we IDEALLY expect one TTR per each tiertemplate to be created (clusterresource, namespace and spacerole), thus a total of 3 TTRs ideally.
404+ // But since the creation of a TTR could be very quick and could trigger another reconcile of the NSTemplateTier before the status is actually updated with the reference,
405+ // this might generate some copies of the TTRs. This is not a problem in production since the cleanup mechanism of TTRs will remove the extra ones but could cause some flakiness with the test,
406+ // thus we assert the number of TTRs doesn't exceed the double of the expected number.
407+ // TODO check for exact match or remove the *2 and check for not empty revisions list, once we implement the cleanup controller
408+ ttrs , err := hostAwait .WaitForTTRs (t , customTier .Name , wait .LessOrEqual (len (tiers .GetTemplateRefs (t , hostAwait , "ttr" ).Flatten ())* 2 ))
409+ require .NoError (t , err )
410+
411+ t .Run ("update of tiertemplate should trigger creation of new TTR" , func (t * testing.T ) {
412+ // given
413+ // that the tiertemplates and nstemlpatetier are provisioned from the parent test
414+ ttrToBeModified , found := customTier .Status .Revisions [customTier .Spec .ClusterResources .TemplateRef ]
415+ require .True (t , found )
416+ // check that it has the crq before updating it
417+ checkThatTTRContainsCRQ (t , ttrToBeModified , ttrs , crq )
418+
419+ // when
420+ // we update one tiertemplate
421+ // let's reduce the pod count
422+ updatedCRQ := getTestCRQ ("100" )
423+ _ , err = wait .For (t , hostAwait .Awaitility , & toolchainv1alpha1.TierTemplate {}).
424+ Update (customTier .Spec .ClusterResources .TemplateRef , hostAwait .Namespace , func (tiertemplate * toolchainv1alpha1.TierTemplate ) {
425+ tiertemplate .Spec .TemplateObjects = []runtime.RawExtension {{Object : & updatedCRQ }}
426+ })
427+ require .NoError (t , err )
428+
429+ // then
430+ // a new TTR was created
431+ // TODO check for exact match or remove the +1 once we implement the cleanup controller
432+ updatedTTRs , err := hostAwait .WaitForTTRs (t , customTier .Name , wait .GreaterOrEqual (len (ttrs )+ 1 ))
433+ require .NoError (t , err )
434+ // get the updated nstemplatetier
435+ updatedCustomTier , err := hostAwait .WaitForNSTemplateTier (t , customTier .Name )
436+ newTTR , found := updatedCustomTier .Status .Revisions [updatedCustomTier .Spec .ClusterResources .TemplateRef ]
437+ require .True (t , found )
438+ // check that it has the updated crq
439+ checkThatTTRContainsCRQ (t , newTTR , updatedTTRs , updatedCRQ )
440+
441+ t .Run ("update of the NSTemplateTier parameters should trigger creation of new TTR" , func (t * testing.T ) {
442+ // given
443+ // that the TierTemplates and NSTemplateTier are provisioned from the parent test
444+ // and they have the initial parameter value for deployment quota
445+ checkThatTTRsHaveParameter (t , customTier , updatedTTRs , toolchainv1alpha1.Parameter {
446+ Name : "DEPLOYMENT_QUOTA" ,
447+ Value : "60" ,
448+ })
449+
450+ // when
451+ // we increase the parameter for the deployment quota
452+ customTier = tiers .UpdateCustomNSTemplateTier (t , hostAwait , customTier , tiers .WithParameter ("DEPLOYMENT_QUOTA" , "100" ))
453+ require .NoError (t , err )
454+
455+ // then
456+ // an additional TTR will be created
457+ // TODO check for exact match or remove the +1 once we implement the cleanup controller
458+ ttrsWithNewParams , err := hostAwait .WaitForTTRs (t , customTier .Name , wait .GreaterOrEqual (len (updatedTTRs )+ 1 ))
459+ require .NoError (t , err )
460+ // retrieve new tier once the ttrs were created and the revision field updated
461+ customTier .NSTemplateTier , err = hostAwait .WaitForNSTemplateTier (t , customTier .Name )
462+ require .NoError (t , err )
463+ // and the parameter is updated in all the ttrs
464+ checkThatTTRsHaveParameter (t , customTier , ttrsWithNewParams , toolchainv1alpha1.Parameter {
465+ Name : "DEPLOYMENT_QUOTA" ,
466+ Value : "100" ,
467+ })
468+ })
469+
470+ })
471+
472+ }
473+
474+ func getTestCRQ (podsCount string ) unstructured.Unstructured {
381475 crq := unstructured.Unstructured {Object : map [string ]interface {}{
382476 "kind" : "ClusterResourceQuota" ,
383477 "metadata" : map [string ]interface {}{
384478 "name" : "for-{{.SPACE_NAME}}-deployments" ,
385479 },
386480 "spec" : map [string ]interface {}{
387481 "quota" : map [string ]interface {}{
388- "hard" : map [string ]string {
482+ "hard" : map [string ]interface {} {
389483 "count/deploymentconfigs.apps" : "{{.DEPLOYMENT_QUOTA}}" ,
390484 "count/deployments.apps" : "{{.DEPLOYMENT_QUOTA}}" ,
391- "count/pods" : "600" ,
485+ "count/pods" : podsCount ,
392486 },
393487 },
394488 "selector" : map [string ]interface {}{
395- "annotations" : map [string ]string {},
489+ "annotations" : map [string ]interface {} {},
396490 "labels" : map [string ]interface {}{
397- "matchLabels" : map [string ]string {
491+ "matchLabels" : map [string ]interface {} {
398492 "toolchain.dev.openshift.com/space" : "{{.SPACE_NAME}}" ,
399493 },
400494 },
401495 },
402496 },
403497 }}
404- rawTemplateObjects := []runtime.RawExtension {{Object : & crq }}
405- updateTierTemplateObjects := func (template * toolchainv1alpha1.TierTemplate ) error {
406- template .Spec .TemplateObjects = rawTemplateObjects
407- return nil
408- }
409- // for simplicity, we add the CRQ to all types of templates (both cluster scope and namespace scoped),
410- // even if the CRQ is cluster scoped.
411- // WARNING: thus THIS NSTemplateTier should NOT be sued to provision a user!!!
412- tiers .CreateCustomNSTemplateTier (t , hostAwait , "ttr" , baseTier ,
413- tiers .WithNamespaceResources (t , baseTier , updateTierTemplateObjects ),
414- tiers .WithClusterResources (t , baseTier , updateTierTemplateObjects ),
415- tiers .WithSpaceRoles (t , baseTier , updateTierTemplateObjects ),
416- tiers .WithParameter ("DEPLOYMENT_QUOTA" , "60" ))
417- // when
418- // we verify the counters in the status.history for 'tierUsingTierTemplateRevisions' tier
419- // and verify that TierTemplateRevision CRs were created, since all the tiertemplates now have templateObjects field populated
420- customTier , err := hostAwait .WaitForNSTemplateTierAndCheckTemplates (t , "ttr" ,
421- wait .HasStatusTierTemplateRevisions (tiers .GetTemplateRefs (t , hostAwait , "ttr" ).Flatten ()))
422- require .NoError (t , err )
423-
424- // then
425- // check the expected total number of ttr matches
426- err = apiwait .PollUntilContextTimeout (context .TODO (), hostAwait .RetryInterval , hostAwait .Timeout , true , func (ctx context.Context ) (done bool , err error ) {
427- objs := & toolchainv1alpha1.TierTemplateRevisionList {}
428- if err := hostAwait .Client .List (ctx , objs , client .InNamespace (hostAwait .Namespace )); err != nil {
429- return false , err
430- }
431- // we IDEALLY expect one TTR per each tiertemplate to be created (clusterresource, namespace and spacerole), thus a total of 3 TTRs ideally.
432- // But since the creation of a TTR could be very quick and could trigger another reconcile of the NSTemplateTier before the status is actually updated with the reference,
433- // this might generate some copies of the TTRs. This is not a problem in production since the cleanup mechanism of TTRs will remove the extra ones but could cause some flakiness with the test,
434- // thus we assert the number of TTRs doesn't exceed the double of the expected number.
435- assert .LessOrEqual (t , len (objs .Items ), len (tiers .GetTemplateRefs (t , hostAwait , "ttr" ).Flatten ())* 2 )
436- // we check that the TTR content has the parameters replaced with values from the NSTemplateTier
437- for _ , obj := range objs .Items {
438- // the object should have all the variables still there since this one will be replaced when provisioning the Space
439- assert .Contains (t , string (obj .Spec .TemplateObjects [0 ].Raw ), ".SPACE_NAME" )
440- assert .Contains (t , string (obj .Spec .TemplateObjects [0 ].Raw ), ".DEPLOYMENT_QUOTA" )
441- // the parameter is copied from the NSTemplateTier
442- assert .NotNil (t , obj .Spec .Parameters )
443- assert .NotNil (t , customTier .Spec .Parameters )
444- // we only expect the static parameter DEPLOYMENT_QUOTA to be copied from the tier to the TTR.
445- // the SPACE_NAME is not a parameter, but a dynamic variable which will be evaluated when provisioning a namespace for the user.
446- assert .ElementsMatch (t , customTier .Spec .Parameters , obj .Spec .Parameters )
447- }
448- return true , nil
449- })
450- require .NoError (t , err )
498+ return crq
451499}
452500
453501func withClusterRoleBindings (t * testing.T , otherTier * toolchainv1alpha1.NSTemplateTier , feature string ) tiers.CustomNSTemplateTierModifier {
@@ -498,3 +546,29 @@ const (
498546}
499547`
500548)
549+
550+ // checkThatTTRContainsCRQ verifies if a given ttr from the list contains the CRQ in the templateObjects field
551+ func checkThatTTRContainsCRQ (t * testing.T , ttrName string , ttrs []toolchainv1alpha1.TierTemplateRevision , crq unstructured.Unstructured ) {
552+ for _ , ttr := range ttrs {
553+ if ttr .Name == ttrName {
554+ assert .NotEmpty (t , ttr .Spec .TemplateObjects )
555+ unstructuredObj := & unstructured.Unstructured {}
556+ _ , _ , err := scheme .Codecs .UniversalDeserializer ().Decode (ttr .Spec .TemplateObjects [0 ].Raw , nil , unstructuredObj )
557+ require .NoError (t , err )
558+ assert .Equal (t , & crq , unstructuredObj )
559+ return
560+ }
561+ }
562+ require .FailNowf (t , "Unable to find a TTR with required crq" , "ttr:%s CRQ:%+v" , ttrName , crq )
563+ }
564+
565+ // checkThatTTRsHaveParameter verifies that ttrs from the list have the required parameter
566+ func checkThatTTRsHaveParameter (t * testing.T , tier * tiers.CustomNSTemplateTier , ttrs []toolchainv1alpha1.TierTemplateRevision , parameters toolchainv1alpha1.Parameter ) {
567+ for _ , ttr := range ttrs {
568+ // if the ttr is still in the revisions field we check that it contains the required parameters
569+ if ttrNameInRev , ttrFound := tier .Status .Revisions [ttr .GetLabels ()[toolchainv1alpha1 .TemplateRefLabelKey ]]; ttrFound && ttrNameInRev == ttr .Name {
570+ assert .Contains (t , ttr .Spec .Parameters , parameters , "Unable to find required parameters:%+v in the TTR:%s" , parameters , ttr .Name )
571+ return
572+ }
573+ }
574+ }
0 commit comments