Skip to content

Commit

Permalink
main: start controllers only on supported platforms or unsupported co…
Browse files Browse the repository at this point in the history
…ntroller instead
  • Loading branch information
damdo authored and openshift-cherrypick-robot committed Jan 31, 2024
1 parent fc98dca commit f8e1628
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 11 deletions.
26 changes: 23 additions & 3 deletions cmd/cluster-capi-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/openshift/cluster-capi-operator/pkg/controllers/cluster"
"github.com/openshift/cluster-capi-operator/pkg/controllers/kubeconfig"
"github.com/openshift/cluster-capi-operator/pkg/controllers/secretsync"
"github.com/openshift/cluster-capi-operator/pkg/controllers/unsupported"
"github.com/openshift/cluster-capi-operator/pkg/operatorstatus"
"github.com/openshift/cluster-capi-operator/pkg/util"
"github.com/openshift/cluster-capi-operator/pkg/webhook"
Expand Down Expand Up @@ -166,8 +167,27 @@ func main() {
os.Exit(1)
}

setupReconcilers(mgr, platform, containerImages, applyClient, apiextensionsClient)
setupWebhooks(mgr, platform)
// Only setup reconcile controllers and webhooks when the platform is supported.
// This avoids unnecessary CAPI providers discovery, installs and reconciles when the platform is not supported.
switch platform {
case configv1.AWSPlatformType,
configv1.GCPPlatformType,
configv1.PowerVSPlatformType,
configv1.OpenStackPlatformType:
setupReconcilers(mgr, platform, containerImages, applyClient, apiextensionsClient)
setupWebhooks(mgr, platform)
default:
klog.Infof("detected platform %q is not supported, skipping capi controllers setup", platform)

// UnsupportedController runs on unsupported platforms, it watches and keeps the cluster-api ClusterObject up to date.
if err := (&unsupported.UnsupportedController{
ClusterOperatorStatusClient: getClusterOperatorStatusClient(mgr, "cluster-capi-operator-unsupported-controller"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
klog.Error(err, "unable to create unsupported controller", "controller", "Unsupported")
os.Exit(1)
}
}

// +kubebuilder:scaffold:builder

Expand Down Expand Up @@ -274,7 +294,7 @@ func setupInfraClusterReconciler(mgr manager.Manager, platform configv1.Platform
os.Exit(1)
}
default:
klog.Info("Platform not supported, skipping infra cluster controller setup")
klog.Infof("detected platform %q is not supported, skipping InfraCluster controller setup", platform)
}
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/controllers/cluster/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (r *CoreClusterReconciler) Reconcile(ctx context.Context, req reconcile.Req
}

if !cluster.DeletionTimestamp.IsZero() {
return ctrl.Result{}, r.SetStatusAvailable(ctx)
return ctrl.Result{}, r.SetStatusAvailable(ctx, "")
}

log.Info("Reconciling core cluster")
Expand All @@ -57,5 +57,5 @@ func (r *CoreClusterReconciler) Reconcile(ctx context.Context, req reconcile.Req
}
}

return ctrl.Result{}, r.SetStatusAvailable(ctx)
return ctrl.Result{}, r.SetStatusAvailable(ctx, "")
}
4 changes: 2 additions & 2 deletions pkg/controllers/cluster/infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (r *GenericInfraClusterReconciler) Reconcile(ctx context.Context, req recon
}

if !infraClusterCopy.GetDeletionTimestamp().IsZero() {
return ctrl.Result{}, r.SetStatusAvailable(ctx)
return ctrl.Result{}, r.SetStatusAvailable(ctx, "")
}

log.Info("Reconciling infrastructure cluster")
Expand Down Expand Up @@ -87,7 +87,7 @@ func (r *GenericInfraClusterReconciler) Reconcile(ctx context.Context, req recon
}
}

return ctrl.Result{}, r.SetStatusAvailable(ctx)
return ctrl.Result{}, r.SetStatusAvailable(ctx, "")
}

