Skip to content

Commit 1b14368

Browse files
committed
rework servicetemplate controller
Signed-off-by: Artem Bortnikov <[email protected]>
1 parent f2d04c7 commit 1b14368

File tree

2 files changed

+94
-113
lines changed

2 files changed

+94
-113
lines changed

api/v1alpha1/servicetemplate_types.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
helmcontrollerv2 "github.com/fluxcd/helm-controller/api/v2"
2222
sourcev1 "github.com/fluxcd/source-controller/api/v1"
2323
sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
24+
corev1 "k8s.io/api/core/v1"
2425
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2526
"sigs.k8s.io/controller-runtime/pkg/client"
2627
)
@@ -30,6 +31,9 @@ const (
3031
ServiceTemplateKind = "ServiceTemplate"
3132
// ChartAnnotationKubernetesConstraint is an annotation containing the Kubernetes constrained version in the SemVer format associated with a ServiceTemplate.
3233
ChartAnnotationKubernetesConstraint = "k0rdent.mirantis.com/k8s-version-constraint"
34+
35+
SecretKind = "Secret"
36+
ConfigMapKind = "ConfigMap"
3337
)
3438

3539
// +kubebuilder:validation:XValidation:rule="has(self.helm) ? (!has(self.kustomize) && !has(self.resources)): true",message="Helm, Kustomize and Resources are mutually exclusive."
@@ -263,6 +267,44 @@ func (t *ServiceTemplate) RemoteSourceSpec() *RemoteSourceSpec {
263267
}
264268
}
265269

270+
// LocalSourceObject returns the client.Object and kind of the defined local source.
271+
// If the ServiceTemplate does not reference a local source, it returns nil.
272+
func (t *ServiceTemplate) LocalSourceObject() (client.Object, string) {
273+
localSourceRef := t.LocalSourceRef()
274+
if localSourceRef == nil {
275+
return nil, ""
276+
}
277+
localSourceMeta := metav1.ObjectMeta{
278+
Name: localSourceRef.Name,
279+
Namespace: t.Namespace,
280+
}
281+
282+
switch localSourceRef.Kind {
283+
case SecretKind:
284+
return &corev1.Secret{
285+
ObjectMeta: localSourceMeta,
286+
}, SecretKind
287+
case ConfigMapKind:
288+
return &corev1.ConfigMap{
289+
ObjectMeta: localSourceMeta,
290+
}, ConfigMapKind
291+
case sourcev1.GitRepositoryKind:
292+
return &sourcev1.GitRepository{
293+
ObjectMeta: localSourceMeta,
294+
}, sourcev1.GitRepositoryKind
295+
case sourcev1.BucketKind:
296+
return &sourcev1.Bucket{
297+
ObjectMeta: localSourceMeta,
298+
}, sourcev1.BucketKind
299+
case sourcev1beta2.OCIRepositoryKind:
300+
return &sourcev1beta2.OCIRepository{
301+
ObjectMeta: localSourceMeta,
302+
}, sourcev1beta2.OCIRepositoryKind
303+
default:
304+
return nil, ""
305+
}
306+
}
307+
266308
// RemoteSourceObject returns the client.Object and kind of the defined remote source.
267309
// If the ServiceTemplate does not define a remote source, returns nil and empty string.
268310
func (t *ServiceTemplate) RemoteSourceObject() (client.Object, string) {
@@ -295,7 +337,7 @@ func (t *ServiceTemplate) RemoteSourceObject() (client.Object, string) {
295337
ObjectMeta: fluxSourceMeta,
296338
Spec: remoteSourceSpec.OCI.OCIRepositorySpec,
297339
}, sourcev1beta2.OCIRepositoryKind
340+
default:
341+
return nil, ""
298342
}
299-
300-
return nil, ""
301343
}

internal/controller/servicetemplate_controller.go

Lines changed: 50 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323

2424
sourcev1 "github.com/fluxcd/source-controller/api/v1"
2525
sourcev1beta2 "github.com/fluxcd/source-controller/api/v1beta2"
26-
corev1 "k8s.io/api/core/v1"
2726
apierrors "k8s.io/apimachinery/pkg/api/errors"
2827
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2928
ctrl "sigs.k8s.io/controller-runtime"
@@ -111,8 +110,6 @@ func (r *ServiceTemplateReconciler) reconcileLocalSource(ctx context.Context, te
111110
return errors.New("local source ref is undefined")
112111
}
113112

