Skip to content
This repository was archived by the owner on May 9, 2025. It is now read-only.

Commit 7a9bcfa

Browse files
authored
feat: apply kstatus feature from ocm-controller (#80)
1 parent c8c050b commit 7a9bcfa

File tree

6 files changed

+199
-160
lines changed

6 files changed

+199
-160
lines changed

apis/delivery/v1alpha1/sync_types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package v1alpha1
66

77
import (
8+
"fmt"
89
"time"
910

1011
"github.com/fluxcd/pkg/apis/meta"
@@ -63,6 +64,18 @@ type SyncStatus struct {
6364
PullRequestID int `json:"pullRequestID,omitempty"`
6465
}
6566

67+
func (in *Sync) GetVID() map[string]string {
68+
vid := fmt.Sprintf("%d:%s", in.Status.PullRequestID, in.Status.Digest)
69+
metadata := make(map[string]string)
70+
metadata[GroupVersion.Group+"/sync"] = vid
71+
72+
return metadata
73+
}
74+
75+
func (in *Sync) SetObservedGeneration(v int64) {
76+
in.Status.ObservedGeneration = v
77+
}
78+
6679
// GetConditions returns the conditions of the ComponentVersion.
6780
func (in *Sync) GetConditions() []metav1.Condition {
6881
return in.Status.Conditions

apis/mpas/v1alpha1/repository_types.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ func (in Repository) GetRepositoryURL() string {
129129
return fmt.Sprintf("https://%s/%s/%s", domain, in.Spec.Owner, in.GetName())
130130
}
131131

132+
func (in *Repository) GetVID() map[string]string {
133+
metadata := make(map[string]string)
134+
metadata[GroupVersion.Group+"/repository"] = fmt.Sprintf("%s/%s", in.Spec.Provider, in.Name)
135+
136+
return metadata
137+
}
138+
139+
func (in *Repository) SetObservedGeneration(v int64) {
140+
in.Status.ObservedGeneration = v
141+
}
142+
132143
//+kubebuilder:object:root=true
133144
//+kubebuilder:subresource:status
134145

controllers/delivery/sync_controller.go

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"github.com/fluxcd/pkg/apis/meta"
1515
"github.com/fluxcd/pkg/runtime/conditions"
1616
"github.com/fluxcd/pkg/runtime/patch"
17+
rreconcile "github.com/fluxcd/pkg/runtime/reconcile"
18+
"github.com/open-component-model/ocm-controller/pkg/status"
1719
corev1 "k8s.io/api/core/v1"
1820
apierrors "k8s.io/apimachinery/pkg/api/errors"
1921
"k8s.io/apimachinery/pkg/runtime"
@@ -22,7 +24,6 @@ import (
2224
ctrl "sigs.k8s.io/controller-runtime"
2325
"sigs.k8s.io/controller-runtime/pkg/builder"
2426
"sigs.k8s.io/controller-runtime/pkg/client"
25-
"sigs.k8s.io/controller-runtime/pkg/log"
2627
"sigs.k8s.io/controller-runtime/pkg/predicate"
2728

2829
ocmv1 "github.com/open-component-model/ocm-controller/api/v1alpha1"
@@ -56,8 +57,6 @@ type SyncReconciler struct {
5657
// Reconcile is part of the main kubernetes reconciliation loop which aims to
5758
// move the current state of the cluster closer to the desired state.
5859
func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, err error) {
59-
log := log.FromContext(ctx)
60-
6160
obj := &v1alpha1.Sync{}
6261
if err = r.Get(ctx, req.NamespacedName, obj); err != nil {
6362
if apierrors.IsNotFound(err) {
@@ -66,7 +65,6 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
6665

6766
return ctrl.Result{}, fmt.Errorf("failed to get git sync object: %w", err)
6867
}
69-
log.V(4).Info("found reconciling object", "sync", obj)
7068

7169
// The replication controller doesn't need a shouldReconcile, because it should always reconcile,
7270
// that is its purpose.
@@ -79,39 +77,46 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
7977
return
8078
}
8179

82-
// Set status observed generation option if the component is stalled or ready.
83-
if conditions.IsReady(obj) {
84-
obj.Status.ObservedGeneration = obj.Generation
85-
}
86-
87-
// Update the object.
88-
if perr := patchHelper.Patch(ctx, obj); perr != nil {
89-
err = errors.Join(err, perr)
80+
if derr := status.UpdateStatus(ctx, patchHelper, obj, r.EventRecorder, obj.GetRequeueAfter()); derr != nil {
81+
err = errors.Join(err, derr)
9082
}
9183
}()
9284

85+
// Starts the progression by setting ReconcilingCondition.
86+
// This will be checked in defer.
87+
// Should only be deleted on a success.
88+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconciliation in progress for resource: %s", obj.Name)
89+
9390
// it's important that this happens here so any residual status condition can be overwritten / set.
9491
if obj.Status.Digest != "" {
95-
log.Info("Sync object already synced; status contains digest information", "digest", obj.Status.Digest)
9692
event.New(r.EventRecorder, obj, eventv1.EventSeverityInfo, fmt.Sprintf("sync object already synced with digest %s", obj.Status.Digest), nil)
9793
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "Reconciliation success")
9894

9995
return ctrl.Result{}, nil
10096
}
10197

98+
if obj.Generation != obj.Status.ObservedGeneration {
99+
rreconcile.ProgressiveStatus(
100+
false,
101+
obj,
102+
meta.ProgressingReason,
103+
"processing object: new generation %d -> %d",
104+
obj.Status.ObservedGeneration,
105+
obj.Generation,
106+
)
107+
}
108+
102109
snapshot := &ocmv1.Snapshot{}
103110
if err = r.Get(ctx, types.NamespacedName{
104111
Namespace: obj.Namespace,
105112
Name: obj.Spec.SnapshotRef.Name,
106113
}, snapshot); err != nil {
107114
err = fmt.Errorf("failed to find snapshot: %w", err)
108-
r.markAndEmitEvent(obj, v1alpha1.SnapshotGetFailedReason, err)
115+
status.MarkNotReady(r.EventRecorder, obj, v1alpha1.SnapshotGetFailedReason, err.Error())
109116

110117
return ctrl.Result{}, err
111118
}
112119

113-
log.V(4).Info("found target snapshot")
114-
115120
namespace := obj.Spec.RepositoryRef.Namespace
116121
if namespace == "" {
117122
namespace = obj.Namespace
@@ -123,26 +128,22 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
123128
Name: obj.Spec.RepositoryRef.Name,
124129
}, repository); err != nil {
125130
err = fmt.Errorf("failed to find repository: %w", err)
126-
r.markAndEmitEvent(obj, v1alpha1.RepositoryGetFailedReason, err)
131+
status.MarkNotReady(r.EventRecorder, obj, v1alpha1.RepositoryGetFailedReason, err.Error())
127132

128133
return ctrl.Result{}, err
129134
}
130135

131-
log.V(4).Info("found target repository")
132-
133136
authSecret := &corev1.Secret{}
134137
if err = r.Get(ctx, types.NamespacedName{
135138
Namespace: repository.Namespace,
136139
Name: repository.Spec.Credentials.SecretRef.Name,
137140
}, authSecret); err != nil {
138141
err = fmt.Errorf("failed to find authentication secret: %w", err)
139-
r.markAndEmitEvent(obj, v1alpha1.CredentialsNotFoundReason, err)
142+
status.MarkNotReady(r.EventRecorder, obj, v1alpha1.CredentialsNotFoundReason, err.Error())
140143

141144
return ctrl.Result{}, err
142145
}
143146

144-
log.V(4).Info("found authentication secret")
145-
146147
baseBranch := obj.Spec.CommitTemplate.BaseBranch
147148
if baseBranch == "" {
148149
baseBranch = "main"
@@ -153,15 +154,13 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
153154
targetBranch = fmt.Sprintf("branch-%d", time.Now().Unix())
154155
} else if targetBranch == "" && !obj.Spec.AutomaticPullRequestCreation {
155156
err = fmt.Errorf("branch cannot be empty if automatic pull request creation is not enabled")
156-
r.markAndEmitEvent(obj, v1alpha1.GitRepositoryPushFailedReason, err)
157+
status.MarkNotReady(r.EventRecorder, obj, v1alpha1.GitRepositoryPushFailedReason, err.Error())
157158

158159
return ctrl.Result{}, err
159160
}
160161

161-
log.Info("preparing to push snapshot content", "base", baseBranch, "target", targetBranch)
162+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "preparing to push snapshot content with base branch %s and target %s", baseBranch, targetBranch)
162163

163-
// trim any trailing `/` and then just add.
164-
log.V(4).Info("crafting artifact URL to download from", "url", snapshot.Status.RepositoryURL)
165164
opts := &pkg.PushOptions{
166165
URL: repository.GetRepositoryURL(),
167166
Message: obj.Spec.CommitTemplate.Message,
@@ -179,41 +178,33 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
179178
digest, err = r.Git.Push(ctx, opts)
180179
if err != nil {
181180
err = fmt.Errorf("failed to push to git repository: %w", err)
182-
r.markAndEmitEvent(obj, v1alpha1.GitRepositoryPushFailedReason, err)
181+
status.MarkNotReady(r.EventRecorder, obj, v1alpha1.GitRepositoryPushFailedReason, err.Error())
183182

184183
return ctrl.Result{}, err
185184
}
186185

187-
log.Info("target content pushed with digest", "base", baseBranch, "target", targetBranch, "digest", digest)
188-
189186
obj.Status.Digest = digest
190187

191188
if obj.Spec.AutomaticPullRequestCreation {
192-
log.Info("automatic pull-request creation is enabled, preparing to create a pull request")
189+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "creating pull request")
193190

194191
id, err := r.Provider.CreatePullRequest(ctx, targetBranch, *obj, *repository)
195192
if err != nil {
196193
err = fmt.Errorf("failed to create pull request: %w", err)
197-
r.markAndEmitEvent(obj, v1alpha1.CreatePullRequestFailedReason, err)
194+
status.MarkNotReady(r.EventRecorder, obj, v1alpha1.CreatePullRequestFailedReason, err.Error())
198195

199196
return ctrl.Result{}, err
200197
}
201198

202199
obj.Status.PullRequestID = id
203200
}
204201

205-
log.Info("successfully reconciled sync object")
206202
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "Reconciliation success")
207203
event.New(r.EventRecorder, obj, eventv1.EventSeverityInfo, "Reconciliation success", nil)
208204

209205
return ctrl.Result{}, nil
210206
}
211207

212-
func (r *SyncReconciler) markAndEmitEvent(obj *v1alpha1.Sync, reason string, err error) {
213-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, err.Error(), nil)
214-
conditions.MarkFalse(obj, meta.ReadyCondition, reason, err.Error())
215-
}
216-
217208
// SetupWithManager sets up the controller with the Manager.
218209
func (r *SyncReconciler) SetupWithManager(mgr ctrl.Manager) error {
219210
return ctrl.NewControllerManagedBy(mgr).

controllers/mpas/repository_controller.go

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import (
1313
"github.com/fluxcd/pkg/apis/meta"
1414
"github.com/fluxcd/pkg/runtime/conditions"
1515
"github.com/fluxcd/pkg/runtime/patch"
16+
rreconcile "github.com/fluxcd/pkg/runtime/reconcile"
17+
"github.com/open-component-model/ocm-controller/pkg/status"
1618
apierrors "k8s.io/apimachinery/pkg/api/errors"
1719
"k8s.io/apimachinery/pkg/runtime"
1820
kuberecorder "k8s.io/client-go/tools/record"
1921
ctrl "sigs.k8s.io/controller-runtime"
2022
"sigs.k8s.io/controller-runtime/pkg/builder"
2123
"sigs.k8s.io/controller-runtime/pkg/client"
22-
"sigs.k8s.io/controller-runtime/pkg/log"
2324
"sigs.k8s.io/controller-runtime/pkg/predicate"
2425

2526
mpasv1alpha1 "github.com/open-component-model/git-controller/apis/mpas/v1alpha1"
@@ -42,9 +43,6 @@ type RepositoryReconciler struct {
4243
// Reconcile is part of the main kubernetes reconciliation loop which aims to
4344
// move the current state of the cluster closer to the desired state.
4445
func (r *RepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) {
45-
logger := log.FromContext(ctx).WithName("repository")
46-
logger.V(4).Info("entering repository loop...")
47-
4846
obj := &mpasv1alpha1.Repository{}
4947

5048
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
@@ -64,17 +62,16 @@ func (r *RepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request)
6462
return
6563
}
6664

67-
// Set status observed generation option if the component is stalled or ready.
68-
if conditions.IsReady(obj) {
69-
obj.Status.ObservedGeneration = obj.Generation
70-
}
71-
72-
// Update the object.
73-
if perr := patchHelper.Patch(ctx, obj); perr != nil {
74-
err = errors.Join(err, perr)
65+
if derr := status.UpdateStatus(ctx, patchHelper, obj, r.EventRecorder, obj.GetRequeueAfter()); derr != nil {
66+
err = errors.Join(err, derr)
7567
}
7668
}()
7769

70+
// Starts the progression by setting ReconcilingCondition.
71+
// This will be checked in defer.
72+
// Should only be deleted on a success.
73+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "reconciliation in progress for resource: %s", obj.Name)
74+
7875
return r.reconcile(ctx, obj)
7976
}
8077

@@ -86,34 +83,48 @@ func (r *RepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
8683
}
8784

8885
func (r *RepositoryReconciler) reconcile(ctx context.Context, obj *mpasv1alpha1.Repository) (ctrl.Result, error) {
89-
logger := log.FromContext(ctx)
86+
if obj.Generation != obj.Status.ObservedGeneration {
87+
rreconcile.ProgressiveStatus(
88+
false,
89+
obj,
90+
meta.ProgressingReason,
91+
"processing object: new generation %d -> %d",
92+
obj.Status.ObservedGeneration,
93+
obj.Generation,
94+
)
95+
}
96+
97+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "creating repository: %s", obj.Name)
9098

91-
logger.Info("creating or adopting repository")
9299
if err := r.Provider.CreateRepository(ctx, *obj); err != nil {
93-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, err.Error(), nil)
94-
conditions.MarkFalse(obj, meta.ReadyCondition, mpasv1alpha1.RepositoryCreateFailedReason, err.Error())
100+
err := fmt.Errorf("failed to create repository: %w", err)
101+
status.MarkNotReady(r.EventRecorder, obj, mpasv1alpha1.RepositoryCreateFailedReason, err.Error())
95102

96-
return ctrl.Result{}, fmt.Errorf("failed to create repository: %w", err)
103+
return ctrl.Result{}, err
97104
}
98105

99-
logger.Info("updating branch protection rules")
106+
rreconcile.ProgressiveStatus(false, obj, meta.ProgressingReason, "setting up branch protection rules: %s", obj.Name)
107+
100108
if err := r.Provider.CreateBranchProtection(ctx, *obj); err != nil {
101109
if errors.Is(err, providers.NotSupportedError) {
102-
// ignore and return without branch protection rules.
103-
logger.Error(err, fmt.Sprintf("provider %s does not support updating branch protection rules", obj.Spec.Provider))
110+
r.markAsDone(obj)
104111

112+
// ignore and return without branch protection rules.
105113
return ctrl.Result{}, nil
106114
}
107115

108-
conditions.MarkFalse(obj, meta.ReadyCondition, mpasv1alpha1.UpdatingBranchProtectionFailedReason, err.Error())
109-
event.New(r.EventRecorder, obj, eventv1.EventSeverityError, err.Error(), nil)
116+
err := fmt.Errorf("failed to update branch protection rules: %w", err)
117+
status.MarkNotReady(r.EventRecorder, obj, mpasv1alpha1.UpdatingBranchProtectionFailedReason, err.Error())
110118

111-
return ctrl.Result{}, fmt.Errorf("failed to update branch protection rules: %w", err)
119+
return ctrl.Result{}, err
112120
}
113121

114-
logger.Info("done reconciling repository")
115-
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "Reconciliation success")
116-
event.New(r.EventRecorder, obj, eventv1.EventSeverityInfo, "Reconciliation success", nil)
122+
r.markAsDone(obj)
117123

118124
return ctrl.Result{}, nil
119125
}
126+
127+
func (r *RepositoryReconciler) markAsDone(obj *mpasv1alpha1.Repository) {
128+
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "Reconciliation success")
129+
event.New(r.EventRecorder, obj, eventv1.EventSeverityInfo, "Reconciliation success", nil)
130+
}

0 commit comments

Comments
 (0)