diff --git a/Containerfile b/Containerfile index 3870adda..05c2f186 100644 --- a/Containerfile +++ b/Containerfile @@ -58,6 +58,14 @@ RUN \ FROM registry.access.redhat.com/ubi9-minimal:9.2 AS runtime +# Install the binary: COPY \ --from=builder \ /home/builder/project/oran-o2ims /usr/bin/oran-o2ims + +# We need to explictly run the servers a a non-root user, otherwise if the container is in a pod +# with `runAsNonRoot: true` in the security context it will fail to start. In addition the user +# needs to be specified numerically, not with a user name like `nobody`, because otherwise the +# cluster can't verify if it is root or not. +USER \ + 65534:65534 diff --git a/Dockerfile b/Dockerfile index 0705e49b..e5ad8252 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,7 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot WORKDIR / -COPY --from=builder /workspace/oran-o2ims . +COPY --from=builder /workspace/oran-o2ims /usr/bin USER 65532:65532 -ENTRYPOINT ["/oran-o2ims"] +ENTRYPOINT ["/usr/bin/oran-o2ims"] diff --git a/api/v1alpha1/orano2ims_types.go b/api/v1alpha1/orano2ims_types.go index a3e4c707..bbda31e5 100644 --- a/api/v1alpha1/orano2ims_types.go +++ b/api/v1alpha1/orano2ims_types.go @@ -28,6 +28,13 @@ type ORANO2IMSSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file + // Image is the full reference of the container image that contains the binary. This is + // optional and the default will be the value passed to the `--image` command line flag of + // the controller manager. + // + //+optional + Image string `json:"image"` + CloudId string `json:"cloudId"` //+kubebuilder:default=false MetadataServer bool `json:"metadataServer"` diff --git a/config/crd/bases/oran.openshift.io_orano2imses.yaml b/config/crd/bases/oran.openshift.io_orano2imses.yaml index 35329f13..7d58662f 100644 --- a/config/crd/bases/oran.openshift.io_orano2imses.yaml +++ b/config/crd/bases/oran.openshift.io_orano2imses.yaml @@ -56,6 +56,12 @@ spec: items: type: string type: array + image: + description: Image is the full reference of the container image that + contains the binary. This is optional and the default will be the + value passed to the `--image` command line flag of the controller + manager. + type: string ingressHost: type: string metadataServer: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index eb97256b..d8b70aa2 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -11,3 +11,20 @@ images: - name: controller newName: quay.io/openshift-kni/oran-o2ims-operator newTag: 4.16.0 + +replacements: + +# This replacment copies the controller image into the `IMAGE` environment variable of the pod, +# which is in turn passed to the `--image` command line flag of the `start controller-manager` +# command. The net result is that the operator knows what is its image and can use it as the +# default for the servers. +- source: + fieldPath: spec.template.spec.containers.[name=manager].image + kind: Deployment + name: controller-manager + targets: + - fieldPaths: + - spec.template.spec.containers.[name=manager].env.[name=IMAGE].value + select: + kind: Deployment + name: controller-manager diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index f3797a53..8f4cbce5 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -66,13 +66,28 @@ spec: # seccompProfile: # type: RuntimeDefault containers: - - command: - - /oran-o2ims - - start - - controller-manager - - --leader-elect + - name: manager image: controller:latest - name: manager + env: + # Note that we have a kustomization replacement that copies the above `image` into this + # environment variable. + - name: IMAGE + value: controller:latest + command: + # We use a shell script here in order to be able to pass the value of the `IMAGE` + # environment variable in the `--image` flag. We don't wan to read environment variables + # directly from the binary because all the other command use flags as their main + # configuration mechanism, and we don't want to deviate from that. + # + # Please keep the `exec` before the command, it removes the shell from the picture once + # the controller manager starts. + - /usr/bin/bash + - -c + - | + exec \ + /usr/bin/oran-o2ims start controller-manager \ + --leader-elect \ + --image="${IMAGE}" securityContext: allowPrivilegeEscalation: false capabilities: diff --git a/internal/cmd/operator/start_controller_manager.go b/internal/cmd/operator/start_controller_manager.go index 42bb4abb..eccab26f 100644 --- a/internal/cmd/operator/start_controller_manager.go +++ b/internal/cmd/operator/start_controller_manager.go @@ -69,8 +69,8 @@ func ControllerManager() *cobra.Command { ) flags.StringVar( &c.image, - "image", - "quay.io/openshift-kni:latest", + imageFlagName, + "quay.io/openshift-kni/oran-o2ims:latest", "Reference of the container image containing the servers.", ) return result @@ -113,6 +113,16 @@ func (c *ControllerManagerCommand) run(cmd *cobra.Command, argv []string) error ctrl.SetLogger(adapter) klog.SetLogger(adapter) + // Check the flags: + if c.image == "" { + logger.ErrorContext( + ctx, + "Image flag is mandatory", + slog.String("flag", imageFlagName), + ) + return exit.Error(1) + } + // Restrict to the following namespaces - subject to change. namespaces := [...]string{"default", "oran", "o2ims", "oran-o2ims"} // List of Namespaces defaultNamespaces := make(map[string]cache.Config) @@ -154,6 +164,7 @@ func (c *ControllerManagerCommand) run(cmd *cobra.Command, argv []string) error if err = (&controllers.Reconciler{ Client: mgr.GetClient(), Logger: slog.With("controller", "ORAN-O2IMS"), + Image: c.image, }).SetupWithManager(mgr); err != nil { logger.Error( "Unable to create controller", @@ -178,7 +189,10 @@ func (c *ControllerManagerCommand) run(cmd *cobra.Command, argv []string) error return exit.Error(1) } - logger.Info("Starting manager") + logger.Info( + "Starting manager", + slog.String("image", c.image), + ) if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { logger.Error( "Problem running manager", @@ -189,3 +203,8 @@ func (c *ControllerManagerCommand) run(cmd *cobra.Command, argv []string) error return nil } + +// Names of command line flags: +const ( + imageFlagName = "image" +) diff --git a/internal/controllers/orano2ims_controller.go b/internal/controllers/orano2ims_controller.go index dfd36d92..16bf557c 100644 --- a/internal/controllers/orano2ims_controller.go +++ b/internal/controllers/orano2ims_controller.go @@ -62,6 +62,7 @@ import ( type Reconciler struct { client.Client Logger *slog.Logger + Image string } // reconcilerTask contains the information and logic needed to perform a single reconciliation @@ -69,6 +70,7 @@ type Reconciler struct { // parameters. type reconcilerTask struct { logger *slog.Logger + image string client client.Client object *oranv1alpha1.ORANO2IMS } @@ -101,6 +103,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (resul task := &reconcilerTask{ logger: r.Logger, client: r.Client, + image: r.Image, object: object, } result, err = task.run(ctx) @@ -404,6 +407,12 @@ func (t *reconcilerTask) deployServer(ctx context.Context, serverName string) er return err } + // Select the container image to use: + image := t.object.Spec.Image + if image == "" { + image = t.image + } + // Build the deployment's spec. deploymentSpec := appsv1.DeploymentSpec{ Replicas: k8sptr.To(int32(1)), @@ -424,7 +433,7 @@ func (t *reconcilerTask) deployServer(ctx context.Context, serverName string) er Containers: []corev1.Container{ { Name: "server", - Image: utils.ORANImage, + Image: image, ImagePullPolicy: "Always", VolumeMounts: deploymentVolumeMounts, Command: []string{"/usr/bin/oran-o2ims"}, diff --git a/internal/controllers/utils/constants.go b/internal/controllers/utils/constants.go index 57fd259b..b200debe 100644 --- a/internal/controllers/utils/constants.go +++ b/internal/controllers/utils/constants.go @@ -1,10 +1,5 @@ package utils -// Default image -const ( - ORANImage = "quay.io/openshift-kni/oran-o2ims:latest" -) - // Default namespace const ( ORANO2IMSNamespace = "oran-o2ims"