diff --git a/cmd/aro/operator.go b/cmd/aro/operator.go index 8e1ac496e67..8ade9cb8fc8 100644 --- a/cmd/aro/operator.go +++ b/cmd/aro/operator.go @@ -28,6 +28,7 @@ import ( "github.com/Azure/ARO-RP/pkg/operator/controllers/clusteroperatoraro" "github.com/Azure/ARO-RP/pkg/operator/controllers/cpms" "github.com/Azure/ARO-RP/pkg/operator/controllers/dnsmasq" + "github.com/Azure/ARO-RP/pkg/operator/controllers/etchosts" "github.com/Azure/ARO-RP/pkg/operator/controllers/genevalogging" "github.com/Azure/ARO-RP/pkg/operator/controllers/guardrails" "github.com/Azure/ARO-RP/pkg/operator/controllers/imageconfig" @@ -233,6 +234,16 @@ func operator(ctx context.Context, log *logrus.Entry) error { client)).SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create controller %s: %v", cloudproviderconfig.ControllerName, err) } + if err = (etchosts.NewReconciler( + log.WithField("controller", etchosts.ControllerName), + client, dh)).SetupWithManager(mgr); err != nil { + return fmt.Errorf("unable to create controller %s: %v", etchosts.ControllerName, err) + } + if err = (etchosts.NewClusterReconciler( + log.WithField("controller", etchosts.ClusterControllerName), + client, dh)).SetupWithManager(mgr); err != nil { + return fmt.Errorf("unable to create controller %s: %v", etchosts.ClusterControllerName, err) + } // only register CPMS controller on clusters that support the CRD if err := discovery.ServerSupportsVersion(discoverycli, machinev1.GroupVersion); err == nil { diff --git a/pkg/operator/controllers/etchosts/cluster_controller.go b/pkg/operator/controllers/etchosts/cluster_controller.go new file mode 100644 index 00000000000..0a5efa23e17 --- /dev/null +++ b/pkg/operator/controllers/etchosts/cluster_controller.go @@ -0,0 +1,131 @@ +package etchosts + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + + mcv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + "github.com/sirupsen/logrus" + kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + 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" + + "github.com/Azure/ARO-RP/pkg/operator" + arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1" + "github.com/Azure/ARO-RP/pkg/operator/controllers/base" + "github.com/Azure/ARO-RP/pkg/operator/predicates" + "github.com/Azure/ARO-RP/pkg/util/dynamichelper" +) + +const ( + ClusterControllerName = "EtcHostsCluster" +) + +type EtcHostsClusterReconciler struct { + base.AROController + + dh dynamichelper.Interface +} + +func NewClusterReconciler(log *logrus.Entry, client client.Client, dh dynamichelper.Interface) *EtcHostsClusterReconciler { + return &EtcHostsClusterReconciler{ + AROController: base.AROController{ + Log: log, + Client: client, + Name: ClusterControllerName, + }, + dh: dh, + } +} + +// Reconcile watches ARO EtcHosts MachineConfig objects, and if any changes, reconciles it +func (r *EtcHostsClusterReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) { + r.Log.Debugf("reconcile MachineConfig openshift-machine-api/%s", request.Name) + + instance, err := r.GetCluster(ctx) + if err != nil { + return reconcile.Result{}, err + } + + if !instance.Spec.OperatorFlags.GetSimpleBoolean(operator.EtcHostsEnabled) { + r.Log.Debug("controller is disabled") + return reconcile.Result{}, nil + } + + r.Log.Debug("running") + + mc := &mcv1.MachineConfig{} + mcp := &mcv1.MachineConfigPool{} + + // If 99-master-aro-etc-hosts-gateway-domains doesn't exist, create it + err = r.Client.Get(ctx, types.NamespacedName{Namespace: "openshift-machine-api", Name: "99-master-aro-etc-hosts-gateway-domains"}, mc) + if kerrors.IsNotFound(err) { + err = r.Client.Get(ctx, types.NamespacedName{Name: "master"}, mcp) + if kerrors.IsNotFound(err) { + r.ClearDegraded(ctx) + return reconcile.Result{}, nil + } + if err != nil { + r.Log.Error(err) + r.SetDegraded(ctx, err) + return reconcile.Result{}, err + } + err = reconcileMachineConfigs(ctx, instance, "master", r.dh, *mcp) + if err != nil { + r.Log.Error(err) + r.SetDegraded(ctx, err) + return reconcile.Result{}, err + } + } + + // If 99-worker-aro-etc-hosts-gateway-domains doesn't exist, create it + err = r.Client.Get(ctx, types.NamespacedName{Namespace: "openshift-machine-api", Name: "99-worker-aro-etc-hosts-gateway-domains"}, mc) + if kerrors.IsNotFound(err) { + err = r.Client.Get(ctx, types.NamespacedName{Name: "worker"}, mcp) + if kerrors.IsNotFound(err) { + r.ClearDegraded(ctx) + return reconcile.Result{}, nil + } + if err != nil { + r.Log.Error(err) + r.SetDegraded(ctx, err) + return reconcile.Result{}, err + } + err = reconcileMachineConfigs(ctx, instance, "worker", r.dh, *mcp) + if err != nil { + r.Log.Error(err) + r.SetDegraded(ctx, err) + return reconcile.Result{}, err + } + } + + r.ClearConditions(ctx) + return reconcile.Result{}, nil +} + +// SetupWithManager setup our mananger to watch for changes to MCP and ARO Cluster obj +func (r *EtcHostsClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.Log.Info("starting etchosts-cluster controller") + + etcHostsBuilder := ctrl.NewControllerManagedBy(mgr). + For(&arov1alpha1.Cluster{}, builder.WithPredicates(predicate.And(predicates.AROCluster, predicate.GenerationChangedPredicate{}))). + Watches(&source.Kind{Type: &mcv1.MachineConfigPool{}}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(predicate.GenerationChangedPredicate{})). + Watches(&source.Kind{Type: &mcv1.MachineConfig{}}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(predicate.GenerationChangedPredicate{})) + + return etcHostsBuilder. + WithEventFilter(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{}, predicate.LabelChangedPredicate{})). + Named(ClusterControllerName). + Complete(r) +} diff --git a/pkg/operator/controllers/etchosts/etchosts.go b/pkg/operator/controllers/etchosts/etchosts.go new file mode 100644 index 00000000000..f10fff847b0 --- /dev/null +++ b/pkg/operator/controllers/etchosts/etchosts.go @@ -0,0 +1,248 @@ +package etchosts + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "bytes" + "encoding/json" + "fmt" + "text/template" + + "github.com/Azure/go-autorest/autorest/to" + ign3types "github.com/coreos/ignition/v2/config/v3_2/types" + mcv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + "github.com/pkg/errors" + "github.com/vincent-petithory/dataurl" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +const ( + configFileName = "aro.conf" + tempFileName = "aro.tmp" + unitFileName = "aro-etchosts-resolver.service" + scriptFileName = "aro-etchosts-resolver.sh" + scriptMarker = "openshift-aro-etchosts-resolver" +) + +type etcHostsAROConfTemplateData struct { + ClusterDomain string + APIIntIP string + GatewayDomains []string + GatewayPrivateEndpointIP string +} + +type etcHostsAROScriptTemplateData struct { + ConfigFileName string + TempFileName string + UnitFileName string + ScriptFileName string + ScriptMarker string +} + +var aroConfTemplate = template.Must(template.New("etchosts").Parse(`{{ .APIIntIP }} api.{{ .ClusterDomain }} api-int.{{ .ClusterDomain }} +{{ $.GatewayPrivateEndpointIP }} {{ range $GatewayDomain := .GatewayDomains }}{{ $GatewayDomain }} {{ end }} +`)) + +var aroScriptTemplate = template.Must(template.New("etchostscript").Parse(`#!/bin/bash +set -uo pipefail + +trap 'jobs -p | xargs kill || true; wait; exit 0' TERM + +OPENSHIFT_MARKER="{{ .ScriptMarker }}" +HOSTS_FILE="/etc/hosts" +CONFIG_FILE="/etc/hosts.d/{{ .ConfigFileName }}" +TEMP_FILE="/etc/hosts.d/{{ .TempFileName }}" + +# Make a temporary file with the old hosts file's data. +if ! cp -f "${HOSTS_FILE}" "${TEMP_FILE}"; then + echo "Failed to preserve hosts file. Exiting." + exit 1 +fi + +if ! sed --silent "/# ${OPENSHIFT_MARKER}/d; w ${TEMP_FILE}" "${HOSTS_FILE}"; then + # Only continue rebuilding the hosts entries if its original content is preserved + sleep 60 & wait + continue +fi + +while IFS= read -r line; do + echo "${line} # ${OPENSHIFT_MARKER}" >> "${TEMP_FILE}" +done < "${CONFIG_FILE}" + +# Replace /etc/hosts with our modified version if needed +cmp "${TEMP_FILE}" "${HOSTS_FILE}" || cp -f "${TEMP_FILE}" "${HOSTS_FILE}" +# TEMP_FILE is not removed to avoid file create/delete and attributes copy churn +`)) + +var aroUnitTemplate = template.Must(template.New("etchostservice").Parse(`[Unit] +Description=One shot service that appends static domains to etchosts +Before=network-online.target + +[Service] +# ExecStart will copy the hosts defined in /etc/hosts.d/aro.conf to /etc/hosts +ExecStart=/bin/bash /usr/local/bin/{{ .ScriptFileName }} + +[Install] +WantedBy=multi-user.target +`)) + +func GenerateEtcHostsAROConf(clusterDomain string, apiIntIP string, gatewayDomains []string, gatewayPrivateEndpointIP string) ([]byte, error) { + buf := &bytes.Buffer{} + templateData := etcHostsAROConfTemplateData{ + ClusterDomain: clusterDomain, + APIIntIP: apiIntIP, + GatewayDomains: gatewayDomains, + GatewayPrivateEndpointIP: gatewayPrivateEndpointIP, + } + + if err := aroConfTemplate.Execute(buf, templateData); err != nil { + return nil, errors.Wrap(err, "failed to generate "+configFileName+" from template") + } + + return buf.Bytes(), nil +} + +func GenerateEtcHostsAROScript() ([]byte, error) { + buf := &bytes.Buffer{} + templateData := etcHostsAROScriptTemplateData{ + ConfigFileName: configFileName, + TempFileName: tempFileName, + UnitFileName: unitFileName, + ScriptFileName: scriptFileName, + ScriptMarker: scriptMarker, + } + + if err := aroScriptTemplate.Execute(buf, templateData); err != nil { + return nil, errors.Wrap(err, "failed to generate "+scriptFileName+" from template") + } + + return buf.Bytes(), nil +} + +func GenerateEtcHostsAROUnit() (string, error) { + buf := &bytes.Buffer{} + templateData := etcHostsAROScriptTemplateData{ + ConfigFileName: configFileName, + TempFileName: tempFileName, + UnitFileName: unitFileName, + ScriptFileName: scriptFileName, + ScriptMarker: scriptMarker, + } + + if err := aroUnitTemplate.Execute(buf, templateData); err != nil { + return "", errors.Wrap(err, "failed to generate "+unitFileName+" from template") + } + + return buf.String(), nil +} + +func EtcHostsIgnitionConfig(clusterDomain string, apiIntIP string, gatewayDomains []string, gatewayPrivateEndpointIP string) (*ign3types.Config, error) { + aroconf, err := GenerateEtcHostsAROConf(clusterDomain, apiIntIP, gatewayDomains, gatewayPrivateEndpointIP) + if err != nil { + return nil, errors.Wrap(err, "failed to generate addtional hosts for etc hosts") + } + + aroscript, err := GenerateEtcHostsAROScript() + if err != nil { + return nil, errors.Wrap(err, "failed to generate template") + } + + arounit, err := GenerateEtcHostsAROUnit() + if err != nil { + return nil, errors.Wrap(err, "failed to generate template") + } + + ign := &ign3types.Config{ + Ignition: ign3types.Ignition{ + Version: ign3types.MaxVersion.String(), + }, + Storage: ign3types.Storage{ + Files: []ign3types.File{ + { + Node: ign3types.Node{ + Path: "/etc/hosts.d/" + configFileName, + Overwrite: to.BoolPtr(true), + User: ign3types.NodeUser{ + Name: to.StringPtr("root"), + }, + }, + FileEmbedded1: ign3types.FileEmbedded1{ + Contents: ign3types.Resource{ + Source: to.StringPtr(dataurl.EncodeBytes(aroconf)), + }, + Mode: to.IntPtr(0644), + }, + }, + { + Node: ign3types.Node{ + Overwrite: to.BoolPtr(true), + Path: "/usr/local/bin/" + scriptFileName, + User: ign3types.NodeUser{ + Name: to.StringPtr("root"), + }, + }, + FileEmbedded1: ign3types.FileEmbedded1{ + Contents: ign3types.Resource{ + Source: to.StringPtr(dataurl.EncodeBytes(aroscript)), + }, + Mode: to.IntPtr(0744), + }, + }, + }, + }, + Systemd: ign3types.Systemd{ + Units: []ign3types.Unit{ + { + Contents: &arounit, + Enabled: to.BoolPtr(true), + Name: unitFileName, + }, + }, + }, + } + + return ign, nil +} + +func EtcHostsMachineConfig(clusterDomain string, apiIntIP string, gatewayDomains []string, gatewayPrivateEndpointIP string, role string) (*mcv1.MachineConfig, error) { + ignConfig, err := EtcHostsIgnitionConfig(clusterDomain, apiIntIP, gatewayDomains, gatewayPrivateEndpointIP) + if err != nil { + return nil, err + } + + b, err := json.Marshal(ignConfig) + if err != nil { + return nil, err + } + + // canonicalise the machineconfig payload the same way as MCO + var i interface{} + err = json.Unmarshal(b, &i) + if err != nil { + return nil, err + } + + rawExt := runtime.RawExtension{} + rawExt.Raw, err = json.Marshal(i) + if err != nil { + return nil, err + } + + return &mcv1.MachineConfig{ + TypeMeta: metav1.TypeMeta{ + APIVersion: mcv1.SchemeGroupVersion.String(), + Kind: "MachineConfig", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("99-%s-aro-etc-hosts-gateway-domains", role), + Labels: map[string]string{ + "machineconfiguration.openshift.io/role": role, + }, + }, + Spec: mcv1.MachineConfigSpec{ + Config: rawExt, + }, + }, nil +} diff --git a/pkg/operator/controllers/etchosts/etchosts_test.go b/pkg/operator/controllers/etchosts/etchosts_test.go new file mode 100644 index 00000000000..dcee8dc72bd --- /dev/null +++ b/pkg/operator/controllers/etchosts/etchosts_test.go @@ -0,0 +1,77 @@ +package etchosts + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGenerateEtcHostsAROConf(t *testing.T) { + cases := []struct { + name string + input etcHostsAROConfTemplateData + expected string + }{ + { + name: "generate aro.conf data", + input: etcHostsAROConfTemplateData{ + ClusterDomain: "test.com", + APIIntIP: "10.10.10.10", + GatewayDomains: []string{"test2.com", "test3.com"}, + GatewayPrivateEndpointIP: "20.20.20.20", + }, + expected: "10.10.10.10\tapi.test.com api-int.test.com\n20.20.20.20\ttest2.com test3.com \n", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + actual, _ := GenerateEtcHostsAROConf(tc.input.ClusterDomain, tc.input.APIIntIP, + tc.input.GatewayDomains, tc.input.GatewayPrivateEndpointIP) + assert.Equal(t, tc.expected, string(actual)) + }) + } +} + +func TestGenerateEtcHostsAROScript(t *testing.T) { + cases := []struct { + name string + input etcHostsAROScriptTemplateData + expected string + }{ + { + name: "generate aro-etchosts-resolver.sh", + expected: "#!/bin/bash\nset -uo pipefail\n\ntrap 'jobs -p | xargs kill || true; wait; exit 0' TERM\n\nOPENSHIFT_MARKER=\"openshift-aro-etchosts-resolver\"\nHOSTS_FILE=\"/etc/hosts\"\nCONFIG_FILE=\"/etc/hosts.d/aro.conf\"\nTEMP_FILE=\"/etc/hosts.d/aro.tmp\"\n\n# Make a temporary file with the old hosts file's data.\nif ! cp -f \"${HOSTS_FILE}\" \"${TEMP_FILE}\"; then\n echo \"Failed to preserve hosts file. Exiting.\"\n exit 1\nfi\n\nif ! sed --silent \"/# ${OPENSHIFT_MARKER}/d; w ${TEMP_FILE}\" \"${HOSTS_FILE}\"; then\n # Only continue rebuilding the hosts entries if its original content is preserved\n sleep 60 & wait\n continue\nfi\n\nwhile IFS= read -r line; do\n echo \"${line} # ${OPENSHIFT_MARKER}\" >> \"${TEMP_FILE}\"\ndone < \"${CONFIG_FILE}\"\n\n# Replace /etc/hosts with our modified version if needed\ncmp \"${TEMP_FILE}\" \"${HOSTS_FILE}\" || cp -f \"${TEMP_FILE}\" \"${HOSTS_FILE}\"\n# TEMP_FILE is not removed to avoid file create/delete and attributes copy churn\n", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + actual, _ := GenerateEtcHostsAROScript() + assert.Equal(t, tc.expected, string(actual)) + }) + } +} + +func TestGenerateEtcHostsAROUnit(t *testing.T) { + cases := []struct { + name string + input etcHostsAROScriptTemplateData + expected string + }{ + { + name: "generate aro-etchosts-resolver.service", + expected: "[Unit]\nDescription=One shot service that appends static domains to etchosts\nBefore=network-online.target\n\n[Service]\n# ExecStart will copy the hosts defined in /etc/hosts.d/aro.conf to /etc/hosts\nExecStart=/bin/bash /usr/local/bin/aro-etchosts-resolver.sh\n\n[Install]\nWantedBy=multi-user.target\n", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + actual, _ := GenerateEtcHostsAROUnit() + assert.Equal(t, tc.expected, actual) + }) + } +} diff --git a/pkg/operator/controllers/etchosts/machineconfig_controller.go b/pkg/operator/controllers/etchosts/machineconfig_controller.go new file mode 100644 index 00000000000..861095ec96b --- /dev/null +++ b/pkg/operator/controllers/etchosts/machineconfig_controller.go @@ -0,0 +1,143 @@ +package etchosts + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + "regexp" + + mcv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + "github.com/sirupsen/logrus" + kerrors "k8s.io/apimachinery/pkg/api/errors" + kruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + 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" + + "github.com/Azure/ARO-RP/pkg/operator" + arov1alpha1 "github.com/Azure/ARO-RP/pkg/operator/apis/aro.openshift.io/v1alpha1" + "github.com/Azure/ARO-RP/pkg/operator/controllers/base" + "github.com/Azure/ARO-RP/pkg/operator/predicates" + "github.com/Azure/ARO-RP/pkg/util/dynamichelper" +) + +const ( + ControllerName = "EtcHostsMachineConfig" +) + +type EtcHostsMachineConfigReconciler struct { + base.AROController + + dh dynamichelper.Interface +} + +var etcHostsRegex = regexp.MustCompile("^99-(.*)-aro-etc-hosts-gateway-domains$") + +func NewReconciler(log *logrus.Entry, client client.Client, dh dynamichelper.Interface) *EtcHostsMachineConfigReconciler { + return &EtcHostsMachineConfigReconciler{ + AROController: base.AROController{ + Log: log, + Client: client, + Name: ControllerName, + }, + dh: dh, + } +} + +// Reconcile watches ARO EtcHosts MachineConfig objects, and if any changes, reconciles it +func (r *EtcHostsMachineConfigReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) { + r.Log.Debugf("reconcile MachineConfig openshift-machine-api/%s", request.Name) + + instance, err := r.GetCluster(ctx) + if err != nil { + return reconcile.Result{}, err + } + + if !instance.Spec.OperatorFlags.GetSimpleBoolean(operator.EtcHostsEnabled) { + r.Log.Debug("controller is disabled") + return reconcile.Result{}, nil + } + + r.Log.Debug("running") + mcp := &mcv1.MachineConfigPool{} + // Make sure we are reconciling against etchosts machine config + m := etcHostsRegex.FindStringSubmatch(request.Name) + r.Log.Debugf("MachineConfig openshift-machine-api/%s", request.Name) + if m == nil { + return reconcile.Result{}, nil + } + role := m[1] + + err = r.Client.Get(ctx, types.NamespacedName{Name: role}, mcp) + if kerrors.IsNotFound(err) { + r.ClearDegraded(ctx) + return reconcile.Result{}, nil + } + if err != nil { + r.Log.Error(err) + r.SetDegraded(ctx, err) + return reconcile.Result{}, err + } + if mcp.GetDeletionTimestamp() != nil { + return reconcile.Result{}, nil + } + + err = reconcileMachineConfigs(ctx, instance, role, r.dh, *mcp) + if err != nil { + r.Log.Error(err) + r.SetDegraded(ctx, err) + return reconcile.Result{}, err + } + + r.ClearConditions(ctx) + return reconcile.Result{}, nil +} + +// SetupWithManager setup our mananger to watch for changes to MCP and ARO Cluster obj +func (r *EtcHostsMachineConfigReconciler) SetupWithManager(mgr ctrl.Manager) error { + r.Log.Info("starting etchosts-machine-config controller") + + etcHostsBuilder := ctrl.NewControllerManagedBy(mgr). + For(&mcv1.MachineConfig{}). + Watches(&source.Kind{Type: &mcv1.MachineConfigPool{}}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(predicate.GenerationChangedPredicate{})). + Watches(&source.Kind{Type: &arov1alpha1.Cluster{}}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(predicate.And(predicates.AROCluster, predicate.GenerationChangedPredicate{}))) + + return etcHostsBuilder. + WithEventFilter(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{}, predicate.LabelChangedPredicate{})). + Named(ControllerName). + Complete(r) +} + +func reconcileMachineConfigs(ctx context.Context, instance *arov1alpha1.Cluster, role string, dh dynamichelper.Interface, mcps ...mcv1.MachineConfigPool) error { + var resources []kruntime.Object + for _, mcp := range mcps { + resource, err := EtcHostsMachineConfig(instance.Spec.Domain, instance.Spec.APIIntIP, instance.Spec.GatewayDomains, instance.Spec.GatewayPrivateEndpointIP, role) + if err != nil { + return err + } + + err = dynamichelper.SetControllerReferences([]kruntime.Object{resource}, &mcp) + if err != nil { + return err + } + + resources = append(resources, resource) + } + + err := dynamichelper.Prepare(resources) + if err != nil { + return err + } + + return dh.Ensure(ctx, resources...) +} diff --git a/pkg/operator/flags.go b/pkg/operator/flags.go index fdb0e603bd0..b4506f78c40 100644 --- a/pkg/operator/flags.go +++ b/pkg/operator/flags.go @@ -34,6 +34,7 @@ const ( GuardrailsEnabled = "aro.guardrails.enabled" GuardrailsDeployManaged = "aro.guardrails.deploy.managed" CloudProviderConfigEnabled = "aro.cloudproviderconfig.enabled" + EtcHostsEnabled = "aro.etchosts.enabled" FlagTrue = "true" FlagFalse = "false" ) @@ -72,5 +73,6 @@ func DefaultOperatorFlags() map[string]string { GuardrailsEnabled: FlagFalse, GuardrailsDeployManaged: FlagFalse, CloudProviderConfigEnabled: FlagTrue, + EtcHostsEnabled: FlagFalse, } }