func setManagedByAnnotation(annotations map[string]string) map[string]string {
Expand Down
4 changes: 2 additions & 2 deletions pkg/controllers/kubeconfig/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (r *KubeconfigReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (c

if infra.Status.PlatformStatus == nil {
log.Info("No platform status exists in infrastructure object. Skipping kubeconfig reconciliation...")
if err := r.SetStatusAvailable(ctx); err != nil {
if err := r.SetStatusAvailable(ctx, ""); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
Expand All @@ -85,7 +85,7 @@ func (r *KubeconfigReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (c
return ctrl.Result{}, err
}

return res, r.SetStatusAvailable(ctx)
return res, r.SetStatusAvailable(ctx, "")
}

func (r *KubeconfigReconciler) reconcileKubeconfig(ctx context.Context, log logr.Logger) (ctrl.Result, error) {
Expand Down
66 changes: 66 additions & 0 deletions pkg/controllers/unsupported/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unsupported

import (
"context"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/rest"
"k8s.io/klog/v2/klogr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/openshift/cluster-capi-operator/pkg/controllers"
"github.com/openshift/cluster-capi-operator/pkg/test"
)

var (
testEnv *envtest.Environment
cfg *rest.Config
cl client.Client
)

func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Controller Suite")
}

var _ = BeforeSuite(func() {
logf.SetLogger(klogr.New())

By("bootstrapping test environment")
var err error
testEnv = &envtest.Environment{}
cfg, cl, err = test.StartEnvTest(testEnv)
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
Expect(cl).NotTo(BeNil())

managedNamespace := &corev1.Namespace{}
managedNamespace.SetName(controllers.DefaultManagedNamespace)
Expect(cl.Create(context.Background(), managedNamespace)).To(Succeed())
})

var _ = AfterSuite(func() {
By("tearing down the test environment")
Expect(test.StopEnvTest(testEnv)).To(Succeed())
})
61 changes: 61 additions & 0 deletions pkg/controllers/unsupported/unsupported_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unsupported

import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/runtime"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"

configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/cluster-capi-operator/pkg/controllers"
"github.com/openshift/cluster-capi-operator/pkg/operatorstatus"
)

const (
capiUnsupportedPlatformMsg = "Cluster API is not yet implemented on this platform"
)

// UnsupportedController on unsupported platforms watches and keeps the cluster-api ClusterObject up to date.
type UnsupportedController struct {
operatorstatus.ClusterOperatorStatusClient
Scheme *runtime.Scheme
}

// Reconcile reconciles the cluster-api ClusterOperator object.
func (r *UnsupportedController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := ctrl.LoggerFrom(ctx).WithName("UnsupportedController")
log.Info(fmt.Sprintf("Reconciling %q ClusterObject", controllers.ClusterOperatorName))

if err := r.ClusterOperatorStatusClient.SetStatusAvailable(ctx, capiUnsupportedPlatformMsg); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to set conditions for %q ClusterObject: %w", controllers.ClusterOperatorName, err)
}

return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *UnsupportedController) SetupWithManager(mgr ctrl.Manager) error {
build := ctrl.NewControllerManagedBy(mgr).
For(&configv1.ClusterOperator{}, builder.WithPredicates(clusterOperatorPredicates()))

return build.Complete(r)
}
81 changes: 81 additions & 0 deletions pkg/controllers/unsupported/unsupported_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unsupported

import (
"context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/cluster-capi-operator/pkg/operatorstatus"
"github.com/openshift/cluster-capi-operator/pkg/test"
)

var _ = Describe("CAPI unsupported controller", func() {
ctx := context.Background()
var r *UnsupportedController
var capiClusterOperator *configv1.ClusterOperator
capiClusterOperatorKey := client.ObjectKey{Name: "cluster-api"}

BeforeEach(func() {
r = &UnsupportedController{
ClusterOperatorStatusClient: operatorstatus.ClusterOperatorStatusClient{
Client: cl,
},
}

capiClusterOperator = &configv1.ClusterOperator{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster-api",
},
}

Expect(cl.Create(ctx, capiClusterOperator)).To(Succeed(), "should be able to create the 'cluster-api' ClusterOperator object")
})

AfterEach(func() {
Expect(test.CleanupAndWait(ctx, cl, &configv1.ClusterOperator{})).To(Succeed())
})

It("should update cluster-api ClusterOperator status with an 'unsupported' message", func() {
_, err := r.Reconcile(ctx, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: capiClusterOperator.Name,
},
})

Expect(err).ToNot(HaveOccurred(), "should be able to reconcile the cluster-api ClusterOperator without erroring")

Eventually(func() (*configv1.ClusterOperator, error) {
err := cl.Get(ctx, capiClusterOperatorKey, capiClusterOperator)
return capiClusterOperator, err
}).Should(HaveField("Status.Conditions",
SatisfyAll(
ContainElement(And(HaveField("Type", Equal(configv1.OperatorAvailable)), HaveField("Status", Equal(configv1.ConditionTrue)), HaveField("Message", Equal(capiUnsupportedPlatformMsg)))),
ContainElement(And(HaveField("Type", Equal(configv1.OperatorProgressing)), HaveField("Status", Equal(configv1.ConditionFalse)))),
ContainElement(And(HaveField("Type", Equal(configv1.OperatorDegraded)), HaveField("Status", Equal(configv1.ConditionFalse)))),
),
), "should match the expected ClusterOperator status conditions")
})

})
41 changes: 41 additions & 0 deletions pkg/controllers/unsupported/watch_predicates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unsupported

import (
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"

configv1 "github.com/openshift/api/config/v1"
"github.com/openshift/cluster-capi-operator/pkg/controllers"
)

// clusterOperatorPredicates defines a predicate function for the cluster-api ClusterOperator.
func clusterOperatorPredicates() predicate.Funcs {
isClusterOperator := func(obj runtime.Object) bool {
clusterOperator, ok := obj.(*configv1.ClusterOperator)
return ok && clusterOperator.GetName() == controllers.ClusterOperatorName
}

return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool { return isClusterOperator(e.Object) },
UpdateFunc: func(e event.UpdateEvent) bool { return isClusterOperator(e.ObjectNew) },
GenericFunc: func(e event.GenericEvent) bool { return isClusterOperator(e.Object) },
DeleteFunc: func(e event.DeleteEvent) bool { return isClusterOperator(e.Object) },
}
}
8 changes: 6 additions & 2 deletions pkg/operatorstatus/operator_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type ClusterOperatorStatusClient struct {

// setStatusAvailable sets the Available condition to True, with the given reason
// and message, and sets both the Progressing and Degraded conditions to False.
func (r *ClusterOperatorStatusClient) SetStatusAvailable(ctx context.Context) error {
func (r *ClusterOperatorStatusClient) SetStatusAvailable(ctx context.Context, availableConditionMsg string) error {
log := ctrl.LoggerFrom(ctx)

co, err := r.GetOrCreateClusterOperator(ctx)
Expand All @@ -44,9 +44,13 @@ func (r *ClusterOperatorStatusClient) SetStatusAvailable(ctx context.Context) er
return err
}

if availableConditionMsg == "" {
availableConditionMsg = fmt.Sprintf("Cluster CAPI Operator is available at %s", r.ReleaseVersion)
}

conds := []configv1.ClusterOperatorStatusCondition{
NewClusterOperatorStatusCondition(configv1.OperatorAvailable, configv1.ConditionTrue, ReasonAsExpected,
fmt.Sprintf("Cluster CAPI Operator is available at %s", r.ReleaseVersion)),
availableConditionMsg),
NewClusterOperatorStatusCondition(configv1.OperatorProgressing, configv1.ConditionFalse, ReasonAsExpected, ""),
NewClusterOperatorStatusCondition(configv1.OperatorDegraded, configv1.ConditionFalse, ReasonAsExpected, ""),
NewClusterOperatorStatusCondition(configv1.OperatorUpgradeable, configv1.ConditionTrue, ReasonAsExpected, ""),
Expand Down

0 comments on commit f8e1628

Please sign in to comment.