Skip to content

Commit 4760b2a

Browse files
Utility for private registries and installer image rootless (#48)
1 parent d1a6e3a commit 4760b2a

14 files changed

+238
-41
lines changed

charts/gitops-runtime/Chart.yaml

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: v2
22
appVersion: 0.1.29
33
description: A Helm chart for Codefresh gitops runtime
44
name: gitops-runtime
5-
version: 0.2.6-alpha
5+
version: 0.2.7-alpha
66
home: https://github.com/codefresh-io/gitops-runtime-helm
77
icon: https://avatars1.githubusercontent.com/u/11412079?v=3
88
keywords:
@@ -16,9 +16,9 @@ annotations:
1616
artifacthub.io/prerelease: "true"
1717
artifacthub.io/changes: |
1818
- kind: changed
19-
description: updated `argo-cd` to `v2.6.0-cap-CR-18430-del-app` (fix application/git-source deletion)
20-
- kind: fixed
21-
description: Fix delete runtime hook when using custom CA
19+
description: Add private registry utililies
20+
- kind: changed
21+
description: Installer image for hooks now runs rootless
2222
dependencies:
2323
- name: argo-cd
2424
repository: https://codefresh-io.github.io/argo-helm

charts/gitops-runtime/README.md

+21-21
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
1-
# gitops-runtime
1+
## Codefresh gitops runtime
2+
![Version: 0.2.7-alpha](https://img.shields.io/badge/Version-0.2.7--alpha-informational?style=flat-square) ![AppVersion: 0.1.29](https://img.shields.io/badge/AppVersion-0.1.29-informational?style=flat-square)
23

3-
![Version: 0.2.6-alpha](https://img.shields.io/badge/Version-0.2.6--alpha-informational?style=flat-square) ![AppVersion: 0.1.29](https://img.shields.io/badge/AppVersion-0.1.29-informational?style=flat-square)
4+
## Codefresh official documentation:
5+
Prior to running the installation please see the official documentation at: https://codefresh.io/docs/docs/installation/gitops/hybrid-gitops-helm-installation/
46

5-
A Helm chart for Codefresh gitops runtime
7+
## Using with private registries - Helper utility
8+
The GitOps Runtime comprises multiple subcharts and container images. Subcharts also vary in values structure, making it difficult to override image specific values to use private registries.
9+
We have created a helper utility to resolve this issue:
10+
- The utility create values files in the correct structure, overriding the registry for each image. When installing the chart, you can then provide those values files to override all images.
11+
- The utility also creates other files with data to help you identify and correctly mirror all the images.
612

7-
**Homepage:** <https://github.com/codefresh-io/gitops-runtime-helm>
13+
#### Usage
814

9-
## Maintainers
15+
The utility is packaged in a container image. Below are instructions on executing the utility using Docker:
1016

11-
| Name | Email | Url |
12-
| ---- | ------ | --- |
13-
| codefresh | | <https://codefresh-io.github.io/> |
17+
```
18+
docker run -v <output_dir>:/output quay.io/codefresh/gitops-runtime-private-registry-utils:0.2.7-alpha <local_registry>
19+
```
20+
`output_dir` - is a local directory where the utility will output files. <br>
21+
`local_registry` - is your local registry where you want to mirror the images to
1422

15-
## Requirements
16-
17-
| Repository | Name | Version |
18-
|------------|------|---------|
19-
| https://bitnami-labs.github.io/sealed-secrets/ | sealed-secrets | 2.7.3 |
20-
| https://chartmuseum.codefresh.io/codefresh-tunnel-client | tunnel-client(codefresh-tunnel-client) | 0.1.12 |
21-
| https://codefresh-io.github.io/argo-helm | argo-cd | 5.29.2-cap-CR-18430 |
22-
| https://codefresh-io.github.io/argo-helm | argo-events | 2.0.5-1-cf-init |
23-
| https://codefresh-io.github.io/argo-helm | argo-rollouts | 2.22.1-1-cap-sw |
24-
| https://codefresh-io.github.io/argo-helm | argo-workflows | 0.22.9-1-CR-17426 |
23+
The utility will output 4 files into the folder:
24+
1. `image-list.txt` - is the list of all images used in this version of the chart. Those are the images that you need to mirror.
25+
2. `image-mirror.csv` - is a csv file with 2 fields - source_image and target_image. source_image is the image with the original registry and target_image is the image with the private registry. Can be used as an input file for a mirroring script.
26+
3. `values-images-no-tags.yaml` - a values file with all image values with the private registry **excluding tags**. If provided through --values to helm install/upgrade command - it will override all images to use the private registry.
27+
4. `values-images-with-tags.yaml` - The same as 3 but with tags **included**.
2528

2629
## Values
2730

@@ -184,6 +187,3 @@ A Helm chart for Codefresh gitops runtime
184187
| tunnel-client | object | `{"enabled":true,"libraryMode":true,"tunnelServer":{"host":"register-tunnels.cf-cd.com","subdomainHost":"tunnels.cf-cd.com"}}` | Tunnel based runtime. Not supported for on-prem platform. In on-prem use ingress based runtimes. |
185188
| tunnel-client.enabled | bool | `true` | Will only be used if global.runtime.ingress.enabled = false |
186189
| tunnel-client.libraryMode | bool | `true` | Do not change this value! Breaks chart logic |
187-
188-
----------------------------------------------
189-
Autogenerated from chart metadata using [helm-docs v1.9.1](https://github.com/norwoodj/helm-docs/releases/v1.9.1)
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Codefresh gitops runtime
2+
{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }}
3+
4+
## Codefresh official documentation:
5+
Prior to running the installation please see the official documentation at: https://codefresh.io/docs/docs/installation/gitops/hybrid-gitops-helm-installation/
6+
7+
## Using with private registries - Helper utility
8+
The GitOps Runtime comprises multiple subcharts and container images. Subcharts also vary in values structure, making it difficult to override image specific values to use private registries.
9+
We have created a helper utility to resolve this issue:
10+
- The utility create values files in the correct structure, overriding the registry for each image. When installing the chart, you can then provide those values files to override all images.
11+
- The utility also creates other files with data to help you identify and correctly mirror all the images.
12+
13+
#### Usage
14+
15+
The utility is packaged in a container image. Below are instructions on executing the utility using Docker:
16+
17+
```
18+
docker run -v <output_dir>:/output quay.io/codefresh/gitops-runtime-private-registry-utils:0.2.7-alpha <local_registry>
19+
```
20+
`output_dir` - is a local directory where the utility will output files. <br>
21+
`local_registry` - is your local registry where you want to mirror the images to
22+
23+
The utility will output 4 files into the folder:
24+
1. `image-list.txt` - is the list of all images used in this version of the chart. Those are the images that you need to mirror.
25+
2. `image-mirror.csv` - is a csv file with 2 fields - source_image and target_image. source_image is the image with the original registry and target_image is the image with the private registry. Can be used as an input file for a mirroring script.
26+
3. `values-images-no-tags.yaml` - a values file with all image values with the private registry **excluding tags**. If provided through --values to helm install/upgrade command - it will override all images to use the private registry.
27+
4. `values-images-with-tags.yaml` - The same as 3 but with tags **included**.
28+
29+
{{ template "chart.valuesSection" . }}

charts/gitops-runtime/ci/values-all-images.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# Values file used to render all image values
12
global:
23
codefresh:
34
accountId: 628a80b693a15c0f9c13ab75 # Codefresh Account id for ilia-codefresh for now, needs to be some test account
@@ -13,7 +14,6 @@ global:
1314

1415
runtime:
1516
name: default
16-
cluster: test-cluster
1717

1818
ingress:
1919
enabled: false

installer-image/Dockerfile

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ FROM --platform=$BUILDPLATFORM debian:bullseye-slim
22
RUN apt-get update -y && apt-get install curl -y
33
ARG CF_CLI_VERSION=v0.1.25
44
ARG KUBECTL_VERSION=v1.26.0
5-
ARG ARGOCD_VERSION=v2.4.15
65
ARG TARGETARCH
7-
RUN echo "${TARGETARCH}"
86
RUN curl -L --output - https://github.com/codefresh-io/cli-v2/releases/download/${CF_CLI_VERSION}/cf-linux-${TARGETARCH}.tar.gz | tar zx && mv ./cf-linux-${TARGETARCH} /usr/local/bin/cf
97
RUN curl -LO https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl && chmod +x kubectl && mv ./kubectl /usr/local/bin/kubectl
10-
RUN curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/${ARGOCD_VERSION}/argocd-linux-${TARGETARCH} && chmod +x /usr/local/bin/argocd
8+
USER 1000

scripts/all-image-values.sh

-4
This file was deleted.

scripts/list-images-for-mirrror.sh

-6
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
README.md
2+
Dockerfile
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM --platform=$BUILDPLATFORM python:3.11.3-slim-bullseye
2+
ARG TARGETARCH
3+
RUN cd /tmp && python3 -c "from urllib.request import urlretrieve; urlretrieve('https://get.helm.sh/helm-v3.12.0-linux-${TARGETARCH}.tar.gz', 'helm-v3.12.0-linux-${TARGETARCH}.tar.gz')" && tar -xvf helm-v3.12.0-linux-${TARGETARCH}.tar.gz && chmod +x linux-${TARGETARCH}/helm && mv linux-${TARGETARCH}/helm /usr/local/bin/helm && rm -rf /tmp/*
4+
COPY charts/gitops-runtime /chart
5+
RUN helm dependency update /chart
6+
COPY scripts/private-registry-utils/python-requirements.txt /scripts/python-requirements.txt
7+
RUN pip3 install -r /scripts/python-requirements.txt
8+
COPY scripts/private-registry-utils /scripts
9+
RUN chmod -R +x /scripts
10+
WORKDIR /scripts
11+
# Output calculated values and filter image values
12+
RUN ./output-calculated-values.sh ./all-values.yaml && python3 ./helper-scripts/yaml-filter.py all-values.yaml image.repository,image.registry,image.tag,argo-events.configs.nats.versions,argo-events.configs.jetstream.versions > all-image-values.yaml
13+
ENTRYPOINT ["python3", "private-registry-utils.py", "all-image-values.yaml"]
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Utilities to assist with image mirroring to private registries
2+
3+
## How it works?
4+
5+
1. All calculated values are outputed using output-calculated-values.sh. The script contains a special template that handles image values. Specifically it derives tags for images that don't have explicit tags sepcified from the subchart appVersion.
6+
2. Those values are then filtered using the helper script yaml-filter.py to filter only the values that contain images.
7+
3. image-values-utils.py then uses the output of the previous steps and can generate:
8+
1. A list of all images
9+
2. Values files with and without tags including a custom registry for each image.
10+
3. A csv file with source and target image names (can help with image mirroring).
11+
12+
## Usage
13+
1. Build the image with buildcontext being the root of the repo `docker build -f scripts/private-registry-utils/Dockerfile -t <image-tag> .` For example: `docker build -f scripts/private-registry-utils/Dockerfile -t gitops-runtime-priv-reg .`
14+
2. Execute with: `docker run -v <local output directory>:/output -it <image-tag> <private registry>` for example: `docker run -v /tmp/output:/output -it gitops-runtime-priv-reg myregisry.example.com`
15+
3. See the generated files in local output folder

scripts/output-calculated-values.sh scripts/private-registry-utils/output-calculated-values.sh

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22
MYDIR=$(dirname $0)
3-
CHARTDIR="${MYDIR}/../charts/gitops-runtime"
3+
CHARTDIR="/chart"
44
VALUESFILE="${CHARTDIR}/ci/values-all-images.yaml"
55
OUTPUTFILE=$1
66
# This template prints all values and also sets tags for all images with non-empty repository value, where the tag is empty and should be derived from the appVersion of the subchart.
@@ -47,6 +47,5 @@ END
4747
)
4848

4949
echo -e "$ALL_VALUES_TEMPLATE" > $CHARTDIR/templates/all-values.yaml
50-
helm dependency update $CHARTDIR
5150
helm template --values $VALUESFILE --set getImages=true --show-only templates/all-values.yaml $CHARTDIR > $OUTPUTFILE
5251
rm $CHARTDIR/templates/all-values.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import argparse
2+
import yaml
3+
import sys
4+
import re
5+
import csv
6+
7+
DEFAULT_REGISTRY_URL = "registry.example.com"
8+
9+
def remove_duplicates(list_of_dicts, key):
10+
seen = set()
11+
deduplicated_list = []
12+
13+
for d in list_of_dicts:
14+
if d[key] not in seen:
15+
deduplicated_list.append(d)
16+
seen.add(d[key])
17+
18+
return deduplicated_list
19+
20+
def replace_registry_in_image(image_string, new_registry):
21+
if '/' in image_string:
22+
parts = image_string.split('/')
23+
if len(parts) >= 2:
24+
parts[0] = new_registry
25+
return '/'.join(parts)
26+
else:
27+
return new_registry + '/' + image_string
28+
29+
# Try to identify whether a string is a docker image
30+
def is_docker_image(image_string):
31+
pattern = r'^[a-zA-Z0-9/_-]+:[a-zA-Z0-9_.-]+$'
32+
return bool(re.match(pattern, image_string))
33+
34+
35+
def recurse_replace_registry(currValue,new_registry):
36+
if type(currValue) is dict:
37+
for key in currValue.keys():
38+
if key == "registry":
39+
currValue[key]=new_registry
40+
elif key == "repository" and "registry" not in currValue:
41+
currValue[key] = replace_registry_in_image(currValue[key],new_registry)
42+
elif type(currValue[key]) is str:
43+
if is_docker_image(currValue[key]):
44+
currValue[key] = replace_registry_in_image(currValue[key],new_registry)
45+
else:
46+
recurse_replace_registry(currValue[key],new_registry)
47+
elif type(currValue) is list:
48+
for item in currValue:
49+
recurse_replace_registry(item,new_registry)
50+
51+
def recurse_remove_tags(currValue):
52+
if type(currValue) is dict:
53+
if "tag" in currValue:
54+
currValue.pop("tag")
55+
else:
56+
for key in currValue.keys():
57+
recurse_remove_tags(currValue[key])
58+
elif type(currValue) is list:
59+
for item in currValue:
60+
recurse_remove_tags(item)
61+
62+
63+
def recurse_get_source_target(currValue,new_registry,lstSourceTarget):
64+
sourceImage = ""
65+
if type(currValue) is dict:
66+
for key in currValue.keys():
67+
if key == "registry":
68+
sourceImage += currValue[key] + "/"
69+
if key == "repository":
70+
sourceImage += currValue[key]
71+
if key == "tag":
72+
sourceImage += ":" + currValue[key]
73+
74+
elif type(currValue[key]) is str:
75+
if is_docker_image(currValue[key]):
76+
sourceImage = currValue[key]
77+
78+
recurse_get_source_target(currValue[key],new_registry,lstSourceTarget)
79+
80+
if len(sourceImage) > 0:
81+
lstSourceTarget.append({"source_image": sourceImage, "target_image": replace_registry_in_image(sourceImage,new_registry)})
82+
83+
84+
elif type(currValue) is list:
85+
for item in currValue:
86+
recurse_get_source_target(item,new_registry,lstSourceTarget)
87+
88+
def generate_file_from_field(list_of_dicts, field_name, output_file):
89+
with open(output_file, 'w+') as file:
90+
for d in list_of_dicts:
91+
field_value = d.get(field_name)
92+
if field_value:
93+
file.write(str(field_value) + '\n')
94+
95+
def generate_image_values(dictImageValues,outputDir):
96+
97+
with open(f"{outputDir}/values-images-with-tags.yaml", 'w+') as file:
98+
yaml.dump(dictImageValues, file)
99+
100+
recurse_remove_tags(dictImageValues)
101+
102+
with open(f"{outputDir}/values-images-no-tags.yaml", 'w+') as file:
103+
yaml.dump(dictImageValues, file)
104+
105+
def generate_image_list():
106+
# Code for generating image list
107+
print("Generating image list")
108+
109+
def generate_mirror_csv(lstSourceTarget, outputDir):
110+
111+
fields = ['source_image', 'target_image']
112+
113+
with open(f"{outputDir}/image-mirror.csv", 'w+') as csvfile:
114+
writer = csv.DictWriter(csvfile, fieldnames = fields)
115+
writer.writeheader()
116+
writer.writerows(lstSourceTarget)
117+
118+
def main():
119+
120+
parser = argparse.ArgumentParser(description="Codefresh gitops runtime - private registry utils")
121+
parser.add_argument("images_values_file", help="Input values.yaml file")
122+
parser.add_argument("private_registry_url", nargs="?", default=DEFAULT_REGISTRY_URL, help="Private Registry URL")
123+
parser.add_argument("--output-dir", help="Output directory",default="/output")
124+
parser.add_argument("--action", choices=["generate-image-values", "generate-image-list", "generate-mirror-csv"], help="Action to execute")
125+
args = parser.parse_args()
126+
127+
# Open yaml
128+
with open(args.images_values_file, 'r') as stream:
129+
try:
130+
dictImageValues=yaml.safe_load(stream)
131+
except yaml.YAMLError as e:
132+
print(e)
133+
134+
lstSourceTarget = []
135+
recurse_get_source_target(dictImageValues,args.private_registry_url,lstSourceTarget)
136+
lstSourceTarget = remove_duplicates(lstSourceTarget, "source_image")
137+
recurse_replace_registry(dictImageValues,args.private_registry_url)
138+
139+
140+
if args.action == "generate-mirror-csv" or args.action is None:
141+
generate_mirror_csv(lstSourceTarget,args.output_dir)
142+
143+
if args.action == "generate-image-list" or args.action is None:
144+
generate_file_from_field(lstSourceTarget,"source_image", f"{args.output_dir}/image-list.txt")
145+
146+
if args.action == "generate-image-values" or args.action is None:
147+
generate_image_values(dictImageValues,args.output_dir)
148+
149+
if __name__ == "__main__":
150+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PyYAML==6.0

0 commit comments

Comments
 (0)