114-
key := client.ObjectKey{Namespace: template.Namespace, Name: ref.Name}
115-
116113
status := kcm.ServiceTemplateStatus{
117114
TemplateStatusCommon: kcm.TemplateStatusCommon{
118115
TemplateValidationStatus: kcm.TemplateValidationStatus{},
@@ -140,68 +137,20 @@ func (r *ServiceTemplateReconciler) reconcileLocalSource(ctx context.Context, te
140137
template.Status = status
141138
}()
142139

143-
switch ref.Kind {
144-
case "Secret":
145-
secret := &corev1.Secret{}
146-
err = r.Get(ctx, key, secret)
147-
if err != nil {
148-
return fmt.Errorf("failed to get referred Secret %s: %w", key, err)
149-
}
150-
status.SourceStatus, err = r.sourceStatusFromLocalObject(secret)
151-
if err != nil {
152-
return fmt.Errorf("failed to get source status from Secret %s: %w", key, err)
153-
}
154-
case "ConfigMap":
155-
configMap := &corev1.ConfigMap{}
156-
err = r.Get(ctx, key, configMap)
157-
if err != nil {
158-
return fmt.Errorf("failed to get referred ConfigMap %s: %w", key, err)
159-
}
160-
status.SourceStatus, err = r.sourceStatusFromLocalObject(configMap)
161-
if err != nil {
162-
return fmt.Errorf("failed to get source status from ConfigMap %s: %w", key, err)
163-
}
164-
case sourcev1.GitRepositoryKind:
165-
gitRepository := &sourcev1.GitRepository{}
166-
err = r.Get(ctx, key, gitRepository)
167-
if err != nil {
168-
return fmt.Errorf("failed to get referred GitRepository %s: %w", key, err)
169-
}
170-
status.SourceStatus, err = r.sourceStatusFromFluxObject(gitRepository)
171-
if err != nil {
172-
return fmt.Errorf("failed to get source status from GitRepository %s: %w", key, err)
173-
}
174-
conditions := make([]metav1.Condition, len(gitRepository.Status.Conditions))
175-
copy(conditions, gitRepository.Status.Conditions)
176-
status.SourceStatus.Conditions = conditions
177-
case sourcev1.BucketKind:
178-
bucket := &sourcev1.Bucket{}
179-
err = r.Get(ctx, key, bucket)
180-
if err != nil {
181-
return fmt.Errorf("failed to get referred Bucket %s: %w", key, err)
182-
}
183-
status.SourceStatus, err = r.sourceStatusFromFluxObject(bucket)
184-
if err != nil {
185-
return fmt.Errorf("failed to get source status from Bucket %s: %w", key, err)
186-
}
187-
conditions := make([]metav1.Condition, len(bucket.Status.Conditions))
188-
copy(conditions, bucket.Status.Conditions)
189-
status.SourceStatus.Conditions = conditions
190-
case sourcev1beta2.OCIRepositoryKind:
191-
ociRepository := &sourcev1beta2.OCIRepository{}
192-
err = r.Get(ctx, key, ociRepository)
193-
if err != nil {
194-
return fmt.Errorf("failed to get referred OCIRepository %s: %w", key, err)
195-
}
196-
status.SourceStatus, err = r.sourceStatusFromFluxObject(ociRepository)
197-
if err != nil {
198-
return fmt.Errorf("failed to get source status from OCIRepository %s: %w", key, err)
140+
localSource, kind := template.LocalSourceObject()
141+
key := client.ObjectKeyFromObject(localSource)
142+
if err = r.Get(ctx, key, localSource); err != nil {
143+
return fmt.Errorf("failed to get referred %s %s: %w", kind, key, err)
144+
}
145+
146+
if status.SourceStatus, err = r.sourceStatusFromObject(localSource); err != nil {
147+
return fmt.Errorf("failed to get common source status from %s %s: %w", kind, key, err)
148+
}
149+
switch kind {
150+
case sourcev1.GitRepositoryKind, sourcev1.BucketKind, sourcev1beta2.OCIRepositoryKind:
151+
if err = r.sourceStatusFromFluxObject(localSource, status.SourceStatus); err != nil {
152+
return fmt.Errorf("failed to get source status from %s %s: %w", kind, key, err)
199153
}
200-
conditions := make([]metav1.Condition, len(ociRepository.Status.Conditions))
201-
copy(conditions, ociRepository.Status.Conditions)
202-
status.SourceStatus.Conditions = conditions
203-
default:
204-
return fmt.Errorf("unsupported source kind %s", ref.Kind)
205154
}
206155
return err
207156
}
@@ -219,46 +168,23 @@ func (r *ServiceTemplateReconciler) reconcileRemoteSource(ctx context.Context, t
219168
return controllerutil.SetControllerReference(template, remoteSourceObject, r.Scheme())
220169
})
221170
if err != nil {
222-
return fmt.Errorf("failed to reconcile OCIRepository object: %w", err)
171+
return fmt.Errorf("failed to reconcile remote source object: %w", err)
223172
}
224173

225174
if op == controllerutil.OperationResultCreated || op == controllerutil.OperationResultUpdated {
226175
l.Info("Successfully mutated remote source object", "kind", kind, "namespaced_name", client.ObjectKeyFromObject(remoteSourceObject), "operation_result", op)
227176
}
228177
if op == controllerutil.OperationResultNone {
229178
l.Info("Remote source object is up-to-date", "kind", kind, "namespaced_name", client.ObjectKeyFromObject(remoteSourceObject))
230-
var (
231-
sourceStatus *kcm.SourceStatus
232-
conditions []metav1.Condition
233-
)
234-
switch source := remoteSourceObject.(type) {
235-
case *sourcev1.GitRepository:
236-
sourceStatus, err = r.sourceStatusFromFluxObject(source)
237-
if err != nil {
238-
return fmt.Errorf("failed to get source status from GitRepository: %w", err)
239-
}
240-
conditions = make([]metav1.Condition, len(source.Status.Conditions))
241-
copy(conditions, source.Status.Conditions)
242-
case *sourcev1.Bucket:
243-
sourceStatus, err = r.sourceStatusFromFluxObject(source)
244-
if err != nil {
245-
return fmt.Errorf("failed to get source status from Bucket: %w", err)
246-
}
247-
conditions = make([]metav1.Condition, len(source.Status.Conditions))
248-
copy(conditions, source.Status.Conditions)
249-
case *sourcev1beta2.OCIRepository:
250-
sourceStatus, err = r.sourceStatusFromFluxObject(source)
251-
if err != nil {
252-
return fmt.Errorf("failed to get source status from OCIRepository: %w", err)
253-
}
254-
conditions = make([]metav1.Condition, len(source.Status.Conditions))
255-
copy(conditions, source.Status.Conditions)
256-
default:
257-
return fmt.Errorf("unsupported remote source type: %T", source)
179+
var sourceStatus *kcm.SourceStatus
180+
if sourceStatus, err = r.sourceStatusFromObject(remoteSourceObject); err != nil {
181+
return fmt.Errorf("failed to get common source status from %s %s: %w", kind, client.ObjectKeyFromObject(remoteSourceObject), err)
182+
}
183+
if err = r.sourceStatusFromFluxObject(remoteSourceObject, sourceStatus); err != nil {
184+
return fmt.Errorf("failed to get source status from %s %s: %w", kind, client.ObjectKeyFromObject(remoteSourceObject), err)
258185
}
259186
template.Status.SourceStatus = sourceStatus
260-
template.Status.SourceStatus.Conditions = conditions
261-
template.Status.Valid = slices.ContainsFunc(conditions, func(c metav1.Condition) bool {
187+
template.Status.Valid = slices.ContainsFunc(sourceStatus.Conditions, func(c metav1.Condition) bool {
262188
return c.Type == kcm.ReadyCondition && c.Status == metav1.ConditionTrue
263189
})
264190
if template.Status.Valid {
@@ -285,7 +211,9 @@ func (r *ServiceTemplateReconciler) SetupWithManager(mgr ctrl.Manager) error {
285211
Complete(r)
286212
}
287213

288-
func (r *ServiceTemplateReconciler) sourceStatusFromLocalObject(obj client.Object) (*kcm.SourceStatus, error) {
214+
// sourceStatusFromObject extracts the common fields from local or remote source defined for
215+
// v1alpha1.ServiceTemplate and returns v1alpha1.SourceStatus and an error.
216+
func (r *ServiceTemplateReconciler) sourceStatusFromObject(obj client.Object) (*kcm.SourceStatus, error) {
289217
gvk, err := apiutil.GVKForObject(obj, r.Scheme())
290218
if err != nil {
291219
return nil, err
@@ -298,20 +226,31 @@ func (r *ServiceTemplateReconciler) sourceStatusFromLocalObject(obj client.Objec
298226
}, nil
299227
}
300228

301-
func (r *ServiceTemplateReconciler) sourceStatusFromFluxObject(obj interface {
302-
client.Object
303-
sourcev1.Source
304-
},
305-
) (*kcm.SourceStatus, error) {
306-
gvk, err := apiutil.GVKForObject(obj, r.Scheme())
307-
if err != nil {
308-
return nil, err
229+
// sourceStatusFromFluxObject extracts the artifact and conditions info from flux source
230+
// defined for v1alpha1.ServiceTemplate and mutates provided v1alpha1.SourceStatus. Returns
231+
// an error if the passed object is not a flux source object.
232+
func (*ServiceTemplateReconciler) sourceStatusFromFluxObject(obj client.Object, status *kcm.SourceStatus) error {
233+
var (
234+
artifact *sourcev1.Artifact
235+
conditions []metav1.Condition
236+
)
237+
switch source := obj.(type) {
238+
case *sourcev1.GitRepository:
239+
artifact = source.GetArtifact()
240+
conditions = make([]metav1.Condition, len(source.Status.Conditions))
241+
copy(conditions, source.Status.Conditions)
242+
case *sourcev1.Bucket:
243+
artifact = source.GetArtifact()
244+
conditions = make([]metav1.Condition, len(source.Status.Conditions))
245+
copy(conditions, source.Status.Conditions)
246+
case *sourcev1beta2.OCIRepository:
247+
artifact = source.GetArtifact()
248+
conditions = make([]metav1.Condition, len(source.Status.Conditions))
249+
copy(conditions, source.Status.Conditions)
250+
default:
251+
return fmt.Errorf("unsupported source type: %T", source)
309252
}
310-
return &kcm.SourceStatus{
311-
Kind: gvk.Kind,
312-
Name: obj.GetName(),
313-
Namespace: obj.GetNamespace(),
314-
ObservedGeneration: obj.GetGeneration(),
315-
Artifact: obj.GetArtifact(),
316-
}, nil
253+
status.Artifact = artifact
254+
status.Conditions = conditions
255+
return nil
317256
}

0 commit comments

Comments
 (0)