Skip to content

Commit

Permalink
Render featuregate using config operator in ignition server init cont…
Browse files Browse the repository at this point in the history
…ainer
  • Loading branch information
JoelSpeed committed Jun 1, 2023
1 parent c5ea887 commit 31463cf
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 18 deletions.
Expand Up @@ -900,7 +900,9 @@ func (r *HostedControlPlaneReconciler) reconcile(ctx context.Context, hostedCont
if err := ignitionserver.ReconcileIgnitionServer(ctx,
r.Client,
createOrUpdate,
releaseImageProvider.Version(),
releaseImageProvider.GetImage(util.CPOImageName),
releaseImageProvider.GetImage("cluster-config-operator"),
hostedControlPlane,
r.DefaultIngressDomain,
// The healthz handler was added before the CPO started to manage the ignition server, and it's the same binary,
Expand Down
@@ -1,11 +1,14 @@
package ignitionserver

import (
"bytes"
"context"
"fmt"
"net"

configv1 "github.com/openshift/api/config/v1"
routev1 "github.com/openshift/api/route/v1"
"github.com/openshift/hypershift/api"
hyperv1 "github.com/openshift/hypershift/api/v1beta1"
"github.com/openshift/hypershift/hypershift-operator/controllers/manifests/controlplaneoperator"
"github.com/openshift/hypershift/hypershift-operator/controllers/manifests/ignitionserver"
Expand All @@ -29,7 +32,9 @@ import (
func ReconcileIgnitionServer(ctx context.Context,
c client.Client,
createOrUpdate upsert.CreateOrUpdateFN,
releaseVersion string,
utilitiesImage string,
configOperatorImage string,
hcp *hyperv1.HostedControlPlane,
defaultIngressDomain string,
hasHealthzHandler bool,
Expand Down Expand Up @@ -252,6 +257,26 @@ func ReconcileIgnitionServer(ctx context.Context,
}
}

// Use the default FeatureSet unless otherwise specified.
featureGate := &configv1.FeatureGate{
TypeMeta: metav1.TypeMeta{
Kind: "FeatureGate",
APIVersion: configv1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
},
}
if hcp.Spec.Configuration != nil && hcp.Spec.Configuration.FeatureGate != nil {
featureGate.Spec = *hcp.Spec.Configuration.FeatureGate
}

featureGateBuffer := &bytes.Buffer{}
if err := api.YamlSerializer.Encode(featureGate, featureGateBuffer); err != nil {
return fmt.Errorf("failed to encode feature gates: %w", err)
}
featureGateYAML := featureGateBuffer.String()

// Reconcile deployment
ignitionServerDeployment := ignitionserver.Deployment(controlPlaneNamespace)
if result, err := createOrUpdate(ctx, c, ignitionServerDeployment, func() error {
Expand Down Expand Up @@ -295,6 +320,38 @@ func ReconcileIgnitionServer(ctx context.Context,
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: "shared",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
},
InitContainers: []corev1.Container{
{
Name: "fetch-feature-gate",
Image: configOperatorImage,
ImagePullPolicy: corev1.PullIfNotPresent,
Command: []string{
"/bin/bash",
},
Args: []string{
"-c",
invokeFeatureGateRenderScript("/shared", releaseVersion, string(featureGateYAML)),
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("40Mi"),
corev1.ResourceCPU: resource.MustParse("10m"),
},
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "shared",
MountPath: "/shared",
},
},
},
},
Containers: []corev1.Container{
{
Expand Down Expand Up @@ -322,6 +379,7 @@ func ReconcileIgnitionServer(ctx context.Context,
"--key-file", "/var/run/secrets/ignition/serving-cert/tls.key",
"--registry-overrides", util.ConvertRegistryOverridesToCommandLineFlag(registryOverrides),
"--platform", string(hcp.Spec.Platform.Type),
"--feature-gate=/shared/99_feature-gate.yaml",
},
LivenessProbe: &corev1.Probe{
ProbeHandler: probeHandler,
Expand Down Expand Up @@ -364,6 +422,10 @@ func ReconcileIgnitionServer(ctx context.Context,
Name: "payloads",
MountPath: "/payloads",
},
{
Name: "shared",
MountPath: "/shared",
},
},
},
},
Expand Down Expand Up @@ -459,3 +521,25 @@ func reconcileInternalRoute(route *routev1.Route, ownerRef config.OwnerRef) erro
// Assumes ownerRef is the HCP
return util.ReconcileInternalRoute(route, ownerRef.Reference.Name, ignitionserver.Service(route.Namespace).Name)
}

func invokeFeatureGateRenderScript(workDir, payloadVersion, featureGateYAML string) string {
var script = `#!/bin/bash
set -e
cd /tmp
mkdir input output manifests
touch /tmp/manifests/99_feature-gate.yaml
cat <<EOF >/tmp/manifests/99_feature-gate.yaml
%[3]s
EOF
/usr/bin/cluster-config-operator render \
--config-output-file config \
--asset-input-dir /tmp/input \
--asset-output-dir /tmp/output \
--rendered-manifest-files=/tmp/manifests \
--payload-version=%[2]s
cp /tmp/manifests/99_feature-gate.yaml %[1]s/99_feature-gate.yaml
`
return fmt.Sprintf(script, workDir, payloadVersion, featureGateYAML)
}
Expand Up @@ -1500,10 +1500,23 @@ func (r *HostedClusterReconciler) reconcile(ctx context.Context, req ctrl.Reques

// Reconcile the Ignition server
if !controlplaneOperatorManagesIgnitionServer {
configOperatorImage, releaseVersion, err := func() (string, string, error) {
releaseInfo, err := r.ReleaseProvider.Lookup(ctx, hcluster.Spec.Release.Image, pullSecretBytes)
if err != nil {
return "", "", fmt.Errorf("failed to lookup release image: %w", err)
}
return releaseInfo.ComponentImages()["cluster-config-operator"], releaseInfo.Version(), nil
}()
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to get cli image: %w", err)
}

if err := ignitionserverreconciliation.ReconcileIgnitionServer(ctx,
r.Client,
createOrUpdate,
releaseVersion,
utilitiesImage,
configOperatorImage,
hcp,
defaultIngressDomain,
ignitionServerHasHealthzHandler,
Expand Down
25 changes: 14 additions & 11 deletions ignition-server/cmd/run_local_ignitionprovider.go
Expand Up @@ -21,10 +21,11 @@ import (
)

type RunLocalIgnitionProviderOptions struct {
Namespace string
Image string
TokenSecret string
WorkDir string
Namespace string
Image string
TokenSecret string
WorkDir string
FeatureGateManifest string
}

func NewRunLocalIgnitionProviderCommand() *cobra.Command {
Expand All @@ -39,6 +40,7 @@ func NewRunLocalIgnitionProviderCommand() *cobra.Command {
cmd.Flags().StringVar(&opts.Image, "image", opts.Image, "Release image")
cmd.Flags().StringVar(&opts.TokenSecret, "token-secret", opts.TokenSecret, "Token secret name")
cmd.Flags().StringVar(&opts.WorkDir, "dir", opts.WorkDir, "Working directory (default: temporary dir)")
cmd.Flags().StringVar(&opts.FeatureGateManifest, "feature-gate-manifest", opts.FeatureGateManifest, "Path to a rendered featuregates.config.openshift.io/v1 manifest")

cmd.RunE = func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -94,13 +96,14 @@ func (o *RunLocalIgnitionProviderOptions) Run(ctx context.Context) error {
}

p := &controllers.LocalIgnitionProvider{
Client: cl,
ReleaseProvider: &releaseinfo.RegistryClientProvider{},
CloudProvider: "",
Namespace: o.Namespace,
WorkDir: o.WorkDir,
PreserveOutput: true,
ImageFileCache: imageFileCache,
Client: cl,
ReleaseProvider: &releaseinfo.RegistryClientProvider{},
CloudProvider: "",
Namespace: o.Namespace,
WorkDir: o.WorkDir,
PreserveOutput: true,
ImageFileCache: imageFileCache,
FeatureGateManifest: o.FeatureGateManifest,
}

payload, err := p.GetPayload(ctx, o.Image, config.String(), "")
Expand Down
17 changes: 10 additions & 7 deletions ignition-server/cmd/start.go
Expand Up @@ -56,6 +56,7 @@ type Options struct {
Platform string
WorkDir string
MetricsAddr string
FeatureGate string
}

// This is a https server that enable us to satisfy
Expand Down Expand Up @@ -88,6 +89,7 @@ func NewStartCommand() *cobra.Command {
cmd.Flags().StringVar(&opts.Platform, "platform", "", "The cloud provider platform name")
cmd.Flags().StringVar(&opts.WorkDir, "work-dir", opts.WorkDir, "Directory in which to store transient working data")
cmd.Flags().StringVar(&opts.MetricsAddr, "metrics-addr", opts.MetricsAddr, "The address the metric endpoint binds to.")
cmd.Flags().StringVar(&opts.FeatureGate, "feature-gate", opts.FeatureGate, "Path to a rendered featuregates.config.openshift.io/v1 file")

cmd.Run = func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -110,7 +112,7 @@ func NewStartCommand() *cobra.Command {

// setUpPayloadStoreReconciler sets up manager with a TokenSecretReconciler controller
// to keep the PayloadStore up to date.
func setUpPayloadStoreReconciler(ctx context.Context, registryOverrides map[string]string, cloudProvider hyperv1.PlatformType, cacheDir string, metricsAddr string) (ctrl.Manager, error) {
func setUpPayloadStoreReconciler(ctx context.Context, registryOverrides map[string]string, cloudProvider hyperv1.PlatformType, cacheDir string, metricsAddr string, featureGateManifest string) (ctrl.Manager, error) {
if os.Getenv(namespaceEnvVariableName) == "" {
return nil, fmt.Errorf("environment variable %s is empty, this is not supported", namespaceEnvVariableName)
}
Expand Down Expand Up @@ -153,11 +155,12 @@ func setUpPayloadStoreReconciler(ctx context.Context, registryOverrides map[stri
},
OpenShiftImageRegistryOverrides: util.ConvertImageRegistryOverrideStringToMap(os.Getenv("OPENSHIFT_IMG_OVERRIDES")),
},
Client: mgr.GetClient(),
Namespace: os.Getenv(namespaceEnvVariableName),
CloudProvider: cloudProvider,
WorkDir: cacheDir,
ImageFileCache: imageFileCache,
Client: mgr.GetClient(),
Namespace: os.Getenv(namespaceEnvVariableName),
CloudProvider: cloudProvider,
WorkDir: cacheDir,
ImageFileCache: imageFileCache,
FeatureGateManifest: featureGateManifest,
},
}).SetupWithManager(ctx, mgr); err != nil {
return nil, fmt.Errorf("unable to create controller: %w", err)
Expand All @@ -178,7 +181,7 @@ func run(ctx context.Context, opts Options) error {
return fmt.Errorf("failed to load serving cert: %w", err)
}

mgr, err := setUpPayloadStoreReconciler(ctx, opts.RegistryOverrides, hyperv1.PlatformType(opts.Platform), opts.WorkDir, opts.MetricsAddr)
mgr, err := setUpPayloadStoreReconciler(ctx, opts.RegistryOverrides, hyperv1.PlatformType(opts.Platform), opts.WorkDir, opts.MetricsAddr, opts.FeatureGate)
if err != nil {
return fmt.Errorf("error setting up manager: %w", err)
}
Expand Down
22 changes: 22 additions & 0 deletions ignition-server/controllers/local_ignitionprovider.go
Expand Up @@ -66,6 +66,11 @@ type LocalIgnitionProvider struct {
// deleted after use.
PreserveOutput bool

// FeatureGateManifest is the path to a rendered feature gate manifest.
// This must be copied into the MCC directory as it is required
// to render the ignition payload.
FeatureGateManifest string

ImageFileCache *imageFileCache

lock sync.Mutex
Expand Down Expand Up @@ -235,6 +240,23 @@ func (p *LocalIgnitionProvider) GetPayload(ctx context.Context, releaseImage str
return nil, fmt.Errorf("failed to extract templates from image: %w", err)
}

// Write out the feature gate manifest to the MCC dir.
// Use the feature gate from the hosted control plane which should reflect the feature gate of the cluster.
if err := func() error {
featureGateBytes, err := os.ReadFile(p.FeatureGateManifest)
if err != nil {
return fmt.Errorf("failed to read feature gate: %w", err)
}

if err := os.WriteFile(filepath.Join(mccBaseDir, "99_feature-gate.yaml"), featureGateBytes, 0644); err != nil {
return fmt.Errorf("failed to write feature gate: %w", err)
}

return nil
}(); err != nil {
return nil, fmt.Errorf("failed to extract feature gate: %w", err)
}

// Extract binaries from the MCO image into the bin directory
err = func() error {
start := time.Now()
Expand Down

0 comments on commit 31463cf

Please sign in to comment.