Skip to content

Commit

Permalink
Shipwright Target Namespace
Browse files Browse the repository at this point in the history
CRD changes, adding `.spec.namespace` attribute.

Deploying Shipwright Build Controller, and dependencies, on a target
namespace informed on the CRD spec. Additionally, implementing
Kubernetes Finalizer workflow to remove objects with actions centered
on the Reconciler directly.

Co-authored-by: Adam Kaplan <[email protected]>
  • Loading branch information
otaviof and adambkaplan committed Aug 2, 2021
1 parent 45bd730 commit a9c5e27
Show file tree
Hide file tree
Showing 18 changed files with 534 additions and 133 deletions.
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,34 @@ An operator to install and configure [Shipwright](https://shipwright.io) on Kube

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to build, test, and submit contributions to the operator.
See [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to build, test, and submit
contributions to the operator.

## Usage

To deploy and manage instances of [Shipwright Build-Controller][build-controller], make sure this
operator is up-and-running, and then create the following:

```yml
---
apiVersion: v1
kind: Namespace
metadata:
name: shipwright-build
spec: {}

---
apiVersion: operator.shipwright.io/v1alpha1
kind: ShipwrightBuild
metadata:
name: shipwright-operator
spec:
targetNamespace: shipwright-build
namespace: default
```
It will deploy the Build-Controller in `shipwright-build` namespace. When `.spec.namespace` is not
set, it will use the `shipwright-build` namespace, this namespace needs to be created before the
actual deployment takes place.

[build-controller]: https://github.com/shipwright-io/build
4 changes: 4 additions & 0 deletions api/v1alpha1/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This package contains the CRD code, describing how the operator API will work in Kubernetes. When
// the contents of this package are modified, you must run `make` command to make sure files with
// `zz_generated.` prefix are updated, the additional code is generated as expected.
package v1alpha1
25 changes: 9 additions & 16 deletions api/v1alpha1/shipwrightbuild_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,20 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

// ShipwrightBuildSpec defines the desired state of ShipwrightBuild
// ShipwrightBuildSpec defines the configuration of a Shipwright Build deployment.
type ShipwrightBuildSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

// Foo is an example field of ShipwrightBuild. Edit ShipwrightBuild_types.go to remove/update
Foo string `json:"foo,omitempty"`
// TargetNamespace is the target namespace where Shipwright's build controller will be deployed.
TargetNamespace string `json:"targetNamespace,omitempty"`
}

// ShipwrightBuildStatus defines the observed state of ShipwrightBuild
type ShipwrightBuildStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// ShipwrightBuildStatus defines the observed state of Shipwright-Build
type ShipwrightBuildStatus struct{}

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:subresource:status

// ShipwrightBuild is the Schema for the shipwrightbuilds API
// ShipwrightBuild represents the deployment of Shipwright's build controller on a Kubernetes cluster.
type ShipwrightBuild struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand All @@ -45,9 +36,11 @@ type ShipwrightBuild struct {
type ShipwrightBuildList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ShipwrightBuild `json:"items"`

Items []ShipwrightBuild `json:"items"`
}

// init registers the current Schema on the Scheme Builder during initialization.
func init() {
SchemeBuilder.Register(&ShipwrightBuild{}, &ShipwrightBuildList{})
}
2 changes: 1 addition & 1 deletion bundle/manifests/operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ spec:
apiservicedefinitions: {}
customresourcedefinitions:
owned:
- description: ShipwrightBuild is the Schema for the shipwrightbuilds API
- description: ShipwrightBuild represents the deployment of Shipwright's build controller on a Kubernetes cluster.
displayName: Shipwright Build
kind: ShipwrightBuild
name: shipwrightbuilds.operator.shipwright.io
Expand Down
10 changes: 5 additions & 5 deletions bundle/manifests/operator.shipwright.io_shipwrightbuilds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ spec:
- name: v1alpha1
schema:
openAPIV3Schema:
description: ShipwrightBuild is the Schema for the shipwrightbuilds API
description: ShipwrightBuild represents the deployment of Shipwright's build controller on a Kubernetes cluster.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
Expand All @@ -28,14 +28,14 @@ spec:
metadata:
type: object
spec:
description: ShipwrightBuildSpec defines the desired state of ShipwrightBuild
description: ShipwrightBuildSpec defines the configuration of a Shipwright Build deployment.
properties:
foo:
description: Foo is an example field of ShipwrightBuild. Edit ShipwrightBuild_types.go to remove/update
targetNamespace:
description: TargetNamespace is the target namespace where Shipwright's build controller will be deployed.
type: string
type: object
status:
description: ShipwrightBuildStatus defines the observed state of ShipwrightBuild
description: ShipwrightBuildStatus defines the observed state of Shipwright-Build
type: object
type: object
served: true
Expand Down
2 changes: 1 addition & 1 deletion cmd/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ func main() {

if err = (&controllers.ShipwrightBuildReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("ShipwrightBuild"),
Scheme: mgr.GetScheme(),
Logger: ctrl.Log.WithName("controllers").WithName("ShipwrightBuild"),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ShipwrightBuild")
os.Exit(1)
Expand Down
14 changes: 8 additions & 6 deletions config/crd/bases/operator.shipwright.io_shipwrightbuilds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ spec:
- name: v1alpha1
schema:
openAPIV3Schema:
description: ShipwrightBuild is the Schema for the shipwrightbuilds API
description: ShipwrightBuild represents the deployment of Shipwright's build
controller on a Kubernetes cluster.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
Expand All @@ -34,15 +35,16 @@ spec:
metadata:
type: object
spec:
description: ShipwrightBuildSpec defines the desired state of ShipwrightBuild
description: ShipwrightBuildSpec defines the configuration of a Shipwright
Build deployment.
properties:
foo:
description: Foo is an example field of ShipwrightBuild. Edit ShipwrightBuild_types.go
to remove/update
targetNamespace:
description: TargetNamespace is the target namespace where Shipwright's
build controller will be deployed.
type: string
type: object
status:
description: ShipwrightBuildStatus defines the observed state of ShipwrightBuild
description: ShipwrightBuildStatus defines the observed state of Shipwright-Build
type: object
type: object
served: true
Expand Down
2 changes: 1 addition & 1 deletion config/manifests/bases/operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ spec:
apiservicedefinitions: {}
customresourcedefinitions:
owned:
- description: ShipwrightBuild is the Schema for the shipwrightbuilds API
- description: ShipwrightBuild represents the deployment of Shipwright's build controller on a Kubernetes cluster.
displayName: Shipwright Build
kind: ShipwrightBuild
name: shipwrightbuilds.operator.shipwright.io
Expand Down
71 changes: 47 additions & 24 deletions controllers/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,77 @@ import (
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/shipwright-io/operator/api/v1alpha1"
"github.com/shipwright-io/operator/test"
)

// createNamespace creates the namespace informed.
func createNamespace(name string) {
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}
err := k8sClient.Get(ctx, types.NamespacedName{Name: ns.Name}, ns)
if errors.IsNotFound(err) {
err = k8sClient.Create(ctx, ns, &client.CreateOptions{})
}
o.Expect(err).NotTo(o.HaveOccurred())
}

var _ = g.Describe("Reconcile default ShipwrightBuild installation", func() {

// namespace where ShipwrightBuild instance will be located
const namespace = "namespace"
// targetNamespace namespace where shipwright Controller and dependencies will be located
const targetNamespace = "target-namespace"
// build Build instance employed during testing
var build *v1alpha1.ShipwrightBuild

g.BeforeEach(func() {
namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "shipwright-build",
},
}
err := k8sClient.Get(ctx, types.NamespacedName{Name: namespace.Name}, namespace)
if errors.IsNotFound(err) {
err = k8sClient.Create(ctx, namespace, &client.CreateOptions{})
}
o.Expect(err).NotTo(o.HaveOccurred())
// setting up the namespaces, where Shipwright Controller will be deployed
createNamespace(namespace)
createNamespace(targetNamespace)

g.By("creating a ShipwrightBuild instance")
build = &v1alpha1.ShipwrightBuild{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
Namespace: namespace,
Name: "cluster",
},
Spec: v1alpha1.ShipwrightBuildSpec{
TargetNamespace: targetNamespace,
},
Spec: v1alpha1.ShipwrightBuildSpec{},
}
err = k8sClient.Create(ctx, build, &client.CreateOptions{})
err := k8sClient.Create(ctx, build, &client.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())

// when the finalizer is in place, the deployment of manifest elements is done, and therefore
// functional testing can proceed
g.By("waiting for the finalizer to be set")
test.EventuallyContainFinalizer(ctx, k8sClient, build, FinalizerAnnotation)
})

g.AfterEach(func() {
g.By("deleting the ShipwrightBuild instance")
err := k8sClient.Get(ctx, types.NamespacedName{Name: build.Name}, build)
namespacedName := types.NamespacedName{Namespace: namespace, Name: build.Name}
err := k8sClient.Get(ctx, namespacedName, build)
if errors.IsNotFound(err) {
return
}
o.Expect(err).NotTo(o.HaveOccurred())

err = k8sClient.Delete(ctx, build, &client.DeleteOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
g.By("checking that the shipwright-build deployment has been removed")

g.By("waiting for ShipwrightBuild instance to be completely removed")
test.EventuallyRemoved(ctx, k8sClient, build)

g.By("checking that the shipwright-build-controller deployment has been removed")
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: targetNamespace,
Name: "shipwright-build-controller",
Namespace: "shipwright-build",
},
}
test.EventuallyRemoved(ctx, k8sClient, deployment)
Expand All @@ -81,7 +102,7 @@ var _ = g.Describe("Reconcile default ShipwrightBuild installation", func() {

expectedServiceAccount := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Namespace: "shipwright-build",
Namespace: targetNamespace,
Name: "shipwright-build-controller",
},
}
Expand All @@ -91,7 +112,7 @@ var _ = g.Describe("Reconcile default ShipwrightBuild installation", func() {
g.It("creates a deployment for the Shipwright build controller", func() {
expectedDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: "shipwright-build",
Namespace: targetNamespace,
Name: "shipwright-build-controller",
},
}
Expand All @@ -111,17 +132,19 @@ var _ = g.Describe("Reconcile default ShipwrightBuild installation", func() {
g.It("deletes the RBAC for the Shipwright build controller", func() {
expectedClusterRole := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "shipwright-build-controller",
Namespace: targetNamespace,
Name: "shipwright-build-controller",
},
}
expectedClusterRoleBinding := &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "shipwright-build-controller",
Namespace: targetNamespace,
Name: "shipwright-build-controller",
},
}
expectedServiceAccount := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Namespace: "shipwright-build",
Namespace: targetNamespace,
Name: "shipwright-build-controller",
},
}
Expand All @@ -144,7 +167,7 @@ var _ = g.Describe("Reconcile default ShipwrightBuild installation", func() {
g.It("deletes the deployment for the Shipwright build controller", func() {
expectedDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: "shipwright-build",
Namespace: targetNamespace,
Name: "shipwright-build-controller",
},
}
Expand Down
25 changes: 25 additions & 0 deletions controllers/result.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package controllers

import (
ctrl "sigs.k8s.io/controller-runtime"
)

// Requeue triggers a object requeue.
func Requeue() (ctrl.Result, error) {
return ctrl.Result{Requeue: true}, nil
}

// RequeueOnError triggers requeue when error is not nil.
func RequeueOnError(err error) (ctrl.Result, error) {
return ctrl.Result{}, err
}

// RequeueWithError triggers a object requeue because the informed error happend.
func RequeueWithError(err error) (ctrl.Result, error) {
return ctrl.Result{Requeue: true}, err
}

// NoRequeue all done, the object does not need reconciliation anymore.
func NoRequeue() (ctrl.Result, error) {
return ctrl.Result{Requeue: false}, nil
}
Loading

0 comments on commit a9c5e27

Please sign in to comment.