Skip to content

Commit

Permalink
Pipe Solver Solution into BundleDeployments
Browse files Browse the repository at this point in the history
Combines @perdasilva 's latest commits with a selection from @varshaprasad96 and @ankitathomas 's controller_prototype to enable the operator-controller to create bundledeployments using a (currently hard-coded) CatalogSource. Also adds additional Makefile targets to enable simpler installation of dependencies (cert-manager and rukpak).

Signed-off-by: dtfranz <dfranz@redhat.com>
  • Loading branch information
dtfranz authored and awgreene committed Jan 31, 2023
1 parent 5541671 commit 13ea200
Show file tree
Hide file tree
Showing 10 changed files with 519 additions and 23 deletions.
17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
export IMAGE_REPO ?= quay.io/operator-framework/operator-controller
export IMAGE_TAG ?= devel
export GO_BUILD_TAGS ?= upstream
export CERT_MGR_VERSION ?= v1.9.0
export WAIT_TIMEOUT ?= 60s
IMG?=$(IMAGE_REPO):$(IMAGE_TAG)

OPERATOR_CONTROLLER_NAMESPACE ?= operator-controller-system
Expand Down Expand Up @@ -99,11 +101,11 @@ build: manifests generate fmt vet ## Build manager binary.
go build -o bin/manager main.go

.PHONY: run
run: docker-build kind-cluster kind-load install deploy wait ## Build the operator-controller then deploy it into a new kind cluster.
run: docker-build kind-cluster kind-load cert-mgr rukpak install deploy wait ## Build the operator-controller then deploy it into a new kind cluster.

.PHONY: wait
wait:
kubectl wait --for=condition=Available --namespace=$(OPERATOR_CONTROLLER_NAMESPACE) deployment/operator-controller-controller-manager --timeout=60s
kubectl wait --for=condition=Available --namespace=$(OPERATOR_CONTROLLER_NAMESPACE) deployment/operator-controller-controller-manager --timeout=$(WAIT_TIMEOUT)

# If you wish built the manager image targeting other platforms you can use the --platform flag.
# (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it.
Expand Down Expand Up @@ -139,6 +141,17 @@ ifndef ignore-not-found
ignore-not-found = false
endif

## TODO dfranz: replace cert-mgr and rukpak targets with our chosen method of distribution when available
.PHONY: cert-mgr
cert-mgr: ## Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/$(CERT_MGR_VERSION)/cert-manager.yaml
kubectl wait --for=condition=Available --namespace=cert-manager deployment/cert-manager-webhook --timeout=$(WAIT_TIMEOUT)

.PHONY: rukpak
rukpak: ## Install rukpak
kubectl apply -f https://github.com/operator-framework/rukpak/releases/latest/download/rukpak.yaml
kubectl wait --for=condition=Available --namespace=rukpak-system deployment/core --timeout=$(WAIT_TIMEOUT)

.PHONY: install
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
$(KUSTOMIZE) build config/crd | kubectl apply -f -
Expand Down
11 changes: 11 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- core.rukpak.io
resources:
- bundledeployments
verbs:
- create
- get
- list
- patch
- update
- watch
- apiGroups:
- operators.operatorframework.io
resources:
Expand Down
81 changes: 77 additions & 4 deletions controllers/operator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package controllers
import (
"context"

rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
"k8s.io/apimachinery/pkg/api/equality"
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"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -41,10 +43,20 @@ type OperatorReconciler struct {
resolver *resolution.OperatorResolver
}

func NewOperatorReconciler(c client.Client, s *runtime.Scheme, r *resolution.OperatorResolver) *OperatorReconciler {
return &OperatorReconciler{
Client: c,
Scheme: s,
resolver: r,
}
}

//+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=operators.operatorframework.io,resources=operators/finalizers,verbs=update

//+kubebuilder:rbac:groups=core.rukpak.io,resources=bundledeployments,verbs=get;list;watch;create;update;patch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
Expand Down Expand Up @@ -125,8 +137,14 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha
if err != nil {
return ctrl.Result{}, err
}
// TODO(perdasilva): use bundlePath to stamp out bundle deployment
_ = bundlePath
dep, err := r.generateExpectedBundleDeployment(*op, bundlePath)
if err != nil {
return ctrl.Result{}, err
}
// Create bundleDeployment if not found or Update if needed
if err := r.ensureBundleDeployment(ctx, dep); err != nil {
return ctrl.Result{}, err
}
break
}
}
Expand All @@ -145,16 +163,71 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha
return ctrl.Result{}, nil
}

