Skip to content

Commit 56975cb

Browse files
committed
refactor: add II workflows
Usually we use those from github-actions repository, but that is impossible here since this is a public repository. JIRA: INFRA-3992
1 parent d5870d6 commit 56975cb

File tree

8 files changed

+984
-0
lines changed

8 files changed

+984
-0
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
name: Build Helm Releases
2+
on:
3+
workflow_call:
4+
inputs:
5+
charts:
6+
description: The list of charts filtered to be built.
7+
required: true
8+
type: string
9+
images:
10+
description: The list of built images.
11+
required: true
12+
type: string
13+
image_tag:
14+
description: The tag of the built images.
15+
required: true
16+
type: string
17+
chart_filters_path:
18+
description: Path to chart filters YAML file.
19+
required: false
20+
default: .github/chart-filters.yml
21+
type: string
22+
outputs:
23+
helmreleases:
24+
description: The dict with the helm releases to be deployed for each cluster.
25+
value: ${{ jobs.build-helmreleases.outputs.helmreleases }}
26+
updated_prod_cluster_names:
27+
description: The list of updated production cluster names.
28+
value: ${{ jobs.build-helmreleases.outputs.updated_prod_cluster_names }}
29+
staging_helmreleases:
30+
description: List of staging helm releases that shoul be waited for.
31+
value: ${{ jobs.build-helmreleases.outputs.staging_helmreleases }}
32+
built_charts:
33+
description: Dict of built charts including the chart name and version.
34+
value: ${{ jobs.build-helmreleases.outputs.built_charts }}
35+
36+
jobs:
37+
build-helmreleases:
38+
runs-on:
39+
group: infra1-runners-arc
40+
labels: runners-small
41+
outputs:
42+
helmreleases: ${{ steps.build-helmreleases.outputs.helmreleases }}
43+
built_charts: ${{ steps.get-built-charts.outputs.built_charts }}
44+
staging_helmreleases: ${{ steps.extract-staging-helmreleases.outputs.staging_helmreleases }}
45+
updated_prod_cluster_names: ${{ steps.extract-updated-prod-names.outputs.updated_prod_names }}
46+
steps:
47+
- name: Checkout repository
48+
uses: actions/checkout@v5
49+
50+
- name: Get built charts
51+
id: get-built-charts
52+
run: |
53+
charts=$(echo '${{ inputs.charts }}' | jq -r '.[]')
54+
result='{}'
55+
for chart in $charts; do
56+
chart_file="k8s/charts/$chart/Chart.yaml"
57+
version=$(yq e '.version' "$chart_file")
58+
chart_obj=$(jq -n --arg name "$chart" --arg version "$version" '{name: $name, version: $version}')
59+
result=$(echo "$result" | jq --arg n "$chart" --argjson obj "$chart_obj" '. + {($n): $obj}')
60+
done
61+
echo "Built charts: $result"
62+
echo "built_charts=$(echo "$result" | jq -c)" >> $GITHUB_OUTPUT
63+
64+
- name: Prepare charts
65+
id: prepare-charts
66+
run: |
67+
built_charts='${{ steps.get-built-charts.outputs.built_charts }}'
68+
all_charts=$(yq e 'keys' "${{ inputs.chart_filters_path }}" -o=json | jq -c)
69+
prepared_charts='{}'
70+
71+
for name in $(echo "$all_charts" | jq -r '.[]'); do
72+
if echo "$built_charts" | jq -e --arg n "$name" 'has($n)' >/dev/null; then
73+
chart=$(echo "$built_charts" | jq -c --arg n "$name" '.[$n]')
74+
else
75+
chart_file="k8s/charts/$name/Chart.yaml"
76+
if [[ ! -f "$chart_file" ]]; then
77+
echo "Chart $name not found at $chart_file"
78+
exit 1
79+
fi
80+
81+
version=$(grep '^version:' "$chart_file" | awk '{print $2}')
82+
chart=$(jq -n \
83+
--arg name "$name" \
84+
--arg version "$version" \
85+
'{name: $name, version: $version}')
86+
fi
87+
prepared_charts=$(echo "$prepared_charts" | jq --arg n "$name" --argjson c "$chart" '. + {($n): $c}')
88+
done
89+
90+
echo "Prepared charts: $prepared_charts"
91+
echo "charts=$(echo "$prepared_charts" | jq -c)" >> $GITHUB_OUTPUT
92+
93+
- name: Checkout gitops-deployments
94+
uses: actions/checkout@v5
95+
with:
96+
repository: gooddata/gitops-deployments
97+
token: ${{ secrets.TOKEN_GITHUB_YENKINS }}
98+
99+
- name: Get cluster mapping
100+
id: cluster-mapping
101+
run: |
102+
cluster_mapping=$(yq -e '.clusterMapping' tools/auto_merge/config.yaml -o json)
103+
echo "Cluster mapping: $cluster_mapping"
104+
echo "cluster_mapping=$(echo "$cluster_mapping" | jq -c)" >> $GITHUB_OUTPUT
105+
106+
- name: Build HelmReleases
107+
id: build-helmreleases
108+
run: |
109+
charts='${{ steps.prepare-charts.outputs.charts }}'
110+
built_images='${{ inputs.images }}'
111+
tag=${{ inputs.image_tag }}
112+
cluster_mapping='${{ steps.cluster-mapping.outputs.cluster_mapping }}'
113+
114+
hrs_per_cluster='{}'
115+
116+
for cluster_id in $(echo "$cluster_mapping" | jq -r '.[]'); do
117+
helmreleases='[]'
118+
echo "Processing cluster $cluster_id"
119+
for entry in $(echo "$charts" | jq -c 'to_entries[]'); do
120+
chart_name=$(echo "$entry" | jq -r '.key')
121+
chart_definition=$(echo "$entry" | jq -c '.value')
122+
123+
file_path="${cluster_id}/${chart_name}.yaml"
124+
if [[ ! -f "$file_path" ]]; then
125+
echo "File $file_path not found, skipping..."
126+
continue
127+
fi
128+
echo "Processing chart $chart_name for cluster $cluster_id"
129+
130+
used_images=$(yq e '.. | .image? | select(.) | .name' "$file_path" | sort -u)
131+
filtered_images='[]'
132+
for img_name in $(echo "$built_images" | jq -r '.[]'); do
133+
if echo "$used_images" | grep -qx "$img_name"; then
134+
filtered_images=$(echo "$filtered_images" | jq --arg n "$img_name" --arg t "$tag" '. + [{name: $n, tag: $t}]')
135+
fi
136+
done
137+
echo "Filtered images for $chart_name: $filtered_images"
138+
139+
if [[ "$filtered_images" == "[]" ]]; then
140+
original_version=$(yq e '.spec.chart.spec.version' "$file_path")
141+
new_version=$(echo "$chart_definition" | jq -r '.version')
142+
143+
if [[ "$original_version" == "$new_version" ]]; then
144+
echo "There is nothing to update for chart $chart_name in cluster $cluster_id, skipping..."
145+
continue
146+
fi
147+
fi
148+
149+
chart_version="$(echo "$chart_definition" | jq -r '.version')"
150+
releasename=$(echo "$chart_definition" | jq -r '.name')
151+
wait_retries=$(yq e '.spec.values.waitRetries' "$file_path")
152+
namespace=$(yq e '.metadata.namespace' "$file_path")
153+
154+
helmrelease=$(jq -n \
155+
--arg chart_version "$chart_version" \
156+
--argjson images "$filtered_images" \
157+
--arg releasename "$releasename" \
158+
--argjson waitRetries "$wait_retries" \
159+
--arg namespace "$namespace" \
160+
'{chart_version: $chart_version, images: $images, releasename: $releasename, waitRetries: $waitRetries, namespace: $namespace}')
161+
162+
helmreleases=$(echo "$helmreleases" | jq --argjson hr "$helmrelease" '. + [ $hr ]')
163+
done
164+
hrs_per_cluster=$(echo "$hrs_per_cluster" | jq --arg cluster_id "$cluster_id" --argjson list "$helmreleases" '. + {($cluster_id): $list}')
165+
done
166+
167+
echo "Final hrs_per_cluster: $hrs_per_cluster"
168+
echo "helmreleases=$(echo "$hrs_per_cluster" | jq -c)" >> $GITHUB_OUTPUT
169+
170+
- name: Extract staging helmreleases
171+
id: extract-staging-helmreleases
172+
run: |
173+
hrs_per_cluster='${{ steps.build-helmreleases.outputs.helmreleases }}'
174+
staging_hrs=$(echo "$hrs_per_cluster" | jq -c '."62" // []')
175+
echo "staging_helmreleases=$(echo "$staging_hrs" | jq -c)" >> $GITHUB_OUTPUT
176+
177+
- name: Extract updated production cluster names
178+
id: extract-updated-prod-names
179+
run: |
180+
hrs_per_cluster='${{ steps.build-helmreleases.outputs.helmreleases }}'
181+
cluster_mapping='${{ steps.cluster-mapping.outputs.cluster_mapping }}'
182+
updated_prod_ids=$(echo "$hrs_per_cluster" | jq 'del(.["62"], .["ucluster"]) | [to_entries | map(select(.value | length > 0) | .key)] | .[]')
183+
updated_prod_names=$(echo "$cluster_mapping" | jq -r --argjson ids "$updated_prod_ids" '
184+
to_entries
185+
| map(select((.value | tostring) as $v | $ids[] == $v))
186+
| map(.key)
187+
')
188+
echo "Updated production clusters: $updated_prod_names"
189+
echo "updated_prod_names=$(echo "$updated_prod_names" | jq -c)" >> $GITHUB_OUTPUT
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
name: II Chart Validation
2+
on:
3+
workflow_call:
4+
inputs:
5+
base_branch:
6+
description: "Base branch to compare against"
7+
required: true
8+
type: string
9+
default: "master"
10+
chart:
11+
description: "Chart name to check compliance"
12+
required: true
13+
type: string
14+
15+
jobs:
16+
chart-validation:
17+
runs-on:
18+
group: infra1-runners-arc
19+
labels: runners-small
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v5
23+
with:
24+
fetch-depth: 0
25+
26+
- name: Add upstream (base/original) repo
27+
run: |
28+
repo_name=$(basename "$GITHUB_REPOSITORY")
29+
git remote add upstream https://github.com/gooddata/${repo_name}.git
30+
git fetch upstream
31+
32+
- name: Check chart version bump
33+
run: |
34+
diff=$(git diff upstream/${{ inputs.base_branch }} HEAD -- k8s/charts/${{ inputs.chart }}/Chart.yaml | grep '^[-+]' | grep 'version:' || true)
35+
36+
added_version=$(echo "$diff" | grep '^+version:' || true)
37+
removed_version=$(echo "$diff" | grep '^-version:' || true)
38+
39+
echo "Added version: $added_version"
40+
echo "Removed version: $removed_version"
41+
42+
if [[ -n "$added_version" && -n "$removed_version" && "$added_version" != "$removed_version" ]]; then
43+
echo "Version bump detected."
44+
else
45+
echo "No version bump detected."
46+
exit 1
47+
fi
48+
49+
- name: Helm Lint
50+
run: |
51+
set -e
52+
echo "Linting ${{ inputs.chart }} helm chart"
53+
helm lint k8s/charts/${{ inputs.chart }} | tee helm_lint_output.txt
54+
echo "${{ inputs.chart }} helm lint passed"
55+
echo '### Helm Lint Output' >> $GITHUB_STEP_SUMMARY
56+
cat helm_lint_output.txt >> $GITHUB_STEP_SUMMARY
57+
58+
- name: Render Helm templates
59+
run: helm template k8s/charts/${{ inputs.chart }} > rendered.yaml
60+
61+
- name: Install prometheus
62+
run: |
63+
sudo apt-get update
64+
sudo apt-get install -y prometheus
65+
66+
- name: Check rules
67+
run: |
68+
set -euo pipefail
69+
70+
yq -o=json '. | select(.kind == "ConfigMap")' rendered.yaml | \
71+
jq -c '.' | while read -r configmap; do
72+
name=$(echo "$configmap" | jq -r '.metadata.name')
73+
# Find all keys ending with -rules.yaml
74+
echo "$configmap" | jq -r '.data | to_entries[] | select(.key|endswith("-rules.yaml")) | .key' | while read -r rulekey; do
75+
rule_content=$(echo "$configmap" | jq -r --arg key "$rulekey" '.data[$key]')
76+
# Remove Helm template expressions
77+
clean_content=$(echo "$rule_content" | sed 's/{{.*}}//g')
78+
tmpfile=$(mktemp)
79+
echo "$clean_content" > "$tmpfile"
80+
promtool check rules "$tmpfile" || { echo "[FAIL] promtool check failed for $name/$rulekey"; exit 1; }
81+
rm "$tmpfile"
82+
done
83+
done
84+
85+
- name: Check security context
86+
run: |
87+
set -euo pipefail
88+
89+
yq -o=json '.' rendered.yaml | jq -c '.' | while read -r doc; do
90+
# Check all containers in spec
91+
echo "$doc" | jq -c '.. | .containers? // empty' | while read -r containers; do
92+
echo "$containers" | jq -c '.[]' | while read -r container; do
93+
name=$(echo "$container" | jq -r '.name // "unnamed"')
94+
sc=$(echo "$container" | jq '.securityContext')
95+
runAsUser=$(echo "$sc" | jq '.runAsUser // empty')
96+
runAsNonRoot=$(echo "$sc" | jq '.runAsNonRoot // empty')
97+
if [[ "$runAsUser" == "" && "$runAsNonRoot" == "" ]]; then
98+
echo "[FAIL] SecurityContext: neither runAsUser nor runAsNonRoot defined for container $name"
99+
exit 1
100+
fi
101+
if [[ "$runAsUser" == "0" ]]; then
102+
echo "[FAIL] SecurityContext: runAsUser is 0 for container $name"
103+
exit 1
104+
fi
105+
if [[ "$runAsNonRoot" != "true" && "$runAsNonRoot" != "" ]]; then
106+
echo "[FAIL] SecurityContext: runAsNonRoot is not true for container $name"
107+
exit 1
108+
fi
109+
done
110+
done
111+
done
112+
echo "Security context checks passed."
113+
114+
- name: Check containers
115+
env:
116+
ECR_URL: ${{ secrets.ECR_URL }}
117+
run: |
118+
set -euo pipefail
119+
120+
red='\033[0;31m'
121+
reset='\033[0m'
122+
green='\033[0;32m'
123+
124+
fail() { echo -e "${red}[FAIL]${reset} $1"; }
125+
ok() { echo -e "${green}[OK]${reset} $1"; }
126+
127+
DOCKER_REGISTRIES="harbor.intgdc.com|${ECR_URL}"
128+
ALLOWED_NAMESPACES="base|tools|staging|stable|3rdparty|pullthrough"
129+
130+
rc=0
131+
yq -o=json '.' rendered.yaml | jq -r '
132+
. as $doc |
133+
(.. | (.containers? + .initContainers?)) as $containers |
134+
if ($containers | (type == "array" and length > 0)) then
135+
# If the list is valid, iterate through each container.
136+
$containers[] | "\($doc.kind) \($doc.metadata.name) \(.name) \(.image)"
137+
else
138+
# If the list is empty, output nothing.
139+
empty
140+
end
141+
' | while read -r kind res name image ; do
142+
if [[ -z "$image" ]]; then
143+
fail "$kind/$res: Image not defined for container $name"
144+
rc=1
145+
elif ! [[ "$image" =~ ^($DOCKER_REGISTRIES)/($ALLOWED_NAMESPACES)/ ]]; then
146+
fail "$kind/$res: Image $image for container $name does not start with ($DOCKER_REGISTRIES)/($ALLOWED_NAMESPACES)"
147+
rc=1
148+
else
149+
ok "$kind/$res: Image $image for container $name passed the registry test"
150+
fi
151+
done
152+
exit $rc

0 commit comments

Comments
 (0)