Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
3 changes: 2 additions & 1 deletion applications/accounts/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
FROM quay.io/keycloak/keycloak:26.5
ARG CLOUDHARNESS_ROOT_BASE=quay.io/keycloak/keycloak:26.5
FROM $CLOUDHARNESS_ROOT_BASE

EXPOSE 9000
EXPOSE 8080
Expand Down
9 changes: 5 additions & 4 deletions applications/nfsserver/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# compile provisioner
FROM golang:1.26 AS provisioner
ARG CLOUDHARNESS_BASE_GOLANG=golang:1.26
ARG CLOUDHARNESS_BASE_ROCKYLINUX=golang:rockylinux/rockylinux:10.1-minimal
FROM $CLOUDHARNESS_BASE_GOLANG AS provisioner

WORKDIR /usr/src/app
COPY nfs-subdir-external-provisioner ./
Expand All @@ -17,10 +19,9 @@ RUN go mod tidy && go build -o /usr/local/bin/nfsvol .
# Upstream continuation of the old k8s.gcr.io/volume-nfs:0.8 image, maintained
# by the Kubernetes project at test/images/volume/nfs. CentOS Stream 9 base
# with nfs-utils already installed; we only need to add e2fsprogs for mkfs.ext4.
FROM rockylinux/rockylinux:10.2-minimal
FROM $CLOUDHARNESS_BASE_ROCKYLINUX

RUN microdnf update -y \
&& microdnf install -y \
RUN microdnf install -y \
e2fsprogs \
rpcbind \
nfs-utils \
Expand Down
3 changes: 2 additions & 1 deletion applications/sentry/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
FROM sentry:9.1.2
ARG CLOUDHARNESS_ROOT_BASE=sentry:9.1.2
FROM $CLOUDHARNESS_ROOT_BASE

ADD sentryrunner.sh ./sentryrunner.sh
RUN chmod +x ./sentryrunner.sh
Expand Down
75 changes: 75 additions & 0 deletions docs/base-common-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,78 @@ libraries or to provide new libraries to share within all applications.

To override cloudharness-base, create a directory `MY_SOLUTION/infrastructure/base-images/cloudharness-base`
then run `harness-deployment cloudharness MY_SOLUTION`


## Change the base image of any application (`FROM [xxx]`)

It's possible to change the image each dockerfile inherits by using the `FROM` directive.
Depending on the kind of application, changing the base image is done in two different ways.
There is basically two main situations:

. your application has a `Dockerfile` (e.g: Keycloak, the app you're building);
. your application doesn't have a `Dockerfile` (e.g: gogatekeeper, Kafka, etc), and the image is directly injected inside the helm chart when the helm chart is generated.

In both situations, the modification is done in the `value-template.yaml` of your CloudHarness projects, but the YAML path to modify depends on the app your targetting then.

### Change the base image for applications with a `Dockerfile`

Changing the base image for your application or applications which have a `Dockerfile` is done through the `source-images` entry of your `value-template.yaml` file.
This entry defines a mapping between the `ARG` of your `Dockerfile` and the value you want to inject.
Here is an example of a declared mapping for two applications:

```yaml
# value-template.yaml
image-sources:
myapp: "mybaseimg:14.5"
samples:
CLOUDHARNESS_FRONTEND_BUILD: "myfrontendbaseimg:15.6"
Comment thread
filippomc marked this conversation as resolved.
Outdated
```

The first entry for `myapp` will change the base image for `mybaseimg:14.5`.
As nothing is given as detail for the base image key inside the `Dockerfile`, by convention, CloudHarness is going to set `CLOUDHARNESS_ROOT_BASE` to `mybaseimg:14.5`.
If your `Dockerfile` doesn't use the `CLOUDHARNESS_ROOT_BASE` `ARG` as variable for your `FROM ...` directive, you can precise the name of the variable which needs to be changed by declaring it and the value it needs to have as a YAML dictionnary.
The second entry is in this situation, the base `ARG` which is used in the `Dockerfile` for the `FROM ...` directive is `CLOUDHARNESS_FRONTEND_BUILD`.

### Change the base image for applications which inject their image in the helm chart

Those applications are not providing a Dockerfile, but directly an image which is injcted in the helm chart from the helm template.
Each of those applications considers a specific path from the helm configuration (i.e: `values.yaml` or `value-template.yaml`).
To change the base image for those, you need to know this path and change it in the `value-template.yaml` by redefining it.
The redefinition will override the base value from the sub-`values.yaml` files on configuration merging.
Here is an example for the gatekeeper and the Argo controller:

```yaml
# value-template.yaml
harness:
proxy:
gatekeeper:
image: YOUR_NEW_IMG

# or, depending on your application
argo:
controller:
image:
registry: YOUR_REG
tag: YOUR TAG
```

Here is a table of the identified applications inside of CloudHarness which do not own a `Dockerfile`.

| Application | Image path |
| --- | --- |
| gatekeeper | harness.proxy.gatekeeper.image |
| Argo controller | argo.controller.image.{repository, tag} |
| Argo executor | argo.executor.image.{repository, tag} |
| Argo server | argo.server.image.{repository, tag} |
| Elasticsearch | elasticsearch.image |
| Events | events.image.{repository, tag, nullPolicy} |
| Events kafka | events.kafka.image |
| JupyterHub | jupyterhub.singleuser.image.{pullSecrets, name, tag} |
| JupyterHub proxy | jupyterhub.proxy.chp.image.{name, tag, pullPolicy} |
| JupyterHub scheduling | jupyterhub.scheduling.userPlaceHolder.image.{name, pullPolicy, tag} |
| NFS server | nfsserver.harness.deployment.image |
| Sentry redis | sentry.redis.image |
| MongoDB | harness.database.mongo.image |
| Neo4J | harness.database.neo4j.image |
| Neo4J reverseProxy | neo4j.reverseProxy.image |
| Postgres | harness.database.postgres.image |
4 changes: 2 additions & 2 deletions infrastructure/base-images/cloudharness-base/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ARG PARENT=python:3.12-slim-trixie
FROM ${PARENT}
ARG CLOUDHARNESS_ROOT_BASE=python:3.12-slim-trixie
FROM $CLOUDHARNESS_ROOT_BASE

RUN apt update && apt install -y --no-install-recommends \
gcc \
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
FROM node:22-alpine


ARG CLOUDHARNESS_ROOT_BASE=node:22-alpine
FROM $CLOUDHARNESS_ROOT_BASE
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
VALUE_TEMPLATE_PATH = f'{DEPLOYMENT_CONFIGURATION_PATH}/value-template.yaml'

CH_BASE_IMAGES = {'cloudharness-base': 'python:3.9.10'}
CH_BASE_IMAGE_CONVENTIONAL_KEY = "CLOUDHARNESS_ROOT_BASE"


CD_BUILD_STEP_PARALLEL = 'build_application_images'
Expand Down
9 changes: 5 additions & 4 deletions test/test-e2e/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
FROM ghcr.io/puppeteer/puppeteer:24
ARG CLOUDHARNESS_ROOT_BASE=ghcr.io/puppeteer/puppeteer:24
FROM $CLOUDHARNESS_ROOT_BASE

USER root
RUN mkdir -p /home/test
RUN chown -R pptruser /home/test
WORKDIR /home/test
# Install Chrome for the root user: Codefresh runs the container as root
RUN npx puppeteer browsers install chrome
RUN npx puppeteer browsers install chrome

USER pptruser
COPY --chown=pptruser package.json .
COPY --chown=pptruser yarn.lock .
RUN yarn install --timeout 99999999

COPY --chown=pptruser . .
COPY --chown=pptruser . .
ENV APP_URL=https://github.com

CMD ["yarn", "test"]
CMD ["yarn", "test"]
31 changes: 20 additions & 11 deletions tools/deployment-cli-tools/ch_cli_tools/skaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from cloudharness_model import ApplicationTestConfig, HarnessMainConfig, GitDependencyConfig

from cloudharness_utils.constants import APPS_PATH, DEPLOYMENT_CONFIGURATION_PATH, \
BASE_IMAGES_PATH, STATIC_IMAGES_PATH, HELM_ENGINE, COMPOSE_ENGINE
BASE_IMAGES_PATH, STATIC_IMAGES_PATH, HELM_ENGINE, COMPOSE_ENGINE, \
CH_BASE_IMAGE_CONVENTIONAL_KEY
from .helm import KEY_APPS, KEY_HARNESS, KEY_DEPLOYMENT, KEY_TASK_IMAGES
from .utils import get_template, dict_merge, find_dockerfiles_paths, app_name_from_path, yaml, \
find_file_paths, guess_build_dependencies_from_dockerfile, get_json_template, clean_image_name
Expand Down Expand Up @@ -59,9 +60,9 @@ def resolve_dockerfile_name(dockerfile_dir_rel):
def build_artifact(
app_name: str,
context_path: str,
requirements: list[str] = None,
requirements: list[str] | None = None,
dockerfile_path: str = '',
additional_build_args: dict[str, str] = None,
additional_build_args: dict[str, str] | None = None,
) -> dict:
build_args = {
'DEBUG': 'true' if helm_values.local or helm_values.debug else ''
Expand Down Expand Up @@ -107,12 +108,14 @@ def process_build_dockerfile(
builds[app_name] = context_path
base_images.add(clean_image_name(app_name))

additional_build_args = get_additional_build_args(helm_values, app_key) | get_image_source(helm_values, app_key)

artifacts[app_name] = build_artifact(
app_name,
context_path,
dockerfile_path=relpath(dockerfile_path, output_path),
requirements=requirements or guess_build_dependencies_from_dockerfile(dockerfile_path),
additional_build_args=get_additional_build_args(helm_values, app_key),
additional_build_args=additional_build_args,
)

if app_key in helm_values.apps and helm_values.apps[app_key].harness.dependencies and helm_values.apps[app_key].harness.dependencies.git:
Expand Down Expand Up @@ -328,10 +331,16 @@ def get_image_tag(name):


def get_additional_build_args(helm_values: HarnessMainConfig, app_key: str) -> dict[str, str]:
if app_key not in helm_values.apps:
return None

if not (helm_values.apps[app_key].harness.dockerfile and helm_values.apps[app_key].harness.dockerfile.build_args):
return None

return helm_values.apps[app_key].harness.dockerfile.build_args
try:
return helm_values.apps[app_key].harness.dockerfile.build_args
except (KeyError, AttributeError):
return {}


def get_image_source(helm_values: HarnessMainConfig, app_key: str) -> dict[str, str]:
source_image = helm_values.get("source-images", {}).get(app_key)
if isinstance(source_image, dict):
return source_image
elif source_image:
return {CH_BASE_IMAGE_CONVENTIONAL_KEY: source_image}
return {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
mainapp: myapp
domain: myapp.local
name: testprojectname
a: b
database:
auto: true

source-images:
myapp: "myregistry.myapp:15.3"
samples:
BASEIMAGE: myother.image:14

events:
kafka:
image: nodocker.io/apache/kafka:4.0.2
74 changes: 74 additions & 0 deletions tools/deployment-cli-tools/tests/test_skaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,77 @@ def test_skaffold_builds_cross_app_task_image(tmp_path):

shutil.rmtree(tmp_path)
shutil.rmtree(BUILD_DIR)


def test_skaffold_imgarg_retrieval(tmp_path):
out_folder = tmp_path / "test_skaffold_imgarg_retrieval"

values = create_helm_chart(
[CLOUDHARNESS_ROOT, RESOURCES],
output_path=out_folder,
include=["samples", "myapp"],
domain="my.local",
namespace="test",
env="nreg",
local=False,
tag=1,
registry="reg",
)

assert values.get("events").kafka.image == "nodocker.io/apache/kafka:4.0.2"

# Ensure in the test that the Helm is well formed
source_images = values.get("source-images")
assert len(source_images) == 2
assert source_images["myapp"] == "myregistry.myapp:15.3"
assert source_images["samples"] == {"BASEIMAGE": "myother.image:14"}

assert get_image_source(values, "myapp") == {CH_BASE_IMAGE_CONVENTIONAL_KEY: "myregistry.myapp:15.3"}
assert get_image_source(values, "samples") == {"BASEIMAGE": "myother.image:14"}
assert get_image_source(values, "events") == {}


def test_skaffold_imgarg(tmp_path):
out_folder = tmp_path / "test_skaffold_imgarg"

values = create_helm_chart(
[CLOUDHARNESS_ROOT, RESOURCES],
output_path=out_folder,
include=["samples", "myapp"],
domain="my.local",
namespace="test",
env="nreg",
local=False,
tag=1,
registry="reg",
)

assert values.get("events").kafka.image == "nodocker.io/apache/kafka:4.0.2"

BUILD_DIR = "/tmp/build"
root_paths = preprocess_build_overrides(
root_paths=[CLOUDHARNESS_ROOT, RESOURCES],
helm_values=values,
merge_build_path=BUILD_DIR,
)

sk = create_skaffold_configuration(
root_paths=root_paths, helm_values=values, output_path=out_folder
)

# Look in sk
sk.get("build").get("artifacts")

def get_buildargs(name) -> dict[str, str]:
f = [e["docker"]["buildArgs"] for e in sk["build"]["artifacts"] if f"applications/{name}" in e["context"]]
if len(f) > 0:
return f[0]
return {}

samples_buildargs = get_buildargs("samples")
assert "BASEIMAGE" in samples_buildargs
assert samples_buildargs["BASEIMAGE"] == "myother.image:14"

myapp_buildargs = get_buildargs("myapp")
assert CH_BASE_IMAGE_CONVENTIONAL_KEY in myapp_buildargs
assert myapp_buildargs[CH_BASE_IMAGE_CONVENTIONAL_KEY] == "myregistry.myapp:15.3"
Loading