Splits ingress checker into a separate controller

This commit is contained in:
Mikalai Radchuk 2022-12-14 14:42:37 +00:00 коммит произвёл Mikalai Radchuk
Родитель 39326bca8c
Коммит 556fb820b3
9 изменённых файлов: 463 добавлений и 422 удалений

Просмотреть файл

@ -26,8 +26,8 @@ import (
"github.com/Azure/ARO-RP/pkg/operator/controllers/alertwebhook"
"github.com/Azure/ARO-RP/pkg/operator/controllers/autosizednodes"
"github.com/Azure/ARO-RP/pkg/operator/controllers/banner"
"github.com/Azure/ARO-RP/pkg/operator/controllers/checker"
"github.com/Azure/ARO-RP/pkg/operator/controllers/checkers/clusterdnschecker"
"github.com/Azure/ARO-RP/pkg/operator/controllers/checkers/ingresscertificatechecker"
"github.com/Azure/ARO-RP/pkg/operator/controllers/checkers/internetchecker"
"github.com/Azure/ARO-RP/pkg/operator/controllers/checkers/serviceprincipalchecker"
"github.com/Azure/ARO-RP/pkg/operator/controllers/clusteroperatoraro"
@ -240,11 +240,6 @@ func operator(ctx context.Context, log *logrus.Entry) error {
arocli, operatorcli)).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create controller %s: %v", ingress.ControllerName, err)
}
if err = (checker.NewReconciler(
log.WithField("controller", checker.ControllerName),
arocli, kubernetescli, maocli, operatorcli, configcli, role)).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create controller %s: %v", checker.ControllerName, err)
}
if err = (serviceprincipalchecker.NewReconciler(
log.WithField("controller", serviceprincipalchecker.ControllerName),
arocli, kubernetescli, role)).SetupWithManager(mgr); err != nil {
@ -255,6 +250,11 @@ func operator(ctx context.Context, log *logrus.Entry) error {
arocli, operatorcli, role)).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create controller %s: %v", clusterdnschecker.ControllerName, err)
}
if err = (ingresscertificatechecker.NewReconciler(
log.WithField("controller", ingresscertificatechecker.ControllerName),
arocli, operatorcli, configcli, role)).SetupWithManager(mgr); err != nil {
return fmt.Errorf("unable to create controller %s: %v", ingresscertificatechecker.ControllerName, err)
}
}
if err = (internetchecker.NewReconciler(

Просмотреть файл

@ -1,16 +0,0 @@
package checker
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"errors"
)
type Checker interface {
Check(context.Context) error
Name() string
}
var errRequeue = errors.New("requeue")

Просмотреть файл

@ -1,98 +0,0 @@
package checker
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"time"
configclient "github.com/openshift/client-go/config/clientset/versioned"
machineclient "github.com/openshift/client-go/machine/clientset/versioned"
operatorclient "github.com/openshift/client-go/operator/clientset/versioned"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
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/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
checkercommon "github.com/Azure/ARO-RP/pkg/operator/controllers/checkers/common"
)
const (
ControllerName = "Checker"
)
// Reconciler runs a number of checkers
type Reconciler struct {
log *logrus.Entry
checkers []Checker
arocli aroclient.Interface
}
func NewReconciler(log *logrus.Entry, arocli aroclient.Interface, kubernetescli kubernetes.Interface, machinecli machineclient.Interface, operatorcli operatorclient.Interface, configcli configclient.Interface, role string) *Reconciler {
checkers := []Checker{
NewIngressCertificateChecker(log, arocli, operatorcli, configcli, role),
}
return &Reconciler{
log: log,
checkers: checkers,
arocli: arocli,
}
}
// This is the permissions that this controller needs to work.
// "make generate" will run kubebuilder and cause operator/deploy/staticresources/*/role.yaml to be updated
// from the annotation below.
// +kubebuilder:rbac:groups=aro.openshift.io,resources=clusters,verbs=get;list;watch
// +kubebuilder:rbac:groups=aro.openshift.io,resources=clusters/status,verbs=get;update;patch
// Reconcile will keep checking that the cluster can connect to essential services.
func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
instance, err := r.arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
if err != nil {
return reconcile.Result{}, err
}
if !instance.Spec.OperatorFlags.GetSimpleBoolean(checkercommon.ControllerEnabled) {
r.log.Debug("controller is disabled")
return reconcile.Result{}, nil
}
r.log.Debug("running")
for _, c := range r.checkers {
thisErr := c.Check(ctx)
if thisErr != nil {
// do all checks even if there is an error
err = thisErr
if thisErr != errRequeue {
r.log.Errorf("checker %s failed with %v", c.Name(), err)
}
}
}
// We always requeue here:
// * Either immediately (with rate limiting) based on the error
// when err != nil.
// * Or based on RequeueAfter when err == nil.
return reconcile.Result{RequeueAfter: time.Hour}, err
}
// SetupWithManager setup our manager
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
aroClusterPredicate := predicate.NewPredicateFuncs(func(o client.Object) bool {
return o.GetName() == arov1alpha1.SingletonClusterName
})
builder := ctrl.NewControllerManagedBy(mgr).
For(&arov1alpha1.Cluster{}, builder.WithPredicates(aroClusterPredicate))
return builder.Named(ControllerName).Complete(r)
}

