Skip to content

Commit

Permalink
Move controllers and reconcile logic
Browse files Browse the repository at this point in the history
This introduce reconciler pkg, this package provides more clarity
between controller and reconciler logic. It also allows each resource
to have an internal resources package to deal with all related resources
involved during that resource reconciliation.
  • Loading branch information
qu1queee committed Feb 16, 2021
1 parent 121b16a commit 47234e2
Show file tree
Hide file tree
Showing 32 changed files with 505 additions and 510 deletions.
14 changes: 0 additions & 14 deletions pkg/controller/add_build.go

This file was deleted.

14 changes: 0 additions & 14 deletions pkg/controller/add_buildrun.go

This file was deleted.

14 changes: 0 additions & 14 deletions pkg/controller/add_buildstrategy.go

This file was deleted.

14 changes: 0 additions & 14 deletions pkg/controller/add_clusterbuildstrategy.go

This file was deleted.

17 changes: 0 additions & 17 deletions pkg/controller/buildrun/buildrun_suite_test.go

This file was deleted.

20 changes: 20 additions & 0 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import (
"github.com/shipwright-io/build/pkg/apis"
"github.com/shipwright-io/build/pkg/config"
"github.com/shipwright-io/build/pkg/ctxlog"
"github.com/shipwright-io/build/pkg/reconciler/build"
"github.com/shipwright-io/build/pkg/reconciler/buildrun"
"github.com/shipwright-io/build/pkg/reconciler/buildstrategy"
"github.com/shipwright-io/build/pkg/reconciler/clusterbuildstrategy"
)

// AddToManagerFuncs is a list of functions to add all Controllers to the Manager
Expand Down Expand Up @@ -48,6 +52,22 @@ func NewManager(ctx context.Context, config *config.Config, cfg *rest.Config, op
return nil, err
}

if err := build.Add(ctx, config, mgr); err != nil {
return nil, err
}

if err := buildrun.Add(ctx, config, mgr); err != nil {
return nil, err
}

if err := buildstrategy.Add(ctx, config, mgr); err != nil {
return nil, err
}

if err := clusterbuildstrategy.Add(ctx, config, mgr); err != nil {
return nil, err
}

// Setup all Controllers
if err := AddToManager(ctx, config, mgr); err != nil {
return nil, err
Expand Down
32 changes: 0 additions & 32 deletions pkg/controller/utils/utils.go

This file was deleted.

95 changes: 95 additions & 0 deletions pkg/reconciler/build/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package build

import (
"context"

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
"github.com/shipwright-io/build/pkg/ctxlog"
buildmetrics "github.com/shipwright-io/build/pkg/metrics"
"github.com/shipwright-io/build/pkg/validate"
)

// Reconcile reads that state of the cluster for a Build object and makes changes based on the state read
// and what is in the Build.Spec
func (r *ReconcileBuild) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Set the ctx to be Background, as the top-level context for incoming requests.
ctx, cancel := context.WithTimeout(r.ctx, r.config.CtxTimeOut)
defer cancel()

ctxlog.Debug(ctx, "start reconciling Build", namespace, request.Namespace, name, request.Name)

b := &build.Build{}
err := r.client.Get(ctx, request.NamespacedName, b)
if err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
} else if apierrors.IsNotFound(err) {
ctxlog.Debug(ctx, "finish reconciling build. build was not found", namespace, request.Namespace, name, request.Name)
return reconcile.Result{}, nil
}

// Populate the status struct with default values
b.Status.Registered = corev1.ConditionFalse
b.Status.Reason = build.SucceedStatus

// build a list of current validation types
validationTypes := []string{
validate.OwnerReferences,
validate.SourceURL,
validate.Secrets,
validate.Strategies,
validate.Runtime,
}

// trigger all current validations
for _, validationType := range validationTypes {
v, err := validate.NewValidation(validationType, b, r.client, r.scheme)
if err != nil {
// when the validation type is unknown
return reconcile.Result{}, err
}

if err := v.ValidatePath(ctx); err != nil {
// We enqueue another reconcile here. This is done only for validation
// types where the error can be produced from a failed API call.
if validationType == validate.Secrets || validationType == validate.Strategies {
return reconcile.Result{}, err
}
if validationType == validate.OwnerReferences {
// we do not want to bail out here if the owerreference validation fails, we ignore this error on purpose
// In case we just created the Build, we want the Build reconcile logic to continue, in order to
// validate the Build references ( e.g secrets, strategies )
ctxlog.Info(ctx, "unexpected error during ownership reference validation", namespace, request.Namespace, name, request.Name, "error", err)
}
}
if b.Status.Reason != build.SucceedStatus {
return r.UpdateBuildStatusAndRetreat(ctx, b)
}
}

b.Status.Registered = corev1.ConditionTrue
b.Status.Message = build.AllValidationsSucceeded
err = r.client.Status().Update(ctx, b)
if err != nil {
return reconcile.Result{}, err
}

// Increase Build count in metrics
buildmetrics.BuildCountInc(b.Spec.StrategyRef.Name, b.Namespace, b.Name)

