Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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"
```

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