Skip to content

Commit

Permalink
Make image configurable (#93)
Browse files Browse the repository at this point in the history
Currently the container image used by the servers is hardcoded in the Go
source code. This complicates development because when the operator is
deployed it isn't possible to use a custom image. To simplify that this
patch adds a new `--image` command line flag to the `start
controller-manager` command. The default will be
`quay.io/openshift-kni/oran-o2ims:latest`, but it will be possible to
change it in the the development environment, in two different ways:

1. Changing the `IMG` make variable, for example:

  ```
  $ make IMG=quay.io/myuser/myimage:123 build deploy
  ```

2. Using the `spec.image` field of the custom resource.

The patch also changes The kustomization files used to generate tha
operator manifests so that the selected image is automatically passed to
the `--image` command line flag.

Signed-off-by: Juan Hernandez <[email protected]>
  • Loading branch information
jhernand authored Apr 19, 2024
1 parent 029ce20 commit e39f1ea
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 17 deletions.
8 changes: 8 additions & 0 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
7 changes: 7 additions & 0 deletions api/v1alpha1/orano2ims_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/oran.openshift.io_orano2imses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
17 changes: 17 additions & 0 deletions config/manager/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
27 changes: 21 additions & 6 deletions config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
25 changes: 22 additions & 3 deletions internal/cmd/operator/start_controller_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -189,3 +203,8 @@ func (c *ControllerManagerCommand) run(cmd *cobra.Command, argv []string) error

return nil
}

// Names of command line flags:
const (
imageFlagName = "image"
)
11 changes: 10 additions & 1 deletion internal/controllers/orano2ims_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,15 @@ import (
type Reconciler struct {
client.Client
Logger *slog.Logger
Image string
}

// reconcilerTask contains the information and logic needed to perform a single reconciliation
// task. This reduces the need to pass things like the current state of the object as function
// parameters.
type reconcilerTask struct {
logger *slog.Logger
image string
client client.Client
object *oranv1alpha1.ORANO2IMS
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)),
Expand All @@ -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"},
Expand Down
5 changes: 0 additions & 5 deletions internal/controllers/utils/constants.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package utils

// Default image
const (
ORANImage = "quay.io/openshift-kni/oran-o2ims:latest"
)

// Default namespace
const (
ORANO2IMSNamespace = "oran-o2ims"
Expand Down

0 comments on commit e39f1ea

Please sign in to comment.