Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions cmd/machine-api-operator/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strconv"

osconfigv1 "github.com/openshift/api/config/v1"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/machine-api-operator/pkg/metrics"
"github.com/openshift/machine-api-operator/pkg/operator"
"github.com/openshift/machine-api-operator/pkg/util"
Expand Down Expand Up @@ -120,7 +121,7 @@ func initMachineAPIInformers(ctx *ControllerContext) {
klog.Info("Synced up machine api informer caches")
}

func initRecorder(kubeClient kubernetes.Interface) (record.EventRecorder, error) {
func initEventRecorder(kubeClient kubernetes.Interface) (record.EventRecorder, error) {
eventRecorderScheme := runtime.NewScheme()
if err := osconfigv1.Install(eventRecorderScheme); err != nil {
return nil, fmt.Errorf("failed to create event recorder scheme: %v", err)
Expand All @@ -131,26 +132,41 @@ func initRecorder(kubeClient kubernetes.Interface) (record.EventRecorder, error)
return eventBroadcaster.NewRecorder(eventRecorderScheme, v1.EventSource{Component: "machineapioperator"}), nil
}

func initRecorder(kubeClient kubernetes.Interface) (events.Recorder, error) {
controllerRef, err := events.GetControllerReferenceForCurrentPod(context.Background(), kubeClient, componentNamespace, nil)
if err != nil {
return nil, fmt.Errorf("failed to create controller ref for recorder: %v", err)
}
recorder := events.NewKubeRecorder(kubeClient.CoreV1().Events(componentNamespace), "machineapioperator", controllerRef)
return recorder, nil
}

func startControllersOrDie(ctx *ControllerContext) {
kubeClient := ctx.ClientBuilder.KubeClientOrDie(componentName)
recorder, err := initRecorder(kubeClient)
eventRecorder, err := initEventRecorder(kubeClient)
if err != nil {
klog.Fatalf("failed to create event recorder: %v", err)
}
recorder, err := initRecorder(kubeClient)
if err != nil {
klog.Fatalf("failed to create recorder: %v", err)
}
optr, err := operator.New(
componentNamespace, componentName,
startOpts.imagesFile,
config,
ctx.KubeNamespacedInformerFactory.Apps().V1().Deployments(),
ctx.KubeNamespacedInformerFactory.Apps().V1().DaemonSets(),
ctx.ConfigInformerFactory.Config().V1().FeatureGates(),
ctx.ConfigInformerFactory.Config().V1().ClusterVersions(),
ctx.KubeNamespacedInformerFactory.Admissionregistration().V1().ValidatingWebhookConfigurations(),
ctx.KubeNamespacedInformerFactory.Admissionregistration().V1().MutatingWebhookConfigurations(),
ctx.ConfigInformerFactory.Config().V1().Proxies(),
ctx.ClientBuilder.KubeClientOrDie(componentName),
ctx.ClientBuilder.OpenshiftClientOrDie(componentName),
ctx.ClientBuilder.MachineClientOrDie(componentName),
ctx.ClientBuilder.DynamicClientOrDie(componentName),
eventRecorder,
recorder,
)
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ require (
github.com/google/uuid v1.3.0
github.com/onsi/ginkgo/v2 v2.9.5
github.com/onsi/gomega v1.27.7
github.com/openshift/api v0.0.0-20230703162140-6e9853e4c905
github.com/openshift/api v0.0.0-20230711095040-ca06f4a23b64
github.com/openshift/client-go v0.0.0-20230503144108-75015d2347cb
github.com/openshift/library-go v0.0.0-20230508110756-9b7abe2c9cbf
github.com/openshift/library-go v0.0.0-20230706195801-561433066966
github.com/operator-framework/operator-sdk v0.5.1-0.20190301204940-c2efe6f74e7b
github.com/prometheus/client_golang v1.15.1
github.com/spf13/cobra v1.6.1
Expand Down Expand Up @@ -189,6 +189,7 @@ require (
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/robfig/cron v1.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryancurrah/gomodguard v1.3.0 // indirect
github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -631,12 +631,12 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
github.com/openshift/api v0.0.0-20230703162140-6e9853e4c905 h1:zvzzN6z/QxwQ6KiHnGb/DuVSOhHkYX6SqkPILdwc/3s=
github.com/openshift/api v0.0.0-20230703162140-6e9853e4c905/go.mod h1:4VWG+W22wrB4HfBL88P40DxLEpSOaiBVxUnfalfJo9k=
github.com/openshift/api v0.0.0-20230711095040-ca06f4a23b64 h1:j7LIIr4Vrdy4Dpd4bw2j53UXUSjA1eXXC0x89g9kyAI=
github.com/openshift/api v0.0.0-20230711095040-ca06f4a23b64/go.mod h1:yimSGmjsI+XF1mr+AKBs2//fSXIOhhetHGbMlBEfXbs=
github.com/openshift/client-go v0.0.0-20230503144108-75015d2347cb h1:Nij5OnaECrkmcRQMAE9LMbQXPo95aqFnf+12B7SyFVI=
github.com/openshift/client-go v0.0.0-20230503144108-75015d2347cb/go.mod h1:Rhb3moCqeiTuGHAbXBOlwPubUMlOZEkrEWTRjIF3jzs=
github.com/openshift/library-go v0.0.0-20230508110756-9b7abe2c9cbf h1:ZpFAN2qprgp7jEhGPrOAwP8mmuYC9BRYzvDefg+k4GM=
github.com/openshift/library-go v0.0.0-20230508110756-9b7abe2c9cbf/go.mod h1:PJVatR/oS/EaFciwylyAr9hORSqQHrC+5bXf4L0wsBY=
github.com/openshift/library-go v0.0.0-20230706195801-561433066966 h1:qJZaVzxJy7s6Cp1908rkSR64YCrpiKMZAkfYhsZPPCw=
github.com/openshift/library-go v0.0.0-20230706195801-561433066966/go.mod h1:PegtilvJPBJXjJG3AV8uL1a0SAnBr6K67ShNiWVb40M=
github.com/operator-framework/operator-sdk v0.5.1-0.20190301204940-c2efe6f74e7b h1:Q1q8w51pAZdx6LEkaYdSbUaaEOHXTyTXLhtGgIiKaiA=
github.com/operator-framework/operator-sdk v0.5.1-0.20190301204940-c2efe6f74e7b/go.mod h1:iVyukRkam5JZa8AnjYf+/G3rk7JI1+M6GsU0sq0B9NA=
github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=
Expand Down Expand Up @@ -700,6 +700,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
Expand Down
19 changes: 19 additions & 0 deletions install/0000_30_machine-api-operator_09_rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,24 @@ rules:
- patch
- delete

- apiGroups:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed for the getting the controller ref for the recorder for the feature gate accessor...

- apps
resources:
- replicasets
verbs:
- get
- list
- watch

- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch

- apiGroups:
- machine.openshift.io
resources:
Expand Down Expand Up @@ -394,6 +412,7 @@ rules:
- featuregates
- featuregates/status
- proxies
- clusterversions
verbs:
- get
- list
Expand Down
4 changes: 4 additions & 0 deletions install/0000_30_machine-api-operator_11_deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ spec:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: POD_NAME
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed for the getting the controller ref for the recorder for the feature gate accessor...

valueFrom:
fieldRef:
fieldPath: metadata.name
- name: METRICS_PORT
value: "8080"
resources:
Expand Down
2 changes: 1 addition & 1 deletion pkg/operator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func getImagesFromJSONFile(filePath string) (*Images, error) {
return &i, nil
}

func getProviderControllerFromImages(platform configv1.PlatformType, featureGate *configv1.FeatureGate, images Images) (string, error) {
func getProviderControllerFromImages(platform configv1.PlatformType, images Images) (string, error) {
switch platform {
case configv1.AWSPlatformType:
return images.ClusterAPIControllerAWS, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/operator/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func TestGetProviderControllerFromImages(t *testing.T) {
}

for _, test := range tests {
res, err := getProviderControllerFromImages(test.provider, &test.featureGate, *img)
res, err := getProviderControllerFromImages(test.provider, *img)
if err != nil {
t.Errorf("failed getProviderControllerFromImages: %v", err)
}
Expand Down
110 changes: 75 additions & 35 deletions pkg/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package operator

import (
"context"
"errors"
"fmt"
"os"
"time"
Expand All @@ -12,9 +13,10 @@ import (
configinformersv1 "github.com/openshift/client-go/config/informers/externalversions/config/v1"
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1"
machineclientset "github.com/openshift/client-go/machine/clientset/versioned"
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
"github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/resource/resourceapply"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
Expand All @@ -39,6 +41,9 @@ const (
// 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s
maxRetries = 15
maoOwnedAnnotation = "machine.openshift.io/owned"

releaseVersionEnvVariableName = "RELEASE_VERSION"
releaseVersionUnknownValue = "unknown"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same value as the raw version string when it's not initialised?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think it's fine to leave as that for now, lets see if anything breaks!

)

// Operator defines machine api operator.
Expand All @@ -53,6 +58,7 @@ type Operator struct {
machineClient machineclientset.Interface
dynamicClient dynamic.Interface
eventRecorder record.EventRecorder
recorder events.Recorder

syncHandler func(ic string) (reconcile.Result, error)

Expand All @@ -71,8 +77,7 @@ type Operator struct {
mutatingWebhookLister admissionlisterv1.MutatingWebhookConfigurationLister
mutatingWebhookListerSynced cache.InformerSynced

featureGateLister configlistersv1.FeatureGateLister
featureGateCacheSynced cache.InformerSynced
featureGateAccessor featuregates.FeatureGateAccess

// queue only ever has one item, but it has nice error handling backoff/retry semantics
queue workqueue.RateLimitingInterface
Expand All @@ -91,6 +96,7 @@ func New(
deployInformer appsinformersv1.DeploymentInformer,
daemonsetInformer appsinformersv1.DaemonSetInformer,
featureGateInformer configinformersv1.FeatureGateInformer,
clusterVersionInformer configinformersv1.ClusterVersionInformer,
validatingWebhookInformer admissioninformersv1.ValidatingWebhookConfigurationInformer,
mutatingWebhookInformer admissioninformersv1.MutatingWebhookConfigurationInformer,
proxyInformer configinformersv1.ProxyInformer,
Expand All @@ -99,13 +105,18 @@ func New(
machineClient machineclientset.Interface,
dynamicClient dynamic.Interface,

recorder record.EventRecorder,
eventRecorder record.EventRecorder,
recorder events.Recorder,
) (*Operator, error) {
// we must report the version from the release payload when we report available at that level
// TODO we will report the version of the operands (so our machine api implementation version)
operandVersions := []osconfigv1.OperandVersion{}
if releaseVersion := os.Getenv("RELEASE_VERSION"); len(releaseVersion) > 0 {
releaseVersion := os.Getenv(releaseVersionEnvVariableName)
if len(releaseVersion) > 0 {
operandVersions = append(operandVersions, osconfigv1.OperandVersion{Name: "operator", Version: releaseVersion})
} else {
klog.Infof("%s environment variable is missing, defaulting to %q", releaseVersionEnvVariableName, releaseVersionUnknownValue)
releaseVersion = releaseVersionUnknownValue
}

optr := &Operator{
Expand All @@ -116,7 +127,8 @@ func New(
osClient: osClient,
machineClient: machineClient,
dynamicClient: dynamicClient,
eventRecorder: recorder,
eventRecorder: eventRecorder,
recorder: recorder,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "machineapioperator"),
operandVersions: operandVersions,
}
Expand All @@ -137,10 +149,36 @@ func New(
if err != nil {
return nil, fmt.Errorf("error adding event handler to mutatingwebhook informer: %v", err)
}
_, err = featureGateInformer.Informer().AddEventHandler(optr.eventHandler())
if err != nil {
return nil, fmt.Errorf("error adding event handler to featuregates informer: %v", err)
}

desiredVersion := releaseVersion
missingVersion := "0.0.1-snapshot"
featureGateAccessor := featuregates.NewFeatureGateAccess(
desiredVersion, missingVersion,
clusterVersionInformer, featureGateInformer,
recorder,
)
featureGateAccessor.SetChangeHandler(func(featureChange featuregates.FeatureChange) {
if featureChange.Previous == nil {
// When the initial featuregate is set, the previous version is nil.
// Nothing to do in this case, it's handled by the 1st sync, which only runs after the initial feature set was received.
return
}

klog.V(4).InfoS("FeatureGates changed", "enabled", featureChange.New.Enabled, "disabled", featureChange.New.Disabled)
prevDisableMHC := featuregates.NewFeatureGate(featureChange.Previous.Enabled, featureChange.Previous.Disabled).
Enabled(osconfigv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController)
newDisableMHC := featuregates.NewFeatureGate(featureChange.New.Enabled, featureChange.New.Disabled).
Enabled(osconfigv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController)

if prevDisableMHC != newDisableMHC {
klog.V(2).InfoS("Resync for modified feature gate",
"FeatureGateMachineAPIOperatorDisableMachineHealthCheckController enabled", newDisableMHC,
)
workQueueKey := fmt.Sprintf("%s/%s", optr.namespace, optr.name)
optr.queue.Add(workQueueKey)
}
})
optr.featureGateAccessor = featureGateAccessor

optr.config = config
optr.syncHandler = optr.sync
Expand All @@ -160,9 +198,6 @@ func New(
optr.mutatingWebhookLister = mutatingWebhookInformer.Lister()
optr.mutatingWebhookListerSynced = mutatingWebhookInformer.Informer().HasSynced

optr.featureGateLister = featureGateInformer.Lister()
optr.featureGateCacheSynced = featureGateInformer.Informer().HasSynced

return optr, nil
}

Expand All @@ -179,12 +214,24 @@ func (optr *Operator) Run(workers int, stopCh <-chan struct{}) {
optr.validatingWebhookListerSynced,
optr.deployListerSynced,
optr.daemonsetListerSynced,
optr.proxyListerSynced,
optr.featureGateCacheSynced) {
optr.proxyListerSynced) {
klog.Error("Failed to sync caches")
return
}
klog.Info("Synced up caches")

ctx, cancelFeatureGateAccessor := context.WithCancel(context.Background())
defer cancelFeatureGateAccessor()
go optr.featureGateAccessor.Run(ctx)
klog.Info("Started feature gate accessor")
select {
case <-optr.featureGateAccessor.InitialFeatureGatesObserved():
klog.V(4).Info("FeatureGates initialized")
case <-time.After(1 * time.Minute):
klog.Error(errors.New("timed out waiting for FeatureGate detection"), "unable to start operator")
return
}

for i := 0; i < workers; i++ {
go wait.Until(optr.worker, time.Second, stopCh)
}
Expand Down Expand Up @@ -353,19 +400,6 @@ func (optr *Operator) sync(key string) (reconcile.Result, error) {
return optr.syncAll(operatorConfig)
}

func getFeatureGate(lister configlistersv1.FeatureGateLister) (*osconfigv1.FeatureGate, error) {
featureGate, err := lister.Get("cluster")
if errors.IsNotFound(err) {
// No feature gate is set, therefore cannot be external.
// This is not an error as the feature gate is an optional resource.
return nil, nil
} else if err != nil {
return nil, fmt.Errorf("could not fetch featuregate: %v", err)
}

return featureGate, nil
}

func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) {
infra, err := optr.osClient.ConfigV1().Infrastructures().Get(context.Background(), "cluster", metav1.GetOptions{})
if err != nil {
Expand All @@ -382,12 +416,7 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) {
return nil, err
}

featureGate, err := getFeatureGate(optr.featureGateLister)
if err != nil {
return nil, err
}

providerControllerImage, err := getProviderControllerFromImages(provider, featureGate, *images)
providerControllerImage, err := getProviderControllerFromImages(provider, *images)
if err != nil {
return nil, err
}
Expand All @@ -412,14 +441,25 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) {
return nil, err
}

// in case the MHC controller is disabled, leave its image empty
mhcImage := machineAPIOperatorImage
featureGates, err := optr.featureGateAccessor.CurrentFeatureGates()
if err != nil {
return nil, err
}
if featureGates.Enabled(osconfigv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController) {
klog.V(2).Info("Disabling MHC controller")
mhcImage = ""
}

return &OperatorConfig{
TargetNamespace: optr.namespace,
Proxy: clusterWideProxy,
Controllers: Controllers{
Provider: providerControllerImage,
MachineSet: machineAPIOperatorImage,
NodeLink: machineAPIOperatorImage,
MachineHealthCheck: machineAPIOperatorImage,
MachineHealthCheck: mhcImage,
KubeRBACProxy: kubeRBACProxy,
TerminationHandler: terminationHandlerImage,
},
Expand Down
Loading