@@ -2,14 +2,17 @@ package nstemplatetier
22
33import (
44 "context"
5+ "fmt"
6+ "time"
57
68 toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
79 "github.com/codeready-toolchain/host-operator/controllers/toolchainconfig"
10+ "k8s.io/apimachinery/pkg/types"
811 "sigs.k8s.io/controller-runtime/pkg/builder"
912 "sigs.k8s.io/controller-runtime/pkg/log"
1013
11- errs "github.com/pkg/errors"
1214 "k8s.io/apimachinery/pkg/api/errors"
15+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1316 "k8s.io/apimachinery/pkg/runtime"
1417 ctrl "sigs.k8s.io/controller-runtime"
1518 runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -49,13 +52,173 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.
4952 }
5053 // Error reading the object - requeue the request.
5154 logger .Error (err , "unable to get the current NSTemplateTier" )
52- return reconcile.Result {}, errs . Wrap ( err , "unable to get the current NSTemplateTier" )
55+ return reconcile.Result {}, fmt . Errorf ( "unable to get the current NSTemplateTier: %w" , err )
5356 }
5457
5558 _ , err := toolchainconfig .GetToolchainConfig (r .Client )
5659 if err != nil {
57- return reconcile.Result {}, errs .Wrapf (err , "unable to get ToolchainConfig" )
60+ return reconcile.Result {}, fmt .Errorf ("unable to get ToolchainConfig: %w" , err )
61+ }
62+
63+ // check if the `status.revisions` field is up-to-date and create a TTR for each TierTemplate
64+ if created , err := r .ensureRevision (ctx , tier ); err != nil {
65+ // todo add/update ready condition false in the NSTemplateTier when something fails
66+ return reconcile.Result {}, fmt .Errorf ("unable to create new TierTemplateRevision after NSTemplateTier changed: %w" , err )
67+ } else if created {
68+ logger .Info ("Requeue after creating a new TTR" )
69+ return reconcile.Result {RequeueAfter : time .Second }, nil
5870 }
5971
6072 return reconcile.Result {}, nil
6173}
74+
75+ // ensureRevision ensures that there is a TierTemplateRevision CR for each of the TierTemplate.
76+ // returns `true` if a new TierTemplateRevision CR was created, `err` if something wrong happened
77+ func (r * Reconciler ) ensureRevision (ctx context.Context , nsTmplTier * toolchainv1alpha1.NSTemplateTier ) (bool , error ) {
78+ logger := log .FromContext (ctx )
79+ refs := getNSTemplateTierRefs (nsTmplTier )
80+
81+ // init revisions
82+ if nsTmplTier .Status .Revisions == nil {
83+ nsTmplTier .Status .Revisions = map [string ]string {}
84+ }
85+ // check for TierTemplates and TierTemplateRevisions associated with the NSTemplateTier
86+ ttrCreated := false
87+ for _ , tierTemplateRef := range refs {
88+ // get the TierTemplate
89+ var tierTemplate toolchainv1alpha1.TierTemplate
90+ if err := r .Client .Get (ctx , types.NamespacedName {Namespace : nsTmplTier .GetNamespace (), Name : tierTemplateRef }, & tierTemplate ); err != nil {
91+ // something went wrong or we haven't found the TierTemplate
92+ return false , err
93+ }
94+
95+ // check if there is TTR associated with this TierTemplate
96+ ttrCreatedLatest , ttrName , err := r .ensureTTRforTemplate (ctx , nsTmplTier , & tierTemplate )
97+ ttrCreated = ttrCreated || ttrCreatedLatest
98+ if err != nil {
99+ return false , err
100+ }
101+ nsTmplTier .Status .Revisions [tierTemplate .GetName ()] = ttrName
102+ }
103+ // TODO handle removal of TierTemplate from NSTemplateTier
104+ // scenario:
105+ // a. TierTemplate is removed/replaced from NSTemplateTier.Spec
106+ // b. NSTemplateTier.Status.Revisions must be cleaned up
107+ // Thus here we should iterate over the Status.Revisions field
108+ // and check if there is any reference to a TierTemplate that is not in the Spec anymore
109+ if ttrCreated {
110+ // we need to update the status.revisions with the new ttrs
111+ logger .Info ("ttr created updating status" )
112+ if err := r .Client .Status ().Update (ctx , nsTmplTier ); err != nil {
113+ return ttrCreated , err
114+ }
115+ }
116+
117+ return ttrCreated , nil
118+ }
119+
120+ func (r * Reconciler ) ensureTTRforTemplate (ctx context.Context , nsTmplTier * toolchainv1alpha1.NSTemplateTier , tierTemplate * toolchainv1alpha1.TierTemplate ) (bool , string , error ) {
121+ logger := log .FromContext (ctx )
122+ // tierTemplate doesn't support TTRs
123+ // we set TierTemplate as revisions
124+ // TODO this step will be removed once we convert all TierTemplates to TTRs
125+ if tierTemplate .Spec .TemplateObjects == nil {
126+ _ , ok := nsTmplTier .Status .Revisions [tierTemplate .GetName ()]
127+ if ! ok {
128+ return true , tierTemplate .GetName (), nil
129+ }
130+ // nothing to update
131+ return false , "" , nil
132+ }
133+
134+ if tierTemplateRevisionName , found := nsTmplTier .Status .Revisions [tierTemplate .GetName ()]; found {
135+ logger .Info ("TTR set in the status.revisions for tiertemplate" , "tierTemplate.Name" , tierTemplate .GetName (), "ttr.Name" , tierTemplateRevisionName )
136+ var tierTemplateRevision toolchainv1alpha1.TierTemplateRevision
137+ if err := r .Client .Get (ctx , types.NamespacedName {Namespace : nsTmplTier .GetNamespace (), Name : tierTemplateRevisionName }, & tierTemplateRevision ); err != nil {
138+ if errors .IsNotFound (err ) {
139+ // no tierTemplateRevision CR was found,
140+ logger .Info ("TTR CR not found" , "tierTemplateRevision.Name" , tierTemplateRevisionName )
141+ // let's create one
142+ ttrName , err := r .createNewTierTemplateRevision (ctx , nsTmplTier , tierTemplate )
143+ return true , ttrName , err
144+ } else {
145+ // something wrong happened
146+ return false , "" , err
147+ }
148+ }
149+ // TODO compare TierTemplate content with TTR content
150+ // if the TierTemplate has changes we need to create new TTR
151+ } else {
152+ // no revision was set for this TierTemplate CR, let's create a TTR for it
153+ ttrName , err := r .createNewTierTemplateRevision (ctx , nsTmplTier , tierTemplate )
154+ return true , ttrName , err
155+ }
156+ // nothing changed
157+ return false , "" , nil
158+ }
159+
160+ func (r * Reconciler ) createNewTierTemplateRevision (ctx context.Context , nsTmplTier * toolchainv1alpha1.NSTemplateTier , tierTemplate * toolchainv1alpha1.TierTemplate ) (string , error ) {
161+ ttr := NewTTR (tierTemplate , nsTmplTier )
162+ ttr , err := r .createTTR (ctx , ttr , tierTemplate )
163+ if err != nil {
164+ // something went wrong while creating new ttr
165+ return "" , err
166+ }
167+ return ttr .GetName (), nil
168+ }
169+
170+ // getNSTemplateTierRefs returns a list with all the refs from the NSTemplateTier
171+ func getNSTemplateTierRefs (tmplTier * toolchainv1alpha1.NSTemplateTier ) []string {
172+ var refs []string
173+ for _ , ns := range tmplTier .Spec .Namespaces {
174+ refs = append (refs , ns .TemplateRef )
175+ }
176+ if tmplTier .Spec .ClusterResources != nil {
177+ refs = append (refs , tmplTier .Spec .ClusterResources .TemplateRef )
178+ }
179+
180+ roles := make ([]string , 0 , len (tmplTier .Spec .SpaceRoles ))
181+ for r := range tmplTier .Spec .SpaceRoles {
182+ roles = append (roles , r )
183+ }
184+ for _ , r := range roles {
185+ refs = append (refs , tmplTier .Spec .SpaceRoles [r ].TemplateRef )
186+ }
187+ return refs
188+ }
189+
190+ func (r * Reconciler ) createTTR (ctx context.Context , ttr * toolchainv1alpha1.TierTemplateRevision , tmplTier * toolchainv1alpha1.TierTemplate ) (* toolchainv1alpha1.TierTemplateRevision , error ) {
191+ err := r .Client .Create (ctx , ttr )
192+ if err != nil {
193+ return nil , fmt .Errorf ("unable to create TierTemplateRevision: %w" , err )
194+ }
195+
196+ logger := log .FromContext (ctx )
197+ logger .Info ("created TierTemplateRevision" , "tierTemplateRevision.Name" , ttr .Name , "tierTemplate.Name" , tmplTier .Name )
198+ return ttr , nil
199+ }
200+
201+ // NewTTR creates a TierTemplateRevision CR for a given TierTemplate object.
202+ func NewTTR (tierTmpl * toolchainv1alpha1.TierTemplate , nsTmplTier * toolchainv1alpha1.NSTemplateTier ) * toolchainv1alpha1.TierTemplateRevision {
203+ tierName := nsTmplTier .GetName ()
204+ tierTemplateName := tierTmpl .GetName ()
205+ labels := map [string ]string {
206+ toolchainv1alpha1 .TierLabelKey : tierName ,
207+ toolchainv1alpha1 .TemplateRefLabelKey : tierTemplateName ,
208+ }
209+
210+ newTTRName := fmt .Sprintf ("%s-%s-" , tierName , tierTemplateName )
211+ ttr := & toolchainv1alpha1.TierTemplateRevision {
212+ ObjectMeta : metav1.ObjectMeta {
213+ Namespace : tierTmpl .GetNamespace (),
214+ GenerateName : newTTRName + "-" ,
215+ Labels : labels ,
216+ },
217+ Spec : toolchainv1alpha1.TierTemplateRevisionSpec {
218+ TemplateObjects : tierTmpl .Spec .TemplateObjects ,
219+ Parameters : nsTmplTier .Spec .Parameters , // save the parameters from the NSTemplateTier,to detect further changes and for evaluating those in the TemplateObjects when provisioning Spaces.
220+ },
221+ }
222+
223+ return ttr
224+ }
0 commit comments