ctxlog.Debug(ctx, "finishing reconciling Build", namespace, request.Namespace, name, request.Name)
return reconcile.Result{}, nil
}

// UpdateBuildStatusAndRetreat returns an error if an update fails, this should force
// a new reconcile until the API call succeeds. If return is nil, no further reconciliations
// will take place
func (r *ReconcileBuild) UpdateBuildStatusAndRetreat(ctx context.Context, b *build.Build) (reconcile.Result, error) {
if err := r.client.Status().Update(ctx, b); err != nil {
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import (

build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
"github.com/shipwright-io/build/pkg/config"
buildController "github.com/shipwright-io/build/pkg/controller/build"
"github.com/shipwright-io/build/pkg/controller/fakes"
"github.com/shipwright-io/build/pkg/ctxlog"
buildController "github.com/shipwright-io/build/pkg/reconciler/build"
"github.com/shipwright-io/build/test"
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0

package build

import (
"context"
"reflect"

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -26,8 +21,6 @@ import (
build "github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
"github.com/shipwright-io/build/pkg/config"
"github.com/shipwright-io/build/pkg/ctxlog"
buildmetrics "github.com/shipwright-io/build/pkg/metrics"
"github.com/shipwright-io/build/pkg/validate"
)

const (
Expand Down Expand Up @@ -188,9 +181,6 @@ func add(ctx context.Context, mgr manager.Manager, r reconcile.Reconciler) error
}, preSecret)
}

// blank assignment to verify that ReconcileBuild implements reconcile.Reconciler
var _ reconcile.Reconciler = &ReconcileBuild{}

// ReconcileBuild reconciles a Build object
type ReconcileBuild struct {
// This client, initialized using mgr.Client() above, is a split client
Expand All @@ -202,87 +192,6 @@ type ReconcileBuild struct {
setOwnerReferenceFunc setOwnerReferenceFunc
}

// Reconcile reads that state of the cluster for a Build object and makes changes based on the state read
// and what is in the Build.Spec
func (r *ReconcileBuild) Reconcile(request reconcile.Request) (reconcile.Result, error) {
// Set the ctx to be Background, as the top-level context for incoming requests.
ctx, cancel := context.WithTimeout(r.ctx, r.config.CtxTimeOut)
defer cancel()

ctxlog.Debug(ctx, "start reconciling Build", namespace, request.Namespace, name, request.Name)

b := &build.Build{}
err := r.client.Get(ctx, request.NamespacedName, b)
if err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
} else if apierrors.IsNotFound(err) {
ctxlog.Debug(ctx, "finish reconciling build. build was not found", namespace, request.Namespace, name, request.Name)
return reconcile.Result{}, nil
}

// Populate the status struct with default values
b.Status.Registered = corev1.ConditionFalse
b.Status.Reason = build.SucceedStatus

// build a list of current validation types
validationTypes := []string{
validate.OwnerReferences,
validate.SourceURL,
validate.Secrets,
validate.Strategies,
validate.Runtime,
}

// trigger all current validations
for _, validationType := range validationTypes {
v, err := validate.NewValidation(validationType, b, r.client, r.scheme)
if err != nil {
// when the validation type is unknown
return reconcile.Result{}, err
}

if err := v.ValidatePath(ctx); err != nil {
// We enqueue another reconcile here. This is done only for validation
// types where the error can be produced from a failed API call.
if validationType == validate.Secrets || validationType == validate.Strategies {
return reconcile.Result{}, err
}
if validationType == validate.OwnerReferences {
// we do not want to bail out here if the owerreference validation fails, we ignore this error on purpose
// In case we just created the Build, we want the Build reconcile logic to continue, in order to
// validate the Build references ( e.g secrets, strategies )
ctxlog.Info(ctx, "unexpected error during ownership reference validation", namespace, request.Namespace, name, request.Name, "error", err)
}
}
if b.Status.Reason != build.SucceedStatus {
return r.UpdateBuildStatusAndRetreat(ctx, b)
}
}

b.Status.Registered = corev1.ConditionTrue
b.Status.Message = build.AllValidationsSucceeded
err = r.client.Status().Update(ctx, b)
if err != nil {
return reconcile.Result{}, err
}

// Increase Build count in metrics
buildmetrics.BuildCountInc(b.Spec.StrategyRef.Name, b.Namespace, b.Name)

ctxlog.Debug(ctx, "finishing reconciling Build", namespace, request.Namespace, name, request.Name)
return reconcile.Result{}, nil
}

// UpdateBuildStatusAndRetreat returns an error if an update fails, this should force
// a new reconcile until the API call succeeds. If return is nil, no further reconciliations
// will take place
func (r *ReconcileBuild) UpdateBuildStatusAndRetreat(ctx context.Context, b *build.Build) (reconcile.Result, error) {
if err := r.client.Status().Update(ctx, b); err != nil {
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}

func buildSecretRefAnnotationExist(annotation map[string]string) (string, bool) {
if val, ok := annotation[build.AnnotationBuildRefSecret]; ok {
return val, true
Expand Down
Loading

0 comments on commit 47234e2

Please sign in to comment.