-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor manager and controllers to split shipwright build controller…
… from certificates controller shipwright build controller to reconcile shipwright build manifest certificates controller to reconcile webhook certificate
- Loading branch information
Showing
14 changed files
with
500 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright The Shipwright Contributors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package controllers | ||
|
||
import ( | ||
"github.com/shipwright-io/operator/controllers/certificates" | ||
) | ||
|
||
func init() { | ||
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager. | ||
AddToManagerFuncs = append(AddToManagerFuncs, certificates.Add) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright The Shipwright Contributors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package controllers | ||
|
||
import ( | ||
"github.com/shipwright-io/operator/controllers/shipwrightbuild" | ||
) | ||
|
||
func init() { | ||
// AddToManagerFuncs is a list of functions to create controllers and add them to a manager. | ||
AddToManagerFuncs = append(AddToManagerFuncs, shipwrightbuild.Add) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// Copyright The Shipwright Contributors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package certificates | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/go-logr/logr" | ||
"github.com/manifestival/manifestival" | ||
corev1 "k8s.io/api/core/v1" | ||
crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" | ||
"k8s.io/apimachinery/pkg/api/errors" | ||
apimeta "k8s.io/apimachinery/pkg/api/meta" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/builder" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/event" | ||
"sigs.k8s.io/controller-runtime/pkg/manager" | ||
"sigs.k8s.io/controller-runtime/pkg/predicate" | ||
|
||
"github.com/shipwright-io/operator/api/v1alpha1" | ||
commonctrl "github.com/shipwright-io/operator/controllers/common" | ||
"github.com/shipwright-io/operator/pkg/certmanager" | ||
"github.com/shipwright-io/operator/pkg/common" | ||
) | ||
|
||
// CertificatesReconciler reconciles a ShipwrightBuild object | ||
type CertificatesReconciler struct { | ||
client.Client // controller kubernetes client | ||
CRDClient crdclientv1.ApiextensionsV1Interface | ||
|
||
Logger logr.Logger // decorated logger | ||
Scheme *runtime.Scheme // runtime scheme | ||
Manifest manifestival.Manifest // release manifests render | ||
} | ||
|
||
// Add creates a new certificates Controller and adds it to the Manager. The Manager will set fields on the Controller | ||
// and Start it when the Manager is Started. | ||
func Add(mgr manager.Manager) error { | ||
r, err := newReconciler(mgr) | ||
if err != nil { | ||
return err | ||
} | ||
return add(mgr, r) | ||
} | ||
|
||
// newReconciler returns a new reconcile.Reconciler | ||
func newReconciler(mgr manager.Manager) (*CertificatesReconciler, error) { | ||
c := mgr.GetClient() | ||
scheme := mgr.GetScheme() | ||
logger := ctrl.Log.WithName("controllers").WithName("certificates") | ||
|
||
crdClient, err := crdclientv1.NewForConfig(mgr.GetConfig()) | ||
if err != nil { | ||
logger.Error(err, "unable to get crd client") | ||
return nil, err | ||
} | ||
|
||
return &CertificatesReconciler{ | ||
CRDClient: crdClient, | ||
Client: c, | ||
Scheme: scheme, | ||
Logger: logger, | ||
}, nil | ||
} | ||
|
||
// add adds a new Controller to mgr with r as the reconcile.Reconciler | ||
func add(mgr manager.Manager, r *CertificatesReconciler) error { | ||
|
||
return ctrl.NewControllerManagedBy(mgr). | ||
For(&v1alpha1.ShipwrightBuild{}, builder.WithPredicates(predicate.Funcs{ | ||
CreateFunc: func(ce event.CreateEvent) bool { | ||
// all new objects must be subject to reconciliation | ||
return true | ||
}, | ||
DeleteFunc: func(e event.DeleteEvent) bool { | ||
// objects that haven't been confirmed deleted must be subject to reconciliation | ||
return !e.DeleteStateUnknown | ||
}, | ||
UpdateFunc: func(e event.UpdateEvent) bool { | ||
// objects that have updated generation must be subject to reconciliation | ||
return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() | ||
}, | ||
})). | ||
Complete(r) | ||
} | ||
|
||
// Reconcile performs the resource reconciliation steps to deploy or remove Shipwright Build | ||
// instances. When deletion-timestamp is found, the removal of the previously deploy resources is | ||
// executed, otherwise the regular deploy workflow takes place. | ||
func (r *CertificatesReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
logger := r.Logger.WithValues("namespace", req.Namespace, "name", req.Name) | ||
logger.Info("Starting resource reconciliation...") | ||
|
||
// retrieving the ShipwrightBuild instance requested for reconcile | ||
b := &v1alpha1.ShipwrightBuild{} | ||
if err := r.Get(ctx, req.NamespacedName, b); err != nil { | ||
if errors.IsNotFound(err) { | ||
logger.Info("Resource is not found!") | ||
return commonctrl.NoRequeue() | ||
} | ||
logger.Error(err, "retrieving ShipwrightBuild object from cache") | ||
return commonctrl.RequeueOnError(err) | ||
} | ||
init := b.Status.Conditions == nil | ||
if init { | ||
b.Status.Conditions = make([]metav1.Condition, 0) | ||
apimeta.SetStatusCondition(&b.Status.Conditions, metav1.Condition{ | ||
Type: commonctrl.ConditionReady, | ||
Status: metav1.ConditionUnknown, // we just started trying to reconcile | ||
Reason: "Init", | ||
Message: "Initializing Shipwright Operator", | ||
}) | ||
if err := r.Client.Status().Update(ctx, b); err != nil { | ||
return commonctrl.RequeueWithError(err) | ||
} | ||
} | ||
|
||
// selecting the target namespace based on the CRD information, when not informed using the | ||
// default namespace instead | ||
targetNamespace := b.Spec.TargetNamespace | ||
if targetNamespace == "" { | ||
logger.Info( | ||
"Namespace is not informed! Target namespace is selected from default settings instead", | ||
"defaultTargetNamespace", commonctrl.DefaultTargetNamespace, | ||
) | ||
targetNamespace = commonctrl.DefaultTargetNamespace | ||
} | ||
logger = logger.WithValues("targetNamespace", targetNamespace) | ||
// create if it does not exist | ||
ns := &corev1.Namespace{} | ||
if err := r.Get(ctx, types.NamespacedName{Name: targetNamespace}, ns); err != nil { | ||
if !errors.IsNotFound(err) { | ||
logger.Info("retrieving target namespace %s error: %s", targetNamespace, err.Error()) | ||
return commonctrl.RequeueOnError(err) | ||
} | ||
ns.Name = targetNamespace | ||
|
||
if err = r.Create(ctx, ns, &client.CreateOptions{Raw: &metav1.CreateOptions{}}); err != nil { | ||
if !errors.IsAlreadyExists(err) { | ||
logger.Info("creating target namespace %s error: %s", targetNamespace, err.Error()) | ||
return commonctrl.RequeueOnError(err) | ||
} | ||
} | ||
logger.Info("created target namespace") | ||
} | ||
|
||
// ReconcileCertManager | ||
if common.BoolFromEnvVar(commonctrl.UseManagedWebhookCerts) { | ||
requeue, err := certmanager.ReconcileCertManager(ctx, r.CRDClient, r.Client, r.Logger, targetNamespace) | ||
if err != nil { | ||
return ctrl.Result{Requeue: requeue}, err | ||
} | ||
if requeue { | ||
return commonctrl.Requeue() | ||
} | ||
} | ||
logger.Info("All done!") | ||
return commonctrl.NoRequeue() | ||
} |
149 changes: 149 additions & 0 deletions
149
controllers/certificates/certificates_controller_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package certificates | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
certmanager "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" | ||
o "github.com/onsi/gomega" | ||
"github.com/shipwright-io/operator/api/v1alpha1" | ||
commonctrl "github.com/shipwright-io/operator/controllers/common" | ||
corev1 "k8s.io/api/core/v1" | ||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" | ||
crdclientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/client/fake" | ||
"sigs.k8s.io/controller-runtime/pkg/log/zap" | ||
"sigs.k8s.io/controller-runtime/pkg/reconcile" | ||
) | ||
|
||
// bootstrapShipwrightBuildReconciler start up a new instance of ShipwrightBuildReconciler which is | ||
// ready to interact with Manifestival, returning the Manifestival instance and the client. | ||
func bootstrapCertificatesReconciler( | ||
t *testing.T, | ||
b *v1alpha1.ShipwrightBuild, | ||
tcrds []*crdv1.CustomResourceDefinition, | ||
) (client.Client, *crdclientv1.Clientset, *CertificatesReconciler) { | ||
g := o.NewGomegaWithT(t) | ||
|
||
s := runtime.NewScheme() | ||
s.AddKnownTypes(corev1.SchemeGroupVersion, &corev1.Namespace{}) | ||
s.AddKnownTypes(certmanager.SchemeGroupVersion, &certmanager.Certificate{}) | ||
s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.ShipwrightBuild{}) | ||
|
||
logger := zap.New() | ||
|
||
c := fake.NewClientBuilder().WithScheme(s).WithObjects(b).WithStatusSubresource(b).Build() | ||
|
||
var crdClient *crdclientv1.Clientset | ||
if len(tcrds) > 0 { | ||
objs := []runtime.Object{} | ||
for _, obj := range tcrds { | ||
objs = append(objs, obj) | ||
} | ||
crdClient = crdclientv1.NewSimpleClientset(objs...) | ||
} else { | ||
crdClient = crdclientv1.NewSimpleClientset() | ||
} | ||
|
||
r := &CertificatesReconciler{CRDClient: crdClient.ApiextensionsV1(), Client: c, Scheme: s, Logger: logger} | ||
|
||
if b.Spec.TargetNamespace != "" { | ||
t.Logf("Creating test namespace '%s'", b.Spec.TargetNamespace) | ||
t.Run("create-test-namespace", func(t *testing.T) { | ||
err := c.Create( | ||
context.TODO(), | ||
&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: b.Spec.TargetNamespace}}, | ||
&client.CreateOptions{}, | ||
) | ||
g.Expect(err).To(o.BeNil()) | ||
}) | ||
} | ||
|
||
return c, crdClient, r | ||
} | ||
|
||
// testCertificatesReconcilerReconcile simulates the reconciliation process for rolling out and | ||
// rolling back manifests in the informed target namespace name. | ||
func testCertificatesReconcilerReconcile(t *testing.T, targetNamespace string) { | ||
g := o.NewGomegaWithT(t) | ||
|
||
namespacedName := types.NamespacedName{Namespace: "default", Name: "name"} | ||
certificateName := types.NamespacedName{ | ||
Namespace: targetNamespace, | ||
Name: "shipwright-build-webhook-cert", | ||
} | ||
req := reconcile.Request{NamespacedName: namespacedName} | ||
|
||
b := &v1alpha1.ShipwrightBuild{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: namespacedName.Name, | ||
Namespace: namespacedName.Namespace, | ||
}, | ||
Spec: v1alpha1.ShipwrightBuildSpec{ | ||
TargetNamespace: targetNamespace, | ||
}, | ||
} | ||
crd1 := &crdv1.CustomResourceDefinition{} | ||
crd1.Name = "certificates.cert-manager.io" | ||
crd2 := &crdv1.CustomResourceDefinition{} | ||
crd2.Name = "certmanagers.operator.openshift.io" | ||
crds := []*crdv1.CustomResourceDefinition{crd1, crd2} | ||
_, _, r := bootstrapCertificatesReconciler(t, b, crds) | ||
|
||
t.Logf("Deploying Certificates Controller against '%s' namespace", targetNamespace) | ||
t.Setenv(commonctrl.UseManagedWebhookCerts, "true") | ||
|
||
// rolling out all manifests on the desired namespace, making sure the webhook certificate is created | ||
t.Run("rollout-manifests", func(t *testing.T) { | ||
ctx := context.TODO() | ||
res, err := r.Reconcile(ctx, req) | ||
g.Expect(err).To(o.BeNil()) | ||
g.Expect(res.Requeue).To(o.BeFalse()) | ||
err = r.Get(ctx, certificateName, &certmanager.Certificate{}) | ||
g.Expect(err).To(o.BeNil()) | ||
}) | ||
|
||
// rolling back all changes, making sure the webhook certificate is also not found afterwards | ||
t.Run("rollback-manifests", func(t *testing.T) { | ||
ctx := context.TODO() | ||
|
||
err := r.Get(ctx, namespacedName, b) | ||
g.Expect(err).To(o.BeNil()) | ||
|
||
// delete the ShipwrightBuild CR triggers the rollback logic so the | ||
// reconciliation should remove the objects previously deployed | ||
err = r.Delete(ctx, b, &client.DeleteOptions{}) | ||
g.Expect(err).To(o.BeNil()) | ||
|
||
res, err := r.Reconcile(ctx, req) | ||
g.Expect(err).To(o.BeNil()) | ||
g.Expect(res.Requeue).To(o.BeFalse()) | ||
|
||
err = r.Get(ctx, certificateName, &certmanager.Certificate{}) | ||
g.Expect(err).To(o.BeNil()) | ||
}) | ||
} | ||
|
||
// TestShipwrightBuildReconciler_Reconcile runs rollout/rollback tests against different namespaces. | ||
func TestCertificateseconciler_Reconcile(t *testing.T) { | ||
tests := []struct { | ||
testName string | ||
targetNamespace string | ||
}{{ | ||
testName: "target namespace is informed", | ||
targetNamespace: "namespace", | ||
}, { | ||
testName: "target namespace is not informed", | ||
targetNamespace: commonctrl.DefaultTargetNamespace, | ||
}} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.testName, func(t *testing.T) { | ||
testCertificatesReconcilerReconcile(t, tt.targetNamespace) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package common | ||
|
||
const ( | ||
// FinalizerAnnotation annotation string appended on finalizer slice. | ||
FinalizerAnnotation = "finalizer.operator.shipwright.io" | ||
// defaultTargetNamespace fallback namespace when `.spec.namepace` is not informed. | ||
DefaultTargetNamespace = "shipwright-build" | ||
|
||
// Ready object is providing service. | ||
ConditionReady = "Ready" | ||
|
||
// UseManagedWebhookCerts is an env Var that controls wether we install the webhook certs | ||
UseManagedWebhookCerts = "USE_MANAGED_WEBHOOK_CERTS" | ||
|
||
CertManagerInjectAnnotationKey = "cert-manager.io/inject-ca-from" | ||
CertManagerInjectAnnotationValueTemplate = "%s/shipwright-build-webhook-cert" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package controllers | ||
package common | ||
|
||
import ( | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
|
Oops, something went wrong.