Просмотреть файл

@ -1,81 +0,0 @@
// Implements a check that provides detail on potentially faulty or customised
// IngressController configurations on the default controller.
//
// Included checks are:
// - existence of custom ingress certificate
// - existence of default ingresscontroller
package checker
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
operatorv1 "github.com/openshift/api/operator/v1"
configclient "github.com/openshift/client-go/config/clientset/versioned"
operatorclient "github.com/openshift/client-go/operator/clientset/versioned"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
"github.com/Azure/ARO-RP/pkg/util/conditions"
)
type IngressCertificateChecker struct {
arocli aroclient.Interface
operatorcli operatorclient.Interface
configcli configclient.Interface
role string
}
func NewIngressCertificateChecker(log *logrus.Entry, arocli aroclient.Interface, operatorcli operatorclient.Interface, configcli configclient.Interface, role string) *IngressCertificateChecker {
return &IngressCertificateChecker{
arocli: arocli,
operatorcli: operatorcli,
configcli: configcli,
}
}
func (r *IngressCertificateChecker) Name() string {
return "IngressCertificateChecker"
}
func (r *IngressCertificateChecker) Check(ctx context.Context) error {
cond := &operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionUnknown,
Message: "",
Reason: "CheckDone",
}
cv, err := r.configcli.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
if err != nil {
cond.Message = err.Error()
cond.Reason = "CheckFailed"
return conditions.SetCondition(ctx, r.arocli, cond, r.role)
}
ingress, err := r.operatorcli.OperatorV1().IngressControllers("openshift-ingress-operator").Get(ctx, "default", metav1.GetOptions{})
if err != nil {
cond.Message = err.Error()
cond.Reason = "CheckFailed"
return conditions.SetCondition(ctx, r.arocli, cond, r.role)
}
if ingress.Spec.DefaultCertificate == nil {
cond.Status = operatorv1.ConditionFalse
cond.Message = "Ingress has no certificate yet"
} else if ingress.Spec.DefaultCertificate.Name != string(cv.Spec.ClusterID)+"-ingress" {
cond.Status = operatorv1.ConditionFalse
cond.Message = "Custom ingress certificate in use: " + ingress.Spec.DefaultCertificate.Name
} else {
cond.Status = operatorv1.ConditionTrue
cond.Message = "Default ingress certificate in use"
}
return conditions.SetCondition(ctx, r.arocli, cond, r.role)
}

Просмотреть файл

@ -1,221 +0,0 @@
package checker
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"testing"
"time"
"github.com/go-test/deep"
configv1 "github.com/openshift/api/config/v1"
operatorv1 "github.com/openshift/api/operator/v1"
configfake "github.com/openshift/client-go/config/clientset/versioned/fake"
operatorfake "github.com/openshift/client-go/operator/clientset/versioned/fake"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
arofake "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/fake"
)
func TestDefaultIngressCertificate(t *testing.T) {
ctx := context.Background()
for _, tt := range []struct {
name string
aroCluster *arov1alpha1.Cluster
clusterVersion *configv1.ClusterVersion
ingressController *operatorv1.IngressController
expectedState operatorv1.OperatorCondition
}{
{
name: "run: has default certificate",
aroCluster: &arov1alpha1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: arov1alpha1.SingletonClusterName,
},
},
clusterVersion: &configv1.ClusterVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "version",
},
Spec: configv1.ClusterVersionSpec{
ClusterID: "00000000-0000-0000-0000-000000000001",
},
},
ingressController: &operatorv1.IngressController{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "openshift-ingress-operator",
},
Spec: operatorv1.IngressControllerSpec{
DefaultCertificate: &corev1.LocalObjectReference{
Name: "00000000-0000-0000-0000-000000000001-ingress",
},
},
},
expectedState: operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionTrue,
Reason: "CheckDone",
Message: "Default ingress certificate in use",
},
},
{
name: "run: does not have default certificate",
aroCluster: &arov1alpha1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: arov1alpha1.SingletonClusterName,
},
},
clusterVersion: &configv1.ClusterVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "version",
},
Spec: configv1.ClusterVersionSpec{
ClusterID: "00000000-0000-0000-0000-000000000001",
},
},
ingressController: &operatorv1.IngressController{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "openshift-ingress-operator",
},
Spec: operatorv1.IngressControllerSpec{
DefaultCertificate: &corev1.LocalObjectReference{
Name: "fancy-custom-cert",
},
},
},
expectedState: operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionFalse,
Reason: "CheckDone",
Message: "Custom ingress certificate in use: fancy-custom-cert",
},
},
{
name: "run: does not have default certificate defined",
aroCluster: &arov1alpha1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: arov1alpha1.SingletonClusterName,
},
},
clusterVersion: &configv1.ClusterVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "version",
},
Spec: configv1.ClusterVersionSpec{
ClusterID: "00000000-0000-0000-0000-000000000001",
},
},
ingressController: &operatorv1.IngressController{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "openshift-ingress-operator",
},
Spec: operatorv1.IngressControllerSpec{
DefaultCertificate: nil,
},
},
expectedState: operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionFalse,
Reason: "CheckDone",
Message: "Ingress has no certificate yet",
},
},
{
name: "fail: no ingress controller",
aroCluster: &arov1alpha1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: arov1alpha1.SingletonClusterName,
},
},
clusterVersion: &configv1.ClusterVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "version",
},
Spec: configv1.ClusterVersionSpec{
ClusterID: "00000000-0000-0000-0000-000000000001",
},
},
expectedState: operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionUnknown,
Reason: "CheckFailed",
Message: `ingresscontrollers.operator.openshift.io "default" not found`,
},
},
{
name: "fail: no clusterversion",
aroCluster: &arov1alpha1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: arov1alpha1.SingletonClusterName,
},
},
ingressController: &operatorv1.IngressController{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "openshift-ingress-operator",
},
Spec: operatorv1.IngressControllerSpec{
DefaultCertificate: &corev1.LocalObjectReference{
Name: "fancy-custom-cert",
},
},
},
expectedState: operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionUnknown,
Reason: "CheckFailed",
Message: `clusterversions.config.openshift.io "version" not found`,
},
},
} {
arocli := arofake.NewSimpleClientset()
configcli := configfake.NewSimpleClientset()
operatorcli := operatorfake.NewSimpleClientset()
if tt.aroCluster != nil {
arocli = arofake.NewSimpleClientset(tt.aroCluster)
}
if tt.clusterVersion != nil {
configcli = configfake.NewSimpleClientset(tt.clusterVersion)
}
if tt.ingressController != nil {
operatorcli = operatorfake.NewSimpleClientset(tt.ingressController)
}
sp := NewIngressCertificateChecker(nil, arocli, operatorcli, configcli, "")
t.Run(tt.name, func(t *testing.T) {
err := sp.Check(ctx)
if err != nil {
t.Error(err)
}
cluster, err := arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
if err != nil {
t.Error(err)
}
conds := []operatorv1.OperatorCondition{}
// nil out time
for _, c := range cluster.Status.Conditions {
c.LastTransitionTime = metav1.NewTime(time.Time{})
conds = append(conds, c)
}
errs := deep.Equal(conds, []operatorv1.OperatorCondition{tt.expectedState})
for _, err := range errs {
t.Error(err)
}
})
}
}

Просмотреть файл

@ -0,0 +1,71 @@
// Implements a check that provides detail on potentially faulty or customised
// IngressController configurations on the default controller.
//
// Included checks are:
// - existence of custom ingress certificate
// - existence of default ingresscontroller
package ingresscertificatechecker
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"errors"
"fmt"
configclient "github.com/openshift/client-go/config/clientset/versioned"
operatorclient "github.com/openshift/client-go/operator/clientset/versioned"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var (
// errNoDefaultCertificate means a cluster has no default cert reference.
// This can happen because of the following reasons:
// 1. A cluster doesn't use a managed domain.
// For example it was created with a custom domain)
// or in a dev env where we don't have managed domains.
// 2. When a customer changed the ingress config incorrectly.
//
// While the first is valid the second is something we should be aware of.
errNoDefaultCertificate = errors.New("ingress has no default certificate set")
)
type ingressCertificateChecker interface {
Check(ctx context.Context) error
}
type checker struct {
operatorcli operatorclient.Interface
configcli configclient.Interface
}
func newIngressCertificateChecker(operatorcli operatorclient.Interface, configcli configclient.Interface) *checker {
return &checker{
operatorcli: operatorcli,
configcli: configcli,
}
}
func (r *checker) Check(ctx context.Context) error {
cv, err := r.configcli.ConfigV1().ClusterVersions().Get(ctx, "version", metav1.GetOptions{})
if err != nil {
return err
}
ingress, err := r.operatorcli.OperatorV1().IngressControllers("openshift-ingress-operator").Get(ctx, "default", metav1.GetOptions{})
if err != nil {
return err
}
if ingress.Spec.DefaultCertificate == nil {
return errNoDefaultCertificate
}
if ingress.Spec.DefaultCertificate.Name != string(cv.Spec.ClusterID)+"-ingress" {
return fmt.Errorf("custom ingress certificate in use: %q", ingress.Spec.DefaultCertificate.Name)
}
return nil
}

Просмотреть файл

@ -0,0 +1,101 @@
package ingresscertificatechecker
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"testing"
configv1 "github.com/openshift/api/config/v1"
operatorv1 "github.com/openshift/api/operator/v1"
configfake "github.com/openshift/client-go/config/clientset/versioned/fake"
operatorfake "github.com/openshift/client-go/operator/clientset/versioned/fake"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestCheck(t *testing.T) {
ctx := context.Background()
clusterVersion := &configv1.ClusterVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "version",
},
Spec: configv1.ClusterVersionSpec{
ClusterID: configv1.ClusterID("fake-cluster-id"),
},
}
for _, tt := range []struct {
name string
ingressController *operatorv1.IngressController
clusterVersion *configv1.ClusterVersion
wantErr string
}{
{
name: "default certificate is set",
clusterVersion: clusterVersion,
ingressController: fakeIngressController(&corev1.LocalObjectReference{
Name: "fake-cluster-id-ingress",
}),
},
{
name: "unexpected certificate name",
clusterVersion: clusterVersion,
ingressController: fakeIngressController(&corev1.LocalObjectReference{
Name: "fake-custom-name-ingress",
}),
wantErr: `custom ingress certificate in use: "fake-custom-name-ingress"`,
},
{
name: "no default certificate set",
clusterVersion: clusterVersion,
ingressController: fakeIngressController(nil),
wantErr: "ingress has no default certificate set",
},
{
name: "missing IngressController",
clusterVersion: clusterVersion,
wantErr: `ingresscontrollers.operator.openshift.io "default" not found`,
},
{
name: "missing ClusterVersion",
wantErr: `clusterversions.config.openshift.io "version" not found`,
},
} {
t.Run(tt.name, func(t *testing.T) {
operatorcliFake := operatorfake.NewSimpleClientset()
if tt.ingressController != nil {
operatorcliFake.Tracker().Add(tt.ingressController)
}
configcliFake := configfake.NewSimpleClientset()
if tt.clusterVersion != nil {
configcliFake.Tracker().Add(tt.clusterVersion)
}
sp := &checker{
operatorcli: operatorcliFake,
configcli: configcliFake,
}
err := sp.Check(ctx)
if err != nil && err.Error() != tt.wantErr ||
err == nil && tt.wantErr != "" {
t.Errorf("\n%s\n !=\n%s", err, tt.wantErr)
}
})
}
}
func fakeIngressController(certificateRef *corev1.LocalObjectReference) *operatorv1.IngressController {
return &operatorv1.IngressController{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "openshift-ingress-operator",
},
Spec: operatorv1.IngressControllerSpec{
DefaultCertificate: certificateRef,
},
}
}

Просмотреть файл

@ -0,0 +1,150 @@
package ingresscertificatechecker
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"errors"
"time"
configv1 "github.com/openshift/api/config/v1"
operatorv1 "github.com/openshift/api/operator/v1"
configclient "github.com/openshift/client-go/config/clientset/versioned"
operatorclient "github.com/openshift/client-go/operator/clientset/versioned"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
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/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
aroclient "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned"
checkercommon "github.com/Azure/ARO-RP/pkg/operator/controllers/checkers/common"
"github.com/Azure/ARO-RP/pkg/util/conditions"
)
// This is the permissions that this controller needs to work.
// "make generate" will run kubebuilder and cause operator/deploy/staticresources/*/role.yaml to be updated
// from the annotation below.
// +kubebuilder:rbac:groups=aro.openshift.io,resources=clusters,verbs=get;list;watch
// +kubebuilder:rbac:groups=aro.openshift.io,resources=clusters/status,verbs=get;update;patch
const (
ControllerName = "ServicePrincipalChecker"
)
// Reconciler runs a number of checkers
type Reconciler struct {
log *logrus.Entry
role string
arocli aroclient.Interface
checker ingressCertificateChecker
}
func NewReconciler(log *logrus.Entry, arocli aroclient.Interface, operatorcli operatorclient.Interface, configcli configclient.Interface, role string) *Reconciler {
return &Reconciler{
log: log,
role: role,
arocli: arocli,
checker: newIngressCertificateChecker(operatorcli, configcli),
}
}
// Reconcile will keep checking that the cluster has a valid default IngressController configuration.
func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
instance, err := r.arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
if err != nil {
return reconcile.Result{}, err
}
if !instance.Spec.OperatorFlags.GetSimpleBoolean(checkercommon.ControllerEnabled) {
r.log.Debug("controller is disabled")
return r.reconcileDisabled(ctx)
}
r.log.Debug("running")
checkErr := r.checker.Check(ctx)
condition := r.condition(checkErr)
err = conditions.SetCondition(ctx, r.arocli, condition, r.role)
if err != nil {
return reconcile.Result{}, err
}
// In case of this error we want to set condition to False, but
// we don't want to continuously try to reconcile it as it might
// be expected config in some cases (e.g. custom domain cluster)
if errors.Is(checkErr, errNoDefaultCertificate) {
return reconcile.Result{RequeueAfter: time.Hour}, nil
}
// We always requeue here:
// * Either immediately (with rate limiting) based on the error
// when checkErr != nil.
// * Or based on RequeueAfter when err == nil.
return reconcile.Result{RequeueAfter: time.Hour}, checkErr
}
func (r *Reconciler) reconcileDisabled(ctx context.Context) (ctrl.Result, error) {
condition := &operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionUnknown,
}
return reconcile.Result{}, conditions.SetCondition(ctx, r.arocli, condition, r.role)
}
func (r *Reconciler) condition(checkErr error) *operatorv1.OperatorCondition {
if checkErr != nil {
return &operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionFalse,
Message: checkErr.Error(),
Reason: "CheckFailed",
}
}
return &operatorv1.OperatorCondition{
Type: arov1alpha1.DefaultIngressCertificate,
Status: operatorv1.ConditionTrue,
Message: "Default ingress certificate is in use",
Reason: "CheckDone",
}
}
// SetupWithManager setup our manager
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
aroClusterPredicate := predicate.NewPredicateFuncs(func(o client.Object) bool {
return o.GetName() == arov1alpha1.SingletonClusterName
})
defaultIngressControllerPredicate := predicate.NewPredicateFuncs(func(o client.Object) bool {
return o.GetNamespace() == "openshift-ingress-operator" && o.GetName() == "default"
})
clusterVersionPredicate := predicate.NewPredicateFuncs(func(o client.Object) bool {
return o.GetName() == "version"
})
builder := ctrl.NewControllerManagedBy(mgr).
For(&arov1alpha1.Cluster{}, builder.WithPredicates(aroClusterPredicate)).
Watches(
&source.Kind{Type: &operatorv1.IngressController{}},
&handler.EnqueueRequestForObject{},
builder.WithPredicates(defaultIngressControllerPredicate),
).
Watches(
&source.Kind{Type: &configv1.ClusterVersion{}},
&handler.EnqueueRequestForObject{},
builder.WithPredicates(clusterVersionPredicate),
)
return builder.Named(ControllerName).Complete(r)
}

Просмотреть файл

@ -0,0 +1,135 @@
package ingresscertificatechecker
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"errors"
"reflect"
"testing"
"time"
operatorv1 "github.com/openshift/api/operator/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1"
arofake "github.com/Azure/ARO-RP/pkg/operator/clientset/versioned/fake"
checkercommon "github.com/Azure/ARO-RP/pkg/operator/controllers/checkers/common"
"github.com/Azure/ARO-RP/pkg/util/azureclient"
"github.com/Azure/ARO-RP/pkg/util/cmp"
utillog "github.com/Azure/ARO-RP/pkg/util/log"
)
type fakeChecker func(ctx context.Context) error
func (fc fakeChecker) Check(ctx context.Context) error {
return fc(ctx)
}
func TestReconcile(t *testing.T) {
ctx := context.Background()
tests := []struct {
name string
controllerDisabled bool
checkerReturnErr error
wantConditionStatus operatorv1.ConditionStatus
wantConditionMessage string
wantErr string
wantResult reconcile.Result
}{
{
name: "no errors",
wantConditionStatus: operatorv1.ConditionTrue,
wantConditionMessage: "Default ingress certificate is in use",
wantResult: reconcile.Result{RequeueAfter: time.Hour},
},
{
name: "check failed with an error",
wantConditionStatus: operatorv1.ConditionFalse,
wantConditionMessage: "fake basic error",
checkerReturnErr: errors.New("fake basic error"),
wantErr: "fake basic error",
wantResult: reconcile.Result{RequeueAfter: time.Hour},
},
{
name: "no default cert set",
wantConditionStatus: operatorv1.ConditionFalse,
wantConditionMessage: "ingress has no default certificate set",
checkerReturnErr: errNoDefaultCertificate,
wantResult: reconcile.Result{RequeueAfter: time.Hour},
},
{
name: "controller disabled",
controllerDisabled: true,
wantConditionStatus: operatorv1.ConditionUnknown,
wantResult: reconcile.Result{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
instance := &arov1alpha1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: arov1alpha1.SingletonClusterName,
},
Spec: arov1alpha1.ClusterSpec{
AZEnvironment: azureclient.PublicCloud.Environment.Name,
OperatorFlags: arov1alpha1.OperatorFlags{
checkercommon.ControllerEnabled: "true",
},
},
}
if tt.controllerDisabled {
instance.Spec.OperatorFlags[checkercommon.ControllerEnabled] = "false"
}
arocli := arofake.NewSimpleClientset(instance)
r := &Reconciler{
log: utillog.GetLogger(),
role: "master",
checker: fakeChecker(func(ctx context.Context) error {
return tt.checkerReturnErr
}),
arocli: arocli,
}
result, err := r.Reconcile(ctx, ctrl.Request{})
if err != nil && err.Error() != tt.wantErr ||
err == nil && tt.wantErr != "" {
t.Error(err)
}
if !reflect.DeepEqual(tt.wantResult, result) {
t.Error(cmp.Diff(tt.wantResult, result))
}
instance, err = arocli.AroV1alpha1().Clusters().Get(ctx, arov1alpha1.SingletonClusterName, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
var condition *operatorv1.OperatorCondition
for i := range instance.Status.Conditions {
if instance.Status.Conditions[i].Type == arov1alpha1.DefaultIngressCertificate {
condition = &instance.Status.Conditions[i]
}
}
if condition == nil {
t.Fatal("no condition found")
}
if condition.Status != tt.wantConditionStatus {
t.Errorf(string(condition.Status))
}
if condition.Message != tt.wantConditionMessage {
t.Errorf(condition.Message)
}
})
}
}