diff --git a/go.mod b/go.mod index 8e7b985f3..b66a33401 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/openshift/api v0.0.0-20211209135129-c58d9f695577 github.com/openshift/build-machinery-go v0.0.0-20211213093930-7e33a7eb4ce3 github.com/openshift/client-go v0.0.0-20211209144617-7385dd6338e3 - github.com/openshift/library-go v0.0.0-20211220195323-eca2c467c492 + github.com/openshift/library-go v0.0.0-20220128121806-335050b25f52 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/common v0.28.0 diff --git a/go.sum b/go.sum index ec7f31229..ebb16e731 100644 --- a/go.sum +++ b/go.sum @@ -518,8 +518,8 @@ github.com/openshift/build-machinery-go v0.0.0-20211213093930-7e33a7eb4ce3 h1:65 github.com/openshift/build-machinery-go v0.0.0-20211213093930-7e33a7eb4ce3/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= github.com/openshift/client-go v0.0.0-20211209144617-7385dd6338e3 h1:SG1aqwleU6bGD0X4mhkTNupjVnByMYYuW4XbnCPavQU= github.com/openshift/client-go v0.0.0-20211209144617-7385dd6338e3/go.mod h1:cwhyki5lqBmrT0m8Im+9I7PGFaraOzcYPtEz93RcsGY= -github.com/openshift/library-go v0.0.0-20211220195323-eca2c467c492 h1:oj/rSQqVWVj6YJUydZwLz2frrJreiyI4oa9g/YPgMsM= -github.com/openshift/library-go v0.0.0-20211220195323-eca2c467c492/go.mod h1:4UQ9snU1vg53fyTpHQw3vLPiAxI8ub5xrc+y8KPQQFs= +github.com/openshift/library-go v0.0.0-20220128121806-335050b25f52 h1:w7Rc4Z2yEadKIQg68pALWP2U+itCRNe5hGK4hWG9VlA= +github.com/openshift/library-go v0.0.0-20220128121806-335050b25f52/go.mod h1:6AmNM4N4nHftckybV/U7bQW+5AvK5TW81ndSI6KEidw= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= diff --git a/vendor/github.com/openshift/library-go/pkg/config/leaderelection/leaderelection.go b/vendor/github.com/openshift/library-go/pkg/config/leaderelection/leaderelection.go index 5cec68257..edbd10fb1 100644 --- a/vendor/github.com/openshift/library-go/pkg/config/leaderelection/leaderelection.go +++ b/vendor/github.com/openshift/library-go/pkg/config/leaderelection/leaderelection.go @@ -22,8 +22,15 @@ import ( configv1 "github.com/openshift/api/config/v1" ) -// ToConfigMapLeaderElection returns a leader election config that you just need to fill in the Callback for. Don't forget the callbacks! -func ToConfigMapLeaderElection(clientConfig *rest.Config, config configv1.LeaderElection, component, identity string) (leaderelection.LeaderElectionConfig, error) { +// ToLeaderElectionWithConfigmapLease returns a "configmapsleases" based leader +// election config that you just need to fill in the Callback for. +// It is compatible with a "configmaps" based leader election and +// paves the way toward using "leases" based leader election. +// See https://github.com/kubernetes/kubernetes/issues/107454 for +// details on how to migrate to "leases" leader election. +// Don't forget the callbacks! +// TODO: In the next version we should switch to using "leases" +func ToLeaderElectionWithConfigmapLease(clientConfig *rest.Config, config configv1.LeaderElection, component, identity string) (leaderelection.LeaderElectionConfig, error) { kubeClient, err := kubernetes.NewForConfig(clientConfig) if err != nil { return leaderelection.LeaderElectionConfig{}, err @@ -50,7 +57,7 @@ func ToConfigMapLeaderElection(clientConfig *rest.Config, config configv1.Leader eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(kubeClient.CoreV1().RESTClient()).Events("")}) eventRecorder := eventBroadcaster.NewRecorder(clientgoscheme.Scheme, corev1.EventSource{Component: component}) rl, err := resourcelock.New( - resourcelock.ConfigMapsResourceLock, + resourcelock.ConfigMapsLeasesResourceLock, config.Namespace, config.Name, kubeClient.CoreV1(), diff --git a/vendor/github.com/openshift/library-go/pkg/controller/controllercmd/builder.go b/vendor/github.com/openshift/library-go/pkg/controller/controllercmd/builder.go index f98b86f3d..820892a17 100644 --- a/vendor/github.com/openshift/library-go/pkg/controller/controllercmd/builder.go +++ b/vendor/github.com/openshift/library-go/pkg/controller/controllercmd/builder.go @@ -329,7 +329,7 @@ func (b *ControllerBuilder) Run(ctx context.Context, config *unstructured.Unstru leaderConfig := rest.CopyConfig(protoConfig) leaderConfig.Timeout = b.leaderElection.RenewDeadline.Duration - leaderElection, err := leaderelectionconverter.ToConfigMapLeaderElection(leaderConfig, *b.leaderElection, b.componentName, b.instanceIdentity) + leaderElection, err := leaderelectionconverter.ToLeaderElectionWithConfigmapLease(leaderConfig, *b.leaderElection, b.componentName, b.instanceIdentity) if err != nil { return err } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/admissionregistration.go b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/admissionregistration.go index 67b6a615d..fafa39c40 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/admissionregistration.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/admissionregistration.go @@ -7,50 +7,60 @@ import ( "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/resource/resourcemerge" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - admissionregistrationclientv1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1" - "k8s.io/klog/v2" - + "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + admissionregistrationclientv1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1" + "k8s.io/klog/v2" ) -// ApplyMutatingWebhookConfiguration ensures the form of the specified +// ApplyMutatingWebhookConfigurationImproved ensures the form of the specified // mutatingwebhookconfiguration is present in the API. If it does not exist, // it will be created. If it does exist, the metadata of the required // mutatingwebhookconfiguration will be merged with the existing mutatingwebhookconfiguration // and an update performed if the mutatingwebhookconfiguration spec and metadata differ from // the previously required spec and metadata based on generation change. -func ApplyMutatingWebhookConfiguration(ctx context.Context, client admissionregistrationclientv1.MutatingWebhookConfigurationsGetter, recorder events.Recorder, - requiredOriginal *admissionregistrationv1.MutatingWebhookConfiguration) (*admissionregistrationv1.MutatingWebhookConfiguration, bool, error) { +func ApplyMutatingWebhookConfigurationImproved(ctx context.Context, client admissionregistrationclientv1.MutatingWebhookConfigurationsGetter, recorder events.Recorder, + requiredOriginal *admissionregistrationv1.MutatingWebhookConfiguration, cache ResourceCache) (*admissionregistrationv1.MutatingWebhookConfiguration, bool, error) { if requiredOriginal == nil { return nil, false, fmt.Errorf("Unexpected nil instead of an object") } - required := requiredOriginal.DeepCopy() - existing, err := client.MutatingWebhookConfigurations().Get(ctx, required.GetName(), metav1.GetOptions{}) + existing, err := client.MutatingWebhookConfigurations().Get(ctx, requiredOriginal.GetName(), metav1.GetOptions{}) if apierrors.IsNotFound(err) { + required := requiredOriginal.DeepCopy() actual, err := client.MutatingWebhookConfigurations().Create( ctx, resourcemerge.WithCleanLabelsAndAnnotations(required).(*admissionregistrationv1.MutatingWebhookConfiguration), metav1.CreateOptions{}) reportCreateEvent(recorder, required, err) if err != nil { return nil, false, err } + // need to store the original so that the early comparison of hashes is done based on the original, not a mutated copy + cache.UpdateCachedResourceMetadata(requiredOriginal, actual) return actual, true, nil } else if err != nil { return nil, false, err } + if cache.SafeToSkipApply(requiredOriginal, existing) { + return existing, false, nil + } + + required := requiredOriginal.DeepCopy() modified := resourcemerge.BoolPtr(false) existingCopy := existing.DeepCopy() resourcemerge.EnsureObjectMeta(modified, &existingCopy.ObjectMeta, required.ObjectMeta) - if !*modified { + copyMutatingWebhookCABundle(existing, required) + webhooksEquivalent := equality.Semantic.DeepEqual(existingCopy.Webhooks, required.Webhooks) + if webhooksEquivalent && !*modified { + // need to store the original so that the early comparison of hashes is done based on the original, not a mutated copy + cache.UpdateCachedResourceMetadata(requiredOriginal, existingCopy) return existingCopy, false, nil } // at this point we know that we're going to perform a write. We're just trying to get the object correct toWrite := existingCopy // shallow copy so the code reads easier - copyMutatingWebhookCABundle(existing, required) toWrite.Webhooks = required.Webhooks klog.V(4).Infof("MutatingWebhookConfiguration %q changes: %v", required.GetNamespace()+"/"+required.GetName(), JSONPatchNoError(existing, toWrite)) @@ -60,7 +70,9 @@ func ApplyMutatingWebhookConfiguration(ctx context.Context, client admissionregi if err != nil { return nil, false, err } - return actual, *modified || actual.GetGeneration() > existingCopy.GetGeneration(), nil + // need to store the original so that the early comparison of hashes is done based on the original, not a mutated copy + cache.UpdateCachedResourceMetadata(requiredOriginal, actual) + return actual, true, nil } // copyMutatingWebhookCABundle populates webhooks[].clientConfig.caBundle fields from existing resource if it was set before @@ -78,42 +90,52 @@ func copyMutatingWebhookCABundle(from, to *admissionregistrationv1.MutatingWebho } } -// ApplyValidatingWebhookConfiguration ensures the form of the specified +// ApplyValidatingWebhookConfigurationImproved ensures the form of the specified // validatingwebhookconfiguration is present in the API. If it does not exist, // it will be created. If it does exist, the metadata of the required // validatingwebhookconfiguration will be merged with the existing validatingwebhookconfiguration // and an update performed if the validatingwebhookconfiguration spec and metadata differ from // the previously required spec and metadata based on generation change. -func ApplyValidatingWebhookConfiguration(ctx context.Context, client admissionregistrationclientv1.ValidatingWebhookConfigurationsGetter, recorder events.Recorder, - requiredOriginal *admissionregistrationv1.ValidatingWebhookConfiguration) (*admissionregistrationv1.ValidatingWebhookConfiguration, bool, error) { +func ApplyValidatingWebhookConfigurationImproved(ctx context.Context, client admissionregistrationclientv1.ValidatingWebhookConfigurationsGetter, recorder events.Recorder, + requiredOriginal *admissionregistrationv1.ValidatingWebhookConfiguration, cache ResourceCache) (*admissionregistrationv1.ValidatingWebhookConfiguration, bool, error) { if requiredOriginal == nil { return nil, false, fmt.Errorf("Unexpected nil instead of an object") } - required := requiredOriginal.DeepCopy() - existing, err := client.ValidatingWebhookConfigurations().Get(ctx, required.GetName(), metav1.GetOptions{}) + existing, err := client.ValidatingWebhookConfigurations().Get(ctx, requiredOriginal.GetName(), metav1.GetOptions{}) if apierrors.IsNotFound(err) { + required := requiredOriginal.DeepCopy() actual, err := client.ValidatingWebhookConfigurations().Create( ctx, resourcemerge.WithCleanLabelsAndAnnotations(required).(*admissionregistrationv1.ValidatingWebhookConfiguration), metav1.CreateOptions{}) reportCreateEvent(recorder, required, err) if err != nil { return nil, false, err } + // need to store the original so that the early comparison of hashes is done based on the original, not a mutated copy + cache.UpdateCachedResourceMetadata(requiredOriginal, actual) return actual, true, nil } else if err != nil { return nil, false, err } + if cache.SafeToSkipApply(requiredOriginal, existing) { + return existing, false, nil + } + + required := requiredOriginal.DeepCopy() modified := resourcemerge.BoolPtr(false) existingCopy := existing.DeepCopy() resourcemerge.EnsureObjectMeta(modified, &existingCopy.ObjectMeta, required.ObjectMeta) - if !*modified { + copyValidatingWebhookCABundle(existing, required) + webhooksEquivalent := equality.Semantic.DeepEqual(existingCopy.Webhooks, required.Webhooks) + if webhooksEquivalent && !*modified { + // need to store the original so that the early comparison of hashes is done based on the original, not a mutated copy + cache.UpdateCachedResourceMetadata(requiredOriginal, existingCopy) return existingCopy, false, nil } // at this point we know that we're going to perform a write. We're just trying to get the object correct toWrite := existingCopy // shallow copy so the code reads easier - copyValidatingWebhookCABundle(existing, required) toWrite.Webhooks = required.Webhooks klog.V(4).Infof("ValidatingWebhookConfiguration %q changes: %v", required.GetNamespace()+"/"+required.GetName(), JSONPatchNoError(existing, toWrite)) @@ -123,7 +145,9 @@ func ApplyValidatingWebhookConfiguration(ctx context.Context, client admissionre if err != nil { return nil, false, err } - return actual, *modified || actual.GetGeneration() > existingCopy.GetGeneration(), nil + // need to store the original so that the early comparison of hashes is done based on the original, not a mutated copy + cache.UpdateCachedResourceMetadata(requiredOriginal, actual) + return actual, true, nil } // copyValidatingWebhookCABundle populates webhooks[].clientConfig.caBundle fields from existing resource if it was set before diff --git a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/generic.go b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/generic.go index c0a9fc8f4..6f654250e 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/generic.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/resource/resourceapply/generic.go @@ -206,13 +206,13 @@ func ApplyDirectly(ctx context.Context, clients *ClientHolder, recorder events.R if clients.kubeClient == nil { result.Error = fmt.Errorf("missing kubeClient") } else { - result.Result, result.Changed, result.Error = ApplyValidatingWebhookConfiguration(ctx, clients.kubeClient.AdmissionregistrationV1(), recorder, t) + result.Result, result.Changed, result.Error = ApplyValidatingWebhookConfigurationImproved(ctx, clients.kubeClient.AdmissionregistrationV1(), recorder, t, cache) } case *admissionregistrationv1.MutatingWebhookConfiguration: if clients.kubeClient == nil { result.Error = fmt.Errorf("missing kubeClient") } else { - result.Result, result.Changed, result.Error = ApplyMutatingWebhookConfiguration(ctx, clients.kubeClient.AdmissionregistrationV1(), recorder, t) + result.Result, result.Changed, result.Error = ApplyMutatingWebhookConfigurationImproved(ctx, clients.kubeClient.AdmissionregistrationV1(), recorder, t, cache) } case *storagev1.CSIDriver: if clients.kubeClient == nil { diff --git a/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controller/guard/guard_controller.go b/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controller/guard/guard_controller.go index ca47c78d8..d83de4a1a 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controller/guard/guard_controller.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controller/guard/guard_controller.go @@ -8,6 +8,7 @@ import ( "strconv" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -22,7 +23,7 @@ import ( "k8s.io/klog/v2" configv1 "github.com/openshift/api/config/v1" - configv1listers "github.com/openshift/client-go/config/listers/config/v1" + configv1informers "github.com/openshift/client-go/config/informers/externalversions/config/v1" "github.com/openshift/library-go/pkg/controller/factory" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/resource/resourceapply" @@ -37,6 +38,7 @@ type GuardController struct { targetNamespace, podResourcePrefix string operatorName string readyzPort string + operandPodLabelSelector labels.Selector nodeLister corelisterv1.NodeLister podLister corelisterv1.PodLister @@ -46,11 +48,13 @@ type GuardController struct { // installerPodImageFn returns the image name for the installer pod installerPodImageFn func() string - createConditionalFunc func() (bool, error) + createConditionalFunc func() (bool, bool, error) } func NewGuardController( - targetNamespace, podResourcePrefix string, + targetNamespace string, + operandPodLabelSelector labels.Selector, + podResourcePrefix string, operatorName string, readyzPort string, kubeInformersForTargetNamespace informers.SharedInformerFactory, @@ -59,26 +63,34 @@ func NewGuardController( podGetter corev1client.PodsGetter, pdbGetter policyclientv1.PodDisruptionBudgetsGetter, eventRecorder events.Recorder, - createConditionalFunc func() (bool, error), -) factory.Controller { + createConditionalFunc func() (bool, bool, error), +) (factory.Controller, error) { + if operandPodLabelSelector == nil { + return nil, fmt.Errorf("GuardController: missing required operandPodLabelSelector") + } + if operandPodLabelSelector.Empty() { + return nil, fmt.Errorf("GuardController: operandPodLabelSelector cannot be empty") + } + c := &GuardController{ - targetNamespace: targetNamespace, - podResourcePrefix: podResourcePrefix, - operatorName: operatorName, - readyzPort: readyzPort, - nodeLister: kubeInformersClusterScoped.Core().V1().Nodes().Lister(), - podLister: kubeInformersForTargetNamespace.Core().V1().Pods().Lister(), - podGetter: podGetter, - pdbGetter: pdbGetter, - pdbLister: kubeInformersForTargetNamespace.Policy().V1().PodDisruptionBudgets().Lister(), - installerPodImageFn: getInstallerPodImageFromEnv, - createConditionalFunc: createConditionalFunc, + targetNamespace: targetNamespace, + operandPodLabelSelector: operandPodLabelSelector, + podResourcePrefix: podResourcePrefix, + operatorName: operatorName, + readyzPort: readyzPort, + nodeLister: kubeInformersClusterScoped.Core().V1().Nodes().Lister(), + podLister: kubeInformersForTargetNamespace.Core().V1().Pods().Lister(), + podGetter: podGetter, + pdbGetter: pdbGetter, + pdbLister: kubeInformersForTargetNamespace.Policy().V1().PodDisruptionBudgets().Lister(), + installerPodImageFn: getInstallerPodImageFromEnv, + createConditionalFunc: createConditionalFunc, } return factory.New().WithInformers( kubeInformersForTargetNamespace.Core().V1().Pods().Informer(), kubeInformersClusterScoped.Core().V1().Nodes().Informer(), - ).WithSync(c.sync).WithSyncDegradedOnError(operatorClient).ToController("GuardController", eventRecorder) + ).WithSync(c.sync).WithSyncDegradedOnError(operatorClient).ToController("GuardController", eventRecorder), nil } func getInstallerPodImageFromEnv() string { @@ -103,16 +115,30 @@ func nodeConditionFinder(status *corev1.NodeStatus, condType corev1.NodeConditio return nil } +func nodeHasUnschedulableTaint(node *corev1.Node) bool { + for _, taint := range node.Spec.Taints { + if taint.Effect == corev1.TaintEffectNoSchedule && taint.Key == corev1.TaintNodeUnschedulable { + return true + } + } + return false +} + func (c *GuardController) sync(ctx context.Context, syncCtx factory.SyncContext) error { klog.V(5).Info("Syncing guards") if c.createConditionalFunc == nil { - return fmt.Errorf("create conditional not set") + return fmt.Errorf("createConditionalFunc not set") } - shouldCreate, err := c.createConditionalFunc() + shouldCreate, precheckSucceeded, err := c.createConditionalFunc() if err != nil { - return fmt.Errorf("create conditional returns an error: %v", err) + return fmt.Errorf("createConditionalFunc returns an error: %v", err) + } + + if !precheckSucceeded { + klog.V(4).Infof("create conditional precheck did not succeed, skipping") + return nil } errs := []error{} @@ -162,7 +188,7 @@ func (c *GuardController) sync(ctx context.Context, syncCtx factory.SyncContext) return err } - pods, err := c.podLister.Pods(c.targetNamespace).List(labels.SelectorFromSet(labels.Set{"app": c.podResourcePrefix})) + pods, err := c.podLister.Pods(c.targetNamespace).List(c.operandPodLabelSelector) if err != nil { return err } @@ -177,25 +203,24 @@ func (c *GuardController) sync(ctx context.Context, syncCtx factory.SyncContext) pdb.Spec.MinAvailable = &minAvailable } - // List the pdb from the cache in case it exists and there's nothing to update - // so no Get request is executed. - pdbs, err := c.pdbLister.PodDisruptionBudgets(c.targetNamespace).List(labels.Everything()) - if err != nil { - klog.Errorf("Unable to list PodDisruptionBudgets: %v", err) - return err - } - - for _, pdbItem := range pdbs { - if pdbItem.Name == pdb.Name { - if pdbItem.Spec.MinAvailable != pdb.Spec.MinAvailable { - _, _, err = resourceapply.ApplyPodDisruptionBudget(ctx, c.pdbGetter, syncCtx.Recorder(), pdb) - if err != nil { - klog.Errorf("Unable to apply PodDisruptionBudget changes: %v", err) - return err - } + pdbObj, err := c.pdbLister.PodDisruptionBudgets(pdb.Namespace).Get(pdb.Name) + if err == nil { + if pdbObj.Spec.MinAvailable != pdb.Spec.MinAvailable { + _, _, err = resourceapply.ApplyPodDisruptionBudget(ctx, c.pdbGetter, syncCtx.Recorder(), pdb) + if err != nil { + klog.Errorf("Unable to apply PodDisruptionBudget changes: %v", err) + return fmt.Errorf("Unable to apply PodDisruptionBudget changes: %v", err) } - break } + } else if errors.IsNotFound(err) { + _, _, err = resourceapply.ApplyPodDisruptionBudget(ctx, c.pdbGetter, syncCtx.Recorder(), pdb) + if err != nil { + klog.Errorf("Unable to create PodDisruptionBudget: %v", err) + return fmt.Errorf("Unable to create PodDisruptionBudget: %v", err) + } + } else { + klog.Errorf("Unable to get PodDisruptionBudget: %v", err) + return err } operands := map[string]*corev1.Pod{} @@ -204,6 +229,12 @@ func (c *GuardController) sync(ctx context.Context, syncCtx factory.SyncContext) } for _, node := range nodes { + // Check whether the node is schedulable + if nodeHasUnschedulableTaint(node) { + klog.Infof("Node %v not schedulable, skipping reconciling the guard pod", node.Name) + continue + } + if _, exists := operands[node.Name]; !exists { // If the operand does not exist and the node is not ready, wait until the node becomes ready nodeReadyCondition := nodeConditionFinder(&node.Status, corev1.NodeReady) @@ -276,16 +307,24 @@ func (c *GuardController) sync(ctx context.Context, syncCtx factory.SyncContext) return utilerrors.NewAggregate(errs) } -func IsSNOCheckFnc(infraLister configv1listers.InfrastructureLister) func() (bool, error) { - return func() (bool, error) { - infraData, err := infraLister.Get("cluster") +// IsSNOCheckFnc creates a function that checks if the topology is SNO +// In case the err is nil, precheckSucceeded signifies whether the isSNO is valid. +// If precheckSucceeded is false, the isSNO return value does not reflect the cluster topology +// and defaults to the bool default value. +func IsSNOCheckFnc(infraInformer configv1informers.InfrastructureInformer) func() (isSNO, precheckSucceeded bool, err error) { + return func() (isSNO, precheckSucceeded bool, err error) { + if !infraInformer.Informer().HasSynced() { + // Do not return transient error + return false, false, nil + } + infraData, err := infraInformer.Lister().Get("cluster") if err != nil { - return false, fmt.Errorf("Unable to list infrastructures.config.openshift.io/cluster object, unable to determine topology mode") + return false, true, fmt.Errorf("Unable to list infrastructures.config.openshift.io/cluster object, unable to determine topology mode") } if infraData.Status.ControlPlaneTopology == "" { - return false, fmt.Errorf("ControlPlaneTopology was not set, unable to determine topology mode") + return false, true, fmt.Errorf("ControlPlaneTopology was not set, unable to determine topology mode") } - return infraData.Status.ControlPlaneTopology == configv1.SingleReplicaTopologyMode, nil + return infraData.Status.ControlPlaneTopology == configv1.SingleReplicaTopologyMode, true, nil } } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controller/staticpodfallback/static_pod_fallback_condition.go b/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controller/staticpodfallback/static_pod_fallback_condition.go index 5cca9bc4f..0edd27448 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controller/staticpodfallback/static_pod_fallback_condition.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controller/staticpodfallback/static_pod_fallback_condition.go @@ -31,14 +31,20 @@ func New(targetNamespace string, operatorClient operatorv1helpers.OperatorClient, kubeInformersForNamespaces operatorv1helpers.KubeInformersForNamespaces, startupMonitorEnabledFn func() (bool, error), - eventRecorder events.Recorder) factory.Controller { + eventRecorder events.Recorder) (factory.Controller, error) { + if podLabelSelector == nil { + return nil, fmt.Errorf("StaticPodFallbackConditionController: missing required podLabelSelector") + } + if podLabelSelector.Empty() { + return nil, fmt.Errorf("StaticPodFallbackConditionController: podLabelSelector cannot be empty") + } fd := &staticPodFallbackConditionController{ operatorClient: operatorClient, podLabelSelector: podLabelSelector, podLister: kubeInformersForNamespaces.InformersFor(targetNamespace).Core().V1().Pods().Lister().Pods(targetNamespace), startupMonitorEnabledFn: startupMonitorEnabledFn, } - return factory.New().WithSync(fd.sync).ResyncEvery(6*time.Minute).WithInformers(kubeInformersForNamespaces.InformersFor(targetNamespace).Core().V1().Pods().Informer()).ToController("StaticPodStateFallback", eventRecorder) + return factory.New().WithSync(fd.sync).ResyncEvery(6*time.Minute).WithInformers(kubeInformersForNamespaces.InformersFor(targetNamespace).Core().V1().Pods().Informer()).ToController("StaticPodStateFallback", eventRecorder), nil } // sync sets/unsets a StaticPodFallbackRevisionDegraded condition if a pod that matches the given label selector is annotated with FallbackForRevision diff --git a/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controllers.go b/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controllers.go index 00025e375..faf650e58 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controllers.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/staticpod/controllers.go @@ -68,7 +68,7 @@ type staticPodOperatorControllerBuilder struct { operatorName string operatorNamespace string readyzPort string - guardCreateConditionalFunc func() (bool, error) + guardCreateConditionalFunc func() (bool, bool, error) } func NewBuilder( @@ -80,7 +80,6 @@ func NewBuilder( staticPodOperatorClient: staticPodOperatorClient, kubeClient: kubeClient, kubeInformers: kubeInformers, - enableStartMonitor: func() (bool, error) { return false, nil }, } } @@ -88,17 +87,18 @@ func NewBuilder( type Builder interface { WithEvents(eventRecorder events.Recorder) Builder WithVersioning(operandName string, versionRecorder status.VersionGetter) Builder + WithOperandPodLabelSelector(labelSelector labels.Selector) Builder WithRevisionedResources(operandNamespace, staticPodName string, revisionConfigMaps, revisionSecrets []revisioncontroller.RevisionResource) Builder WithUnrevisionedCerts(certDir string, certConfigMaps, certSecrets []installer.UnrevisionedResource) Builder WithInstaller(command []string) Builder WithMinReadyDuration(minReadyDuration time.Duration) Builder - WithStartupMonitor(enabledStartupMonitor func() (bool, error), operandPodLabelSelector labels.Selector) Builder + WithStartupMonitor(enabledStartupMonitor func() (bool, error)) Builder // WithCustomInstaller allows mutating the installer pod definition just before // the installer pod is created for a revision. WithCustomInstaller(command []string, installerPodMutationFunc installer.InstallerPodMutationFunc) Builder WithPruning(command []string, staticPodPrefix string) Builder - WithPodDisruptionBudgetGuard(operatorNamespace, operatorName, readyzPort string, createConditionalFunc func() (bool, error)) Builder + WithPodDisruptionBudgetGuard(operatorNamespace, operatorName, readyzPort string, createConditionalFunc func() (bool, bool, error)) Builder ToControllers() (manager.ControllerManager, error) } @@ -113,6 +113,11 @@ func (b *staticPodOperatorControllerBuilder) WithVersioning(operandName string, return b } +func (b *staticPodOperatorControllerBuilder) WithOperandPodLabelSelector(labelSelector labels.Selector) Builder { + b.operandPodLabelSelector = labelSelector + return b +} + func (b *staticPodOperatorControllerBuilder) WithRevisionedResources(operandNamespace, staticPodName string, revisionConfigMaps, revisionSecrets []revisioncontroller.RevisionResource) Builder { b.operandNamespace = operandNamespace b.staticPodName = staticPodName @@ -141,9 +146,8 @@ func (b *staticPodOperatorControllerBuilder) WithMinReadyDuration(minReadyDurati return b } -func (b *staticPodOperatorControllerBuilder) WithStartupMonitor(enabledStartupMonitor func() (bool, error), operandPodLabelSelector labels.Selector) Builder { +func (b *staticPodOperatorControllerBuilder) WithStartupMonitor(enabledStartupMonitor func() (bool, error)) Builder { b.enableStartMonitor = enabledStartupMonitor - b.operandPodLabelSelector = operandPodLabelSelector return b } @@ -161,7 +165,7 @@ func (b *staticPodOperatorControllerBuilder) WithPruning(command []string, stati return b } -func (b *staticPodOperatorControllerBuilder) WithPodDisruptionBudgetGuard(operatorNamespace, operatorName, readyzPort string, createConditionalFunc func() (bool, error)) Builder { +func (b *staticPodOperatorControllerBuilder) WithPodDisruptionBudgetGuard(operatorNamespace, operatorName, readyzPort string, createConditionalFunc func() (bool, bool, error)) Builder { b.operatorNamespace = operatorNamespace b.operatorName = operatorName b.readyzPort = readyzPort @@ -227,8 +231,6 @@ func (b *staticPodOperatorControllerBuilder) ToControllers() (manager.Controller b.installerPodMutationFunc, ).WithMinReadyDuration( b.minReadyDuration, - ).WithStartupMonitorSupport( - b.enableStartMonitor, ), 1) manager.WithController(installerstate.NewInstallerStateController( @@ -276,23 +278,29 @@ func (b *staticPodOperatorControllerBuilder) ToControllers() (manager.Controller eventRecorder.Warning("PruningControllerMissing", "not enough information provided, not all functionality is present") } - manager.WithController(startupmonitorcondition.New( - b.operandNamespace, - b.staticPodName, - b.staticPodOperatorClient, - b.kubeInformers, - b.enableStartMonitor, - eventRecorder, - ), 1) + if b.enableStartMonitor != nil { + manager.WithController(startupmonitorcondition.New( + b.operandNamespace, + b.staticPodName, + b.staticPodOperatorClient, + b.kubeInformers, + b.enableStartMonitor, + eventRecorder, + ), 1) - manager.WithController(staticpodfallback.New( - b.operandNamespace, - b.operandPodLabelSelector, - b.staticPodOperatorClient, - b.kubeInformers, - b.enableStartMonitor, - b.eventRecorder, - ), 1) + if staticPodFallbackController, err := staticpodfallback.New( + b.operandNamespace, + b.operandPodLabelSelector, + b.staticPodOperatorClient, + b.kubeInformers, + b.enableStartMonitor, + b.eventRecorder, + ); err == nil { + manager.WithController(staticPodFallbackController, 1) + } else { + errs = append(errs, err) + } + } manager.WithController(node.NewNodeController( b.staticPodOperatorClient, @@ -317,8 +325,9 @@ func (b *staticPodOperatorControllerBuilder) ToControllers() (manager.Controller manager.WithController(loglevel.NewClusterOperatorLoggingController(b.staticPodOperatorClient, eventRecorder), 1) if len(b.operatorNamespace) > 0 && len(b.operatorName) > 0 && len(b.readyzPort) > 0 { - manager.WithController(guard.NewGuardController( + if guardController, err := guard.NewGuardController( b.operandNamespace, + b.operandPodLabelSelector, b.staticPodName, b.operatorName, b.readyzPort, @@ -329,7 +338,11 @@ func (b *staticPodOperatorControllerBuilder) ToControllers() (manager.Controller pdbClient, eventRecorder, b.guardCreateConditionalFunc, - ), 1) + ); err == nil { + manager.WithController(guardController, 1) + } else { + errs = append(errs, err) + } } return manager, errors.NewAggregate(errs) diff --git a/vendor/github.com/openshift/library-go/pkg/operator/staticresourcecontroller/static_resource_controller.go b/vendor/github.com/openshift/library-go/pkg/operator/staticresourcecontroller/static_resource_controller.go index 7c3f3e691..cc0a0fbce 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/staticresourcecontroller/static_resource_controller.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/staticresourcecontroller/static_resource_controller.go @@ -51,10 +51,17 @@ func init() { utilruntime.Must(admissionregistrationv1.AddToScheme(genericScheme)) } +// StaticResourcesPreconditionsFuncType checks if the precondition is met (is true) and then proceeds with the sync. +// When the requirement is not met, the controller reports degraded status. +// +// In case, the returned ready flag is false, a proper error with a valid description is recommended. +type StaticResourcesPreconditionsFuncType func(ctx context.Context) (bool, error) + type StaticResourceController struct { name string manifests []conditionalManifests ignoreNotFoundOnCreate bool + preconditions []StaticResourcesPreconditionsFuncType operatorClient v1helpers.OperatorClient clients *resourceapply.ClientHolder @@ -99,6 +106,8 @@ func NewStaticResourceController( operatorClient: operatorClient, clients: clients, + preconditions: []StaticResourcesPreconditionsFuncType{defaultStaticResourcesPreconditionsFunc}, + eventRecorder: eventRecorder.WithComponentSuffix(strings.ToLower(name)), factory: factory.New().WithInformers(operatorClient.Informer()).ResyncEvery(1 * time.Minute), @@ -119,6 +128,18 @@ func (c *StaticResourceController) WithIgnoreNotFoundOnCreate() *StaticResourceC return c } +// WithPrecondition adds a precondition, which blocks the sync method from being executed. Preconditions might be chained using: +// WithPrecondition(a).WithPrecondition(b).WithPrecondition(c). +// If any of the preconditions is false, the sync will result in an error. +// +// The requirement parameter should follow the convention described in the StaticResourcesPreconditionsFuncType. +// +// When the requirement is not met, the controller reports degraded status. +func (c *StaticResourceController) WithPrecondition(precondition StaticResourcesPreconditionsFuncType) *StaticResourceController { + c.preconditions = append(c.preconditions, precondition) + return c +} + // WithConditionalResources adds a set of manifests to be created when the shouldCreateFnArg is true and should be // deleted when the shouldDeleteFnArg is true. // If shouldCreateFnArg is nil, then it is always create. @@ -262,6 +283,28 @@ func (c *StaticResourceController) Sync(ctx context.Context, syncContext factory return nil } + for _, precondition := range c.preconditions { + ready, err := precondition(ctx) + // We don't care about the other preconditions, we just stop on the first one. + if !ready { + var message string + if err != nil { + message = err.Error() + } else { + message = "the operator didn't specify what preconditions are missing" + } + if _, _, updateErr := v1helpers.UpdateStatus(ctx, c.operatorClient, v1helpers.UpdateConditionFn(operatorv1.OperatorCondition{ + Type: fmt.Sprintf("%sDegraded", c.name), + Status: operatorv1.ConditionTrue, + Reason: "PreconditionNotReady", + Message: message, + })); updateErr != nil { + return updateErr + } + return err + } + } + errors := []error{} var notFoundErrorsCount int for _, conditionalManifest := range c.manifests { @@ -393,3 +436,7 @@ func (c *StaticResourceController) RelatedObjects() ([]configv1.ObjectReference, func (c *StaticResourceController) Run(ctx context.Context, workers int) { c.factory.WithSync(c.Sync).ToController(c.Name(), c.eventRecorder).Run(ctx, workers) } + +func defaultStaticResourcesPreconditionsFunc(_ context.Context) (bool, error) { + return true, nil +} diff --git a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go index 46d5c13b0..de48550f6 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/v1helpers/helpers.go @@ -383,3 +383,42 @@ func InjectObservedProxyIntoContainers(podSpec *corev1.PodSpec, containerNames [ return nil } + +func InjectTrustedCAIntoContainers(podSpec *corev1.PodSpec, configMapName string, containerNames []string) error { + podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{ + Name: "non-standard-root-system-trust-ca-bundle", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: configMapName, + }, + Items: []corev1.KeyToPath{ + {Key: "ca-bundle.crt", Path: "tls-ca-bundle.pem"}, + }, + }, + }, + }) + + for _, containerName := range containerNames { + for i := range podSpec.InitContainers { + if podSpec.InitContainers[i].Name == containerName { + podSpec.InitContainers[i].VolumeMounts = append(podSpec.InitContainers[i].VolumeMounts, corev1.VolumeMount{ + Name: "non-standard-root-system-trust-ca-bundle", + MountPath: "/etc/pki/ca-trust/extracted/pem", + ReadOnly: true, + }) + } + } + for i := range podSpec.Containers { + if podSpec.Containers[i].Name == containerName { + podSpec.Containers[i].VolumeMounts = append(podSpec.Containers[i].VolumeMounts, corev1.VolumeMount{ + Name: "non-standard-root-system-trust-ca-bundle", + MountPath: "/etc/pki/ca-trust/extracted/pem", + ReadOnly: true, + }) + } + } + } + + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3f19a6eef..c776e2179 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -248,7 +248,7 @@ github.com/openshift/client-go/operator/informers/externalversions/operator/v1 github.com/openshift/client-go/operator/informers/externalversions/operator/v1alpha1 github.com/openshift/client-go/operator/listers/operator/v1 github.com/openshift/client-go/operator/listers/operator/v1alpha1 -# github.com/openshift/library-go v0.0.0-20211220195323-eca2c467c492 +# github.com/openshift/library-go v0.0.0-20220128121806-335050b25f52 ## explicit; go 1.17 github.com/openshift/library-go/pkg/assets github.com/openshift/library-go/pkg/authorization/hardcodedauthorizer