func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string) (*rukpakv1alpha1.BundleDeployment, error) {
dep := &rukpakv1alpha1.BundleDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: o.GetName(),
},
Spec: rukpakv1alpha1.BundleDeploymentSpec{
//TODO: Don't assume plain provisioner
ProvisionerClassName: "core-rukpak-io-plain",
Template: &rukpakv1alpha1.BundleTemplate{
ObjectMeta: metav1.ObjectMeta{
// TODO: Remove
Labels: map[string]string{
"app": "my-bundle",
},
},
Spec: rukpakv1alpha1.BundleSpec{
Source: rukpakv1alpha1.BundleSource{
// TODO: Don't assume image type
Type: rukpakv1alpha1.SourceTypeImage,
Image: &rukpakv1alpha1.ImageSource{
Ref: bundlePath,
},
},

//TODO: Don't assume registry provisioner
ProvisionerClassName: "core-rukpak-io-registry",
},
},
},
}

if err := ctrl.SetControllerReference(&o, dep, r.Scheme); err != nil {
return nil, err
}
return dep, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *OperatorReconciler) SetupWithManager(mgr ctrl.Manager) error {
r.resolver = resolution.NewOperatorResolver(mgr.GetClient(), resolution.HardcodedEntitySource)

err := ctrl.NewControllerManagedBy(mgr).
For(&operatorsv1alpha1.Operator{}).
Owns(&rukpakv1alpha1.BundleDeployment{}).
Complete(r)

if err != nil {
return err
}
return nil
}

func (r *OperatorReconciler) ensureBundleDeployment(ctx context.Context, desiredBundleDeployment *rukpakv1alpha1.BundleDeployment) error {
existingBundleDeployment := &rukpakv1alpha1.BundleDeployment{}
err := r.Client.Get(ctx, types.NamespacedName{Name: desiredBundleDeployment.GetName()}, existingBundleDeployment)
if err != nil {
if client.IgnoreNotFound(err) != nil {
return err
}
return r.Client.Create(ctx, desiredBundleDeployment)
}

// Check if the existing bundleDeployment's spec needs to be updated
if equality.Semantic.DeepEqual(existingBundleDeployment.Spec, desiredBundleDeployment.Spec) {
return nil
}

existingBundleDeployment.Spec = desiredBundleDeployment.Spec
return r.Client.Update(ctx, existingBundleDeployment)
}
18 changes: 13 additions & 5 deletions controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ import (

operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
"github.com/operator-framework/operator-controller/controllers"
"github.com/operator-framework/operator-controller/internal/resolution"
operatorutil "github.com/operator-framework/operator-controller/internal/util"
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
//+kubebuilder:scaffold:imports
)

Expand All @@ -63,7 +65,9 @@ var _ = BeforeSuite(func() {

By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
CRDDirectoryPaths: []string{
filepath.Join("..", "config", "crd", "bases"),
filepath.Join("..", "testdata", "crds")},
ErrorIfCRDPathMissing: true,
}

Expand All @@ -76,6 +80,9 @@ var _ = BeforeSuite(func() {
err = operatorsv1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())

err = rukpakv1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())

//+kubebuilder:scaffold:scheme

k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expand All @@ -87,10 +94,11 @@ var _ = BeforeSuite(func() {
})
Expect(err).ToNot(HaveOccurred())

err = (&controllers.OperatorReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
}).SetupWithManager(k8sManager)
err = controllers.NewOperatorReconciler(
k8sManager.GetClient(),
k8sManager.GetScheme(),
resolution.NewOperatorResolver(k8sManager.GetClient(), resolution.HardcodedEntitySource),
).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())

