Skip to content

Commit 5f7ef5a

Browse files
committed
Add new component build model with per-version lifecycle management
Implement new component model that manages build configurations and pipelines at the version level. Each version can be independently onboarded, triggered, and offboarded through component spec actions. Key features: - Per-version PaC configuration and pipeline management - Action-based workflow (create configuration, trigger build, offboard) - Version status tracking with error reporting - Cache synchronization to prevent race conditions between reconciles - Support for invalid version cleanup from actions The new model is enabled via component annotation or default setting. STONEBLD-4018 Signed-off-by: Robert Cerven <rcerven@redhat.com>
1 parent 1aae2f2 commit 5f7ef5a

21 files changed

+3167
-273
lines changed

cmd/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ import (
6161
l "github.com/konflux-ci/build-service/pkg/logs"
6262
pacwebhook "github.com/konflux-ci/build-service/pkg/pacwebhook"
6363

64-
appstudiov1alpha1 "github.com/konflux-ci/application-api/api/v1alpha1"
64+
applicationapiv1alpha1 "github.com/konflux-ci/application-api/api/v1alpha1"
6565
imagerepositoryapi "github.com/konflux-ci/image-controller/api/v1alpha1"
6666
releaseapi "github.com/konflux-ci/release-service/api/v1alpha1"
6767
pacv1alpha1 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
@@ -78,7 +78,7 @@ var (
7878
func init() {
7979
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
8080

81-
utilruntime.Must(appstudiov1alpha1.AddToScheme(scheme))
81+
utilruntime.Must(applicationapiv1alpha1.AddToScheme(scheme))
8282
// +kubebuilder:scaffold:scheme
8383
}
8484

config/rbac/role.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ rules:
3232
- appstudio.redhat.com
3333
resources:
3434
- components
35+
- components/status
3536
verbs:
3637
- get
3738
- list
@@ -41,7 +42,6 @@ rules:
4142
- apiGroups:
4243
- appstudio.redhat.com
4344
resources:
44-
- components/status
4545
- imagerepositories
4646
- imagerepositories/status
4747
- releaseplanadmissions

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ require (
3333

3434
// If you update dependencies below you must also update internal/controller/suite_test.go
3535
require (
36-
github.com/konflux-ci/application-api v0.0.0-20240812090716-e7eb2ecfb409
36+
github.com/konflux-ci/application-api v0.0.0-20260205151641-c691ffebedf8
3737
github.com/konflux-ci/image-controller v0.0.0-20250424143112-69ec692d353c
3838
github.com/konflux-ci/release-service v0.0.0-20240610124538-758a1d48d002
3939
github.com/openshift-pipelines/pipelines-as-code v0.37.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
309309
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
310310
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
311311
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
312-
github.com/konflux-ci/application-api v0.0.0-20240812090716-e7eb2ecfb409 h1:hAhhcfc/EXW9eKS827PmyYuhk+Y4KkVOsT53Uo9OzCU=
313-
github.com/konflux-ci/application-api v0.0.0-20240812090716-e7eb2ecfb409/go.mod h1:948Z+a1IbfRT0RtoHzWWSN9YEucSbMJTHaMhz7dVICc=
312+
github.com/konflux-ci/application-api v0.0.0-20260205151641-c691ffebedf8 h1:HjVmLXbIzsqOAU1gN+qhJdUIIaDiUtd2nc9YvLg8iHo=
313+
github.com/konflux-ci/application-api v0.0.0-20260205151641-c691ffebedf8/go.mod h1:948Z+a1IbfRT0RtoHzWWSN9YEucSbMJTHaMhz7dVICc=
314314
github.com/konflux-ci/coverport/instrumentation/go v0.0.0-20251127115143-b5207b335f8b h1:NoriO1KRc+7d2/JA07JizqxP0LlA2oJdD5AuQOvEIjE=
315315
github.com/konflux-ci/coverport/instrumentation/go v0.0.0-20251127115143-b5207b335f8b/go.mod h1:WVMHU9A2464s/vjH1xOTm4LJDD4xP+VlEiU+KM0gkSU=
316316
github.com/konflux-ci/image-controller v0.0.0-20250424143112-69ec692d353c h1:+LtI4WT2KDDpCbVki47dLIu03NH6+Qj0d/MKNu3MZYk=

internal/controller/component_build_controller.go

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
3636
"sigs.k8s.io/controller-runtime/pkg/predicate"
3737

38-
appstudiov1alpha1 "github.com/konflux-ci/application-api/api/v1alpha1"
38+
applicationapiv1alpha1 "github.com/konflux-ci/application-api/api/v1alpha1"
3939

4040
"github.com/konflux-ci/build-service/pkg/boerrors"
4141
"github.com/konflux-ci/build-service/pkg/bometrics"
@@ -63,12 +63,18 @@ const (
6363

6464
gitCommitShaAnnotationName = "build.appstudio.redhat.com/commit_sha"
6565
gitRepoAtShaAnnotationName = "build.appstudio.openshift.io/repo"
66+
ContextAnnotationName = "appstudio.openshift.io/context"
67+
VersionAnnotationName = "appstudio.openshift.io/version"
6668

6769
defaultBuildPipelineAnnotation = "build.appstudio.openshift.io/pipeline"
6870
buildPipelineConfigMapResourceName = "build-pipeline-config"
6971
buildPipelineConfigName = "config.yaml"
7072

7173
waitForContainerImageMessage = "waiting for spec.containerImage to be set by ImageRepository with annotation image-controller.appstudio.redhat.com/update-component-image"
74+
75+
ComponentModelVersionAnnotation = "build.appstudio.openshift.io/component_model_version"
76+
DefaultComponentModelVersion = "v1"
77+
NewComponentModelVersion = "v2"
7278
)
7379

7480
type BuildStatus struct {
@@ -109,12 +115,13 @@ type ComponentBuildReconciler struct {
109115
// SetupWithManager sets up the controller with the Manager.
110116
func (r *ComponentBuildReconciler) SetupWithManager(mgr ctrl.Manager) error {
111117
return ctrl.NewControllerManagedBy(mgr).
112-
For(&appstudiov1alpha1.Component{}, builder.WithPredicates(predicate.Funcs{
118+
For(&applicationapiv1alpha1.Component{}, builder.WithPredicates(predicate.Funcs{
113119
CreateFunc: func(e event.CreateEvent) bool {
114120
return true
115121
},
116122
UpdateFunc: func(e event.UpdateEvent) bool {
117-
return true
123+
// this might prevent scheduled (daily) reconcile to be triggered (but we shouldn't need it)
124+
return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration()
118125
},
119126
DeleteFunc: func(e event.DeleteEvent) bool {
120127
return false
@@ -164,7 +171,7 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
164171
reconcileStartTime := time.Now()
165172

166173
// Fetch the Component instance
167-
var component appstudiov1alpha1.Component
174+
var component applicationapiv1alpha1.Component
168175
err := r.Client.Get(ctx, req.NamespacedName, &component)
169176
if err != nil {
170177
if errors.IsNotFound(err) {
@@ -177,11 +184,16 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
177184
return ctrl.Result{}, err
178185
}
179186

187+
requestedModel, requestedModelExists := component.Annotations[ComponentModelVersionAnnotation]
188+
if (requestedModelExists && requestedModel == NewComponentModelVersion) || DefaultComponentModelVersion == NewComponentModelVersion {
189+
return r.ReconcileNewModel(ctx, req)
190+
}
191+
180192
// Don't recreate build pipeline Service Account upon component deletion.
181193
if component.ObjectMeta.DeletionTimestamp.IsZero() {
182194
// We need to make sure the Service Account exists before checking the Component image,
183195
// because Image Controller operator expects the Service Account to exist to link push secret to it.
184-
if err := r.EnsureBuildPipelineServiceAccount(ctx, &component); err != nil {
196+
if err := r.EnsureBuildPipelineServiceAccount(ctx, &component, true); err != nil {
185197
return ctrl.Result{}, err
186198
}
187199
}
@@ -202,7 +214,7 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
202214
log.Error(err, "failed to update component after waiting for containerImage", l.Action, l.ActionUpdate, l.Audit, "true")
203215
return ctrl.Result{}, err
204216
}
205-
r.WaitForCacheUpdate(ctx, req.NamespacedName, &component)
217+
r.WaitForCacheUpdateOldModel(ctx, req.NamespacedName, &component)
206218

207219
return ctrl.Result{}, nil
208220
}
@@ -256,7 +268,7 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
256268
log.Info("PaC finalizer removed", l.Action, l.ActionDelete)
257269

258270
// Try to clean up Pipelines as Code configuration
259-
_, _ = r.UndoPaCProvisionForComponent(ctx, &component)
271+
_, _ = r.UndoPaCProvisionForComponentOldModel(ctx, &component)
260272
}
261273

262274
// Clean up common build pipelines Role Binding
@@ -305,7 +317,7 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
305317
return ctrl.Result{}, err
306318
}
307319
log.Info(fmt.Sprintf("updated component after wrong %s annotation", defaultBuildPipelineAnnotation))
308-
r.WaitForCacheUpdate(ctx, req.NamespacedName, &component)
320+
r.WaitForCacheUpdateOldModel(ctx, req.NamespacedName, &component)
309321

310322
return ctrl.Result{}, nil
311323
}
@@ -338,7 +350,7 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
338350
return ctrl.Result{}, nil
339351
}
340352

341-
reconcileRequired, err := r.TriggerPaCBuild(ctx, &component)
353+
reconcileRequired, err := r.TriggerPaCBuildOldModel(ctx, &component)
342354

343355
if err != nil {
344356
if boErr, ok := err.(*boerrors.BuildOpError); ok && boErr.IsPersistent() {
@@ -368,7 +380,7 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
368380
}()
369381

370382
pacBuildStatus := &PaCBuildStatus{}
371-
if mergeUrl, err := r.ProvisionPaCForComponent(ctx, &component); err != nil {
383+
if mergeUrl, err := r.ProvisionPaCForComponentOldModel(ctx, &component); err != nil {
372384
if boErr, ok := err.(*boerrors.BuildOpError); ok && boErr.IsPersistent() {
373385
log.Error(err, "Pipelines as Code provision for the Component failed")
374386
pacBuildStatus.State = "error"
@@ -436,7 +448,7 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
436448
}
437449

438450
pacBuildStatus := &PaCBuildStatus{}
439-
if mergeUrl, err := r.UndoPaCProvisionForComponent(ctx, &component); err != nil {
451+
if mergeUrl, err := r.UndoPaCProvisionForComponentOldModel(ctx, &component); err != nil {
440452
if boErr, ok := err.(*boerrors.BuildOpError); ok && boErr.IsPersistent() {
441453
log.Error(err, "Pipelines as Code unprovision for the Component failed")
442454
pacBuildStatus.State = "error"
@@ -491,12 +503,12 @@ func (r *ComponentBuildReconciler) Reconcile(ctx context.Context, req ctrl.Reque
491503
// remove component from metrics map
492504
delete(bometrics.ComponentTimesForMetrics, componentIdForMetrics)
493505

494-
r.WaitForCacheUpdate(ctx, req.NamespacedName, &component)
506+
r.WaitForCacheUpdateOldModel(ctx, req.NamespacedName, &component)
495507

496508
return ctrl.Result{}, nil
497509
}
498510

499-
func (r *ComponentBuildReconciler) WaitForCacheUpdate(ctx context.Context, namespace types.NamespacedName, component *appstudiov1alpha1.Component) {
511+
func (r *ComponentBuildReconciler) WaitForCacheUpdateOldModel(ctx context.Context, namespace types.NamespacedName, component *applicationapiv1alpha1.Component) {
500512
log := ctrllog.FromContext(ctx)
501513

502514
// Here we do some trick.
@@ -532,7 +544,7 @@ func (r *ComponentBuildReconciler) WaitForCacheUpdate(ctx context.Context, names
532544
}
533545
}
534546

535-
func readBuildStatus(component *appstudiov1alpha1.Component) *BuildStatus {
547+
func readBuildStatus(component *applicationapiv1alpha1.Component) *BuildStatus {
536548
if component.Annotations == nil {
537549
return &BuildStatus{}
538550
}
@@ -545,7 +557,7 @@ func readBuildStatus(component *appstudiov1alpha1.Component) *BuildStatus {
545557
return &BuildStatus{}
546558
}
547559

548-
func writeBuildStatus(component *appstudiov1alpha1.Component, buildStatus *BuildStatus) {
560+
func writeBuildStatus(component *applicationapiv1alpha1.Component, buildStatus *BuildStatus) {
549561
if component.Annotations == nil {
550562
component.Annotations = make(map[string]string)
551563
}

0 commit comments

Comments
 (0)