Skip to content

Commit a1bcf67

Browse files
mfranciscDevtoolsMatousJobanekfbm3307
authored
feat: create tiertemplaterevision for each tiertemplate (codeready-toolchain#1103)
* ensure revisions field in nstemplatetier --------- Co-authored-by: Devtools <devtools@redhat.com> Co-authored-by: Matous Jobanek <mjobanek@redhat.com> Co-authored-by: Feny Mehta <fbm3307@gmail.com>
1 parent 8d4a501 commit a1bcf67

File tree

6 files changed

+644
-44
lines changed

6 files changed

+644
-44
lines changed

controllers/nstemplatetier/nstemplatetier_controller.go

Lines changed: 166 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ package nstemplatetier
22

33
import (
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

Comments
 (0)