Skip to content

Commit 9310d10

Browse files
authored
feat: Merge pull request #50 from tonedefdev/feat/split-module-provider-scanning
feat: Split module scanning and trivy into separate images
2 parents e17c5a1 + ed6f7fe commit 9310d10

22 files changed

Lines changed: 570 additions & 237 deletions

File tree

.github/agents/developer.agent.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Follow these patterns exactly as they exist in the codebase:
7474
- CRD types live in `api/v1alpha1/` — never define new types elsewhere
7575
- Storage backends are in `pkg/storage/`
7676
- GitHub integration is in `pkg/github/`
77-
- Test utilities are in `pkg/testutils/`
77+
- Test utilities are in `pkg/testutils/` — all shared e2e helpers (`NeedsRebuild`, `ComputeBuildContextHash`, `SplitImageRef`, etc.) MUST live here; never duplicate them per-suite
7878

7979
**Testing**:
8080
- Tests use Ginkgo v2 (`Describe`, `Context`, `It`, `BeforeEach`, `AfterEach`)

.github/agents/planner.agent.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: "Use when: planning a new feature, designing a change, creating a spec, gathering requirements, breaking down work, clarifying scope, or starting implementation of anything in the OpenDepot project. Produces a structured implementation plan saved to session memory for the developer agent."
33
name: "OpenDepot Planner"
4-
tools: [read, edit, agent, todo]
4+
tools: [read, search, web, vscode/memory]
55
argument-hint: "Describe the feature or change you want to implement"
66
---
77

.github/workflows/build.yaml

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,28 @@ jobs:
1717
service:
1818
- name: version-controller
1919
path: services/version
20+
buildArgs: ""
21+
tagSuffix: ""
22+
- name: version-controller
23+
path: services/version
24+
buildArgs: "INCLUDE_TRIVY=true"
25+
tagSuffix: "-scanning"
2026
- name: module-controller
2127
path: services/module
28+
buildArgs: ""
29+
tagSuffix: ""
2230
- name: depot-controller
2331
path: services/depot
32+
buildArgs: ""
33+
tagSuffix: ""
2434
- name: server
2535
path: services/server
36+
buildArgs: ""
37+
tagSuffix: ""
2638
- name: provider-controller
2739
path: services/provider
40+
buildArgs: ""
41+
tagSuffix: ""
2842
arch:
2943
- amd64
3044
- arm64
@@ -51,7 +65,7 @@ jobs:
5165
with:
5266
images: ghcr.io/${{ github.repository }}/${{ matrix.service.name }}
5367
flavor: |
54-
suffix=-${{ matrix.arch }}
68+
suffix=-${{ matrix.arch }}${{ matrix.service.tagSuffix }}
5569
tags: |
5670
type=ref,event=branch
5771
type=semver,pattern={{version}}
@@ -68,6 +82,7 @@ jobs:
6882
push: ${{ github.event_name != 'pull_request' }}
6983
tags: ${{ steps.meta.outputs.tags }}
7084
labels: ${{ steps.meta.outputs.labels }}
85+
build-args: ${{ matrix.service.buildArgs }}
7186
cache-from: type=gha
7287
cache-to: type=gha,mode=max
7388

@@ -78,17 +93,19 @@ jobs:
7893
matrix:
7994
service:
8095
- name: version-controller
81-
path: services/version
96+
tagSuffix: ""
97+
- name: version-controller
98+
tagSuffix: "-scanning"
8299
- name: module-controller
83-
path: services/module
100+
tagSuffix: ""
84101
- name: depot-controller
85-
path: services/depot
102+
tagSuffix: ""
86103
- name: server
87-
path: services/server
104+
tagSuffix: ""
88105
- name: provider-controller
89-
path: services/provider
106+
tagSuffix: ""
90107

91-
name: Manifest ${{ matrix.service.name }}
108+
name: Manifest ${{ matrix.service.name }}${{ matrix.service.tagSuffix }}
92109

93110
steps:
94111
- name: Log in to GitHub Container Registry
@@ -103,6 +120,8 @@ jobs:
103120
uses: docker/metadata-action@v5
104121
with:
105122
images: ghcr.io/${{ github.repository }}/${{ matrix.service.name }}
123+
flavor: |
124+
suffix=${{ matrix.service.tagSuffix }}
106125
tags: |
107126
type=ref,event=branch
108127
type=semver,pattern={{version}}
@@ -112,11 +131,20 @@ jobs:
112131
113132
- name: Create and push multi-arch manifest
114133
run: |
115-
IMAGE="ghcr.io/${{ github.repository }}/${{ matrix.service.name }}"
134+
SUFFIX="${{ matrix.service.tagSuffix }}"
116135
for TAG in $(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' '); do
117-
docker buildx imagetools create -t "${TAG}" \
118-
"${TAG}-amd64" \
119-
"${TAG}-arm64" || true
136+
if [ -n "$SUFFIX" ]; then
137+
# Build job produced tags like: 0.2.7-amd64-scanning
138+
# Strip the tagSuffix from the end of TAG to get the base, then append arch+suffix
139+
BASE="${TAG%$SUFFIX}"
140+
docker buildx imagetools create -t "${TAG}" \
141+
"${BASE}-amd64${SUFFIX}" \
142+
"${BASE}-arm64${SUFFIX}" || true
143+
else
144+
docker buildx imagetools create -t "${TAG}" \
145+
"${TAG}-amd64" \
146+
"${TAG}-arm64" || true
147+
fi
120148
done
121149
122150
build-status:

.github/workflows/e2e.yaml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,22 @@ jobs:
3131
include:
3232
- service: depot-controller
3333
dockerfile: services/depot/Dockerfile
34+
buildArgs: ""
3435
- service: module-controller
3536
dockerfile: services/module/Dockerfile
37+
buildArgs: ""
3638
- service: provider-controller
3739
dockerfile: services/provider/Dockerfile
40+
buildArgs: ""
3841
- service: server
3942
dockerfile: services/server/Dockerfile
43+
buildArgs: ""
4044
- service: version-controller
4145
dockerfile: services/version/Dockerfile
46+
buildArgs: ""
47+
- service: version-controller-scanning
48+
dockerfile: services/version/Dockerfile
49+
buildArgs: "INCLUDE_TRIVY=true"
4250

4351
steps:
4452
- uses: actions/checkout@v4
@@ -60,6 +68,7 @@ jobs:
6068
file: ${{ matrix.dockerfile }}
6169
push: true
6270
tags: ${{ env.REGISTRY }}/${{ matrix.service }}:${{ env.IMAGE_TAG }}
71+
build-args: ${{ matrix.buildArgs }}
6372
cache-from: type=gha,scope=${{ matrix.service }}
6473
cache-to: type=gha,mode=max,scope=${{ matrix.service }}
6574

@@ -93,9 +102,11 @@ jobs:
93102
run: |
94103
docker pull ${{ env.REGISTRY }}/module-controller:${{ env.IMAGE_TAG }}
95104
docker pull ${{ env.REGISTRY }}/version-controller:${{ env.IMAGE_TAG }}
105+
docker pull ${{ env.REGISTRY }}/version-controller-scanning:${{ env.IMAGE_TAG }}
96106
docker pull ${{ env.REGISTRY }}/server:${{ env.IMAGE_TAG }}
97107
docker tag ${{ env.REGISTRY }}/module-controller:${{ env.IMAGE_TAG }} module-controller:e2e-test
98108
docker tag ${{ env.REGISTRY }}/version-controller:${{ env.IMAGE_TAG }} version-controller:e2e-test
109+
docker tag ${{ env.REGISTRY }}/version-controller-scanning:${{ env.IMAGE_TAG }} version-controller:e2e-test-scanning
99110
docker tag ${{ env.REGISTRY }}/server:${{ env.IMAGE_TAG }} server:e2e-test
100111
101112
- name: Install kind
@@ -118,7 +129,7 @@ jobs:
118129

119130
- name: Run module e2e tests
120131
working-directory: services/module
121-
run: KIND_CLUSTER=opendepot-test-e2e IMG=module-controller:e2e-test go test ./test/e2e/ -v -ginkgo.v -count=1 -timeout 20m
132+
run: SKIP_IMAGE_BUILD=true KIND_CLUSTER=opendepot-test-e2e IMG=module-controller:e2e-test go test ./test/e2e/ -v -ginkgo.v -count=1 -timeout 20m
122133

123134
e2e-provider:
124135
name: Provider controller e2e
@@ -150,9 +161,11 @@ jobs:
150161
run: |
151162
docker pull ${{ env.REGISTRY }}/provider-controller:${{ env.IMAGE_TAG }}
152163
docker pull ${{ env.REGISTRY }}/version-controller:${{ env.IMAGE_TAG }}
164+
docker pull ${{ env.REGISTRY }}/version-controller-scanning:${{ env.IMAGE_TAG }}
153165
docker pull ${{ env.REGISTRY }}/server:${{ env.IMAGE_TAG }}
154166
docker tag ${{ env.REGISTRY }}/provider-controller:${{ env.IMAGE_TAG }} provider-controller:e2e-test
155167
docker tag ${{ env.REGISTRY }}/version-controller:${{ env.IMAGE_TAG }} version-controller:e2e-test
168+
docker tag ${{ env.REGISTRY }}/version-controller-scanning:${{ env.IMAGE_TAG }} version-controller:e2e-test-scanning
156169
docker tag ${{ env.REGISTRY }}/server:${{ env.IMAGE_TAG }} server:e2e-test
157170
158171
- name: Install kind
@@ -175,7 +188,7 @@ jobs:
175188

176189
- name: Run provider e2e tests
177190
working-directory: services/provider
178-
run: KIND_CLUSTER=opendepot-test-e2e IMG=provider-controller:e2e-test go test ./test/e2e/ -v -ginkgo.v -count=1 -timeout 30m
191+
run: SKIP_IMAGE_BUILD=true KIND_CLUSTER=opendepot-test-e2e IMG=provider-controller:e2e-test go test ./test/e2e/ -v -ginkgo.v -count=1 -timeout 30m
179192

180193
e2e-depot:
181194
name: Depot controller e2e
@@ -311,8 +324,10 @@ jobs:
311324
- name: Pull e2e images
312325
run: |
313326
docker pull ${{ env.REGISTRY }}/version-controller:${{ env.IMAGE_TAG }}
327+
docker pull ${{ env.REGISTRY }}/version-controller-scanning:${{ env.IMAGE_TAG }}
314328
docker pull ${{ env.REGISTRY }}/server:${{ env.IMAGE_TAG }}
315329
docker tag ${{ env.REGISTRY }}/version-controller:${{ env.IMAGE_TAG }} version-controller:e2e-test
330+
docker tag ${{ env.REGISTRY }}/version-controller-scanning:${{ env.IMAGE_TAG }} version-controller:e2e-test-scanning
316331
docker tag ${{ env.REGISTRY }}/server:${{ env.IMAGE_TAG }} server:e2e-test
317332
318333
- name: Install kind
@@ -330,4 +345,4 @@ jobs:
330345

331346
- name: Run version e2e tests
332347
working-directory: services/version
333-
run: KIND_CLUSTER=opendepot-test-e2e IMG=version-controller:e2e-test go test ./test/e2e/ -v -ginkgo.v -count=1 -timeout 20m
348+
run: SKIP_IMAGE_BUILD=true KIND_CLUSTER=opendepot-test-e2e IMG=version-controller:e2e-test go test ./test/e2e/ -v -ginkgo.v -count=1 -timeout 20m

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Go](https://img.shields.io/badge/Go-1.25-00ADD8?logo=go&logoColor=white)](https://go.dev/)
44
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/tonedefdev/opendepot/blob/main/LICENSE)
5-
[![Helm](https://img.shields.io/badge/Helm_Chart-0.2.7-0F1689?logo=helm&logoColor=white)](https://github.com/tonedefdev/opendepot/tree/main/chart/opendepot)
5+
[![Helm](https://img.shields.io/badge/Helm_Chart-0.3.0-0F1689?logo=helm&logoColor=white)](https://github.com/tonedefdev/opendepot/tree/main/chart/opendepot)
66
[![Docs](https://img.shields.io/badge/Docs-tonedefdev.github.io-047df1?logo=materialformkdocs&logoColor=white)](https://tonedefdev.github.io/opendepot/)
77

88
<p align="center">

chart/opendepot/Chart.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ apiVersion: v2
22
name: opendepot
33
description: A Helm chart for deploying OpenDepot, a cloud-native OpenTofu Registry
44
type: application
5-
version: 0.2.7
6-
appVersion: "0.2.7"
5+
version: 0.3.0
6+
appVersion: "0.3.0"
77
keywords:
88
- terraform
99
- opentofu

chart/opendepot/templates/scanning-cronjob.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{{- if .Values.scanning.enabled }}
1+
{{- if and .Values.scanning.enabled .Values.scanning.providerScanning }}
22
apiVersion: batch/v1
33
kind: CronJob
44
metadata:

chart/opendepot/templates/scanning-pvc.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{{- if .Values.scanning.enabled }}
1+
{{- if and .Values.scanning.enabled .Values.scanning.providerScanning }}
22
apiVersion: v1
33
kind: PersistentVolumeClaim
44
metadata:

chart/opendepot/templates/version-deployment.yaml

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,21 @@ spec:
4141
{{- end }}
4242
containers:
4343
- name: version-controller
44-
image: "{{ .Values.version.image.repository }}:{{ default (default .Chart.AppVersion .Values.global.image.tag) .Values.version.image.tag }}"
44+
{{- $tag := default (default .Chart.AppVersion .Values.global.image.tag) .Values.version.image.tag }}
45+
{{- $imageTag := ternary (printf "%s-scanning" $tag) $tag .Values.scanning.enabled }}
46+
image: "{{ .Values.version.image.repository }}:{{ $imageTag }}"
4547
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
4648
{{- if or .Values.scanning.enabled .Values.version.zapLogLevel }}
4749
args:
4850
{{- if .Values.scanning.enabled }}
4951
- --scanning-enabled=true
50-
- --trivy-cache-dir={{ .Values.scanning.cacheMountPath }}
51-
- --scan-offline={{ .Values.scanning.offline }}
5252
- --scan-block-on-critical={{ .Values.scanning.blockOnCritical }}
5353
- --scan-block-on-high={{ .Values.scanning.blockOnHigh }}
54-
- --scan-modules={{ .Values.scanning.scanModules }}
54+
- --scan-modules=true
55+
{{- if .Values.scanning.providerScanning }}
56+
- --trivy-cache-dir={{ .Values.scanning.cacheMountPath }}
57+
- --scan-offline={{ .Values.scanning.offline }}
58+
{{- end }}
5559
{{- end }}
5660
{{- if .Values.version.zapLogLevel }}
5761
- --zap-log-level={{ .Values.version.zapLogLevel }}
@@ -69,18 +73,18 @@ spec:
6973
{{- end }}
7074
resources:
7175
{{- toYaml .Values.version.resources | nindent 10 }}
72-
{{- if or .Values.storage.filesystem.enabled .Values.scanning.enabled }}
76+
{{- if or .Values.storage.filesystem.enabled .Values.scanning.providerScanning }}
7377
volumeMounts:
7478
{{- if .Values.storage.filesystem.enabled }}
7579
- name: modules
7680
mountPath: {{ .Values.storage.filesystem.mountPath }}
7781
{{- end }}
78-
{{- if .Values.scanning.enabled }}
82+
{{- if .Values.scanning.providerScanning }}
7983
- name: trivy-cache
8084
mountPath: {{ .Values.scanning.cacheMountPath }}
8185
{{- end }}
8286
{{- end }}
83-
{{- if or .Values.storage.filesystem.enabled .Values.scanning.enabled }}
87+
{{- if or .Values.storage.filesystem.enabled .Values.scanning.providerScanning }}
8488
volumes:
8589
{{- if .Values.storage.filesystem.enabled }}
8690
- name: modules
@@ -93,7 +97,7 @@ spec:
9397
claimName: opendepot-modules
9498
{{- end }}
9599
{{- end }}
96-
{{- if .Values.scanning.enabled }}
100+
{{- if .Values.scanning.providerScanning }}
97101
- name: trivy-cache
98102
persistentVolumeClaim:
99103
claimName: opendepot-trivy-cache

chart/opendepot/values.yaml

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,23 +159,29 @@ storage:
159159
# Size of the PersistentVolumeClaim (ignored when hostPath is set)
160160
size: 10Gi
161161

162-
## Scanning configuration (Trivy-based provider vulnerability scanning)
162+
## Scanning configuration (Trivy-based vulnerability and IaC scanning)
163163
scanning:
164-
# Enable Trivy security scanning for provider artifacts
164+
# Enable Trivy-based scanning. When true, the version-controller automatically
165+
# uses the image variant tagged with the -scanning suffix, which bundles the
166+
# Trivy binary. Module IaC scanning (HCL misconfiguration detection) is active
167+
# at this level — no PVC or CronJob infrastructure is required.
165168
enabled: false
166-
# Mount path inside the version-controller container for the Trivy DB cache
169+
# Enable provider binary and source scanning. Requires scanning.enabled=true.
170+
# When true, creates the Trivy vulnerability DB PVC and the trivy-db-updater
171+
# CronJob, and mounts the cache volume into the version-controller.
172+
providerScanning: false
173+
# Mount path inside the version-controller container for the Trivy DB cache.
174+
# Only used when providerScanning is true.
167175
cacheMountPath: /var/cache/trivy
168176
# Pass --offline-scan to Trivy, preventing network calls during scans.
169-
# Set to false only when you want Trivy to download the DB at scan time instead of
170-
# relying on the pre-populated cache from the trivy-db-updater CronJob.
177+
# Only relevant when providerScanning is true.
171178
offline: true
172-
# Block provider reconciliation when CRITICAL vulnerabilities are found
179+
# Block reconciliation when CRITICAL vulnerabilities are found (modules or providers).
173180
blockOnCritical: false
174-
# Block provider reconciliation when HIGH vulnerabilities are found
181+
# Block reconciliation when HIGH vulnerabilities are found (modules or providers).
175182
blockOnHigh: false
176-
# Enable Trivy IaC scanning for module version archives (requires scanning.enabled=true)
177-
scanModules: false
178-
# PersistentVolumeClaim for the shared Trivy vulnerability database
183+
# PersistentVolumeClaim for the shared Trivy vulnerability database.
184+
# Only created when providerScanning is true.
179185
cache:
180186
# StorageClass must support ReadWriteMany so that scanner and updater pods can share it.
181187
# For single-node environments (e.g. Kind), ReadWriteOnce with the default storage class works.
@@ -185,7 +191,8 @@ scanning:
185191
accessMode: ReadWriteMany
186192
# Size of the Trivy DB cache PVC
187193
size: 1Gi
188-
# CronJob that runs 'trivy image --download-db-only' daily to keep the offline DB current
194+
# CronJob that runs 'trivy image --download-db-only' on a schedule to keep the offline DB current.
195+
# Only created when providerScanning is true.
189196
dbUpdater:
190197
# Cron schedule for the Trivy DB update job (default: 02:00 UTC daily)
191198
schedule: "0 2 * * *"

0 commit comments

Comments
 (0)