ctx, cancel = context.WithCancel(context.Background())
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/onsi/gomega v1.22.1
github.com/operator-framework/deppy v0.0.0-20230125110717-dc02e928470f
github.com/operator-framework/operator-registry v1.26.2
github.com/operator-framework/rukpak v0.11.0
k8s.io/apimachinery v0.25.0
k8s.io/client-go v0.25.0
sigs.k8s.io/controller-runtime v0.13.1
Expand Down Expand Up @@ -42,7 +43,7 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand Down
8 changes: 5 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
Expand Down Expand Up @@ -291,6 +291,8 @@ github.com/operator-framework/deppy v0.0.0-20230125110717-dc02e928470f h1:YxUZyQ
github.com/operator-framework/deppy v0.0.0-20230125110717-dc02e928470f/go.mod h1:JaF7sX6tn7mpXcOehYjSHiKM1Y0z09vEfC6dca4AVuo=
github.com/operator-framework/operator-registry v1.26.2 h1:kQToR/hPqdivljaRXM0olPllNIcc/GUk1VBoGwagJmk=
github.com/operator-framework/operator-registry v1.26.2/go.mod h1:Z7XIb/3ZkhBQCvMD/rJphyuY4LmU/eWpZS+o0Mm1WAk=
github.com/operator-framework/rukpak v0.11.0 h1:D2UAlYkmCl/i6zWE+yP9oIzOScVu9VwtqJKKt+dklWw=
github.com/operator-framework/rukpak v0.11.0/go.mod h1:n8oYwfz4a50Oh9hYnPgxiD2wa+xLbPq3cI67wapcL4Y=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down Expand Up @@ -336,8 +338,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
12 changes: 8 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (

operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
"github.com/operator-framework/operator-controller/controllers"
"github.com/operator-framework/operator-controller/internal/resolution"
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
//+kubebuilder:scaffold:imports
)

Expand All @@ -45,6 +47,7 @@ func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))

utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme))
utilruntime.Must(rukpakv1alpha1.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme
}

Expand Down Expand Up @@ -89,10 +92,11 @@ func main() {
os.Exit(1)
}

if err = (&controllers.OperatorReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
if err = controllers.NewOperatorReconciler(
mgr.GetClient(),
mgr.GetScheme(),
resolution.NewOperatorResolver(mgr.GetClient(), resolution.HardcodedEntitySource),
).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Operator")
os.Exit(1)
}
Expand Down
4 changes: 4 additions & 0 deletions test/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
)

var (
Expand All @@ -33,6 +34,9 @@ var _ = BeforeSuite(func() {
err := operatorv1alpha1.AddToScheme(scheme)
Expect(err).To(Not(HaveOccurred()))

err = rukpakv1alpha1.AddToScheme(scheme)
Expect(err).To(Not(HaveOccurred()))

c, err = client.New(cfg, client.Options{Scheme: scheme})
Expect(err).To(Not(HaveOccurred()))
})
20 changes: 16 additions & 4 deletions test/e2e/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
)

const (
Expand All @@ -22,11 +23,12 @@ const (
var _ = Describe("Operator Install", func() {
It("resolves the specified package with correct bundle path", func() {
var (
ctx context.Context = context.Background()
pkgName string = "prometheus"
operator *operatorv1alpha1.Operator = &operatorv1alpha1.Operator{
ctx context.Context = context.Background()
pkgName string = "prometheus"
operatorName string = fmt.Sprintf("operator-%s", rand.String(8))
operator *operatorv1alpha1.Operator = &operatorv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("operator-%s", rand.String(8)),
Name: operatorName,
},
Spec: operatorv1alpha1.OperatorSpec{
PackageName: pkgName,
Expand All @@ -48,6 +50,16 @@ var _ = Describe("Operator Install", func() {
g.Expect(operator.Status.Conditions[0].Message).To(Equal("resolution was successful"))
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())

By("eventually installing the package successfully")
Eventually(func(g Gomega) {
bd := rukpakv1alpha1.BundleDeployment{}
err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(len(bd.Status.Conditions)).To(Equal(2))
g.Expect(bd.Status.Conditions[0].Reason).To(Equal("UnpackSuccessful"))
g.Expect(bd.Status.Conditions[1].Reason).To(Equal("InstallationSucceeded"))
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())

By("deleting the Operator resource")
err = c.Delete(ctx, operator)
Expect(err).ToNot(HaveOccurred())
Expand Down
Loading

0 comments on commit 13ea200

Please sign in to comment.