Skip to content
Merged
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 .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
artifactory
bak
bitnami
containerregistry
curlimages
deepcopy
deletecollection
Expand All @@ -20,9 +21,9 @@
rolearn
selfsigned
servicemonitor
servicemonitors

Check warning on line 24 in .github/actions/spelling/expect.txt

View workflow job for this annotation

GitHub Actions / Run spell check

`servicemonitors` is ignored by check-spelling because another more general variant is also in expect. (ignored-expect-variant)
SResources
spiffe
SResources
SVIDs
tekton
tpl
Expand Down
3 changes: 3 additions & 0 deletions pkg/utils/k8s/update_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func UpdateCronJobFields(obj, desired *batchv1.CronJob) {
ps.InitContainers = dps.InitContainers
ps.Containers = dps.Containers
ps.Volumes = dps.Volumes
ps.ImagePullSecrets = dps.ImagePullSecrets
Comment thread
mondoo-code-review[bot] marked this conversation as resolved.
}

// UpdateDeploymentFields copies managed fields from desired to obj,
Expand All @@ -54,6 +55,7 @@ func UpdateDeploymentFields(obj, desired *appsv1.Deployment) {
ps.ServiceAccountName = dps.ServiceAccountName
ps.Containers = dps.Containers
ps.Volumes = dps.Volumes
ps.ImagePullSecrets = dps.ImagePullSecrets
}

// UpdateDaemonSetFields copies managed fields from desired to obj,
Expand All @@ -73,4 +75,5 @@ func UpdateDaemonSetFields(obj, desired *appsv1.DaemonSet) {
ps.Tolerations = dps.Tolerations
ps.Containers = dps.Containers
ps.Volumes = dps.Volumes
ps.ImagePullSecrets = dps.ImagePullSecrets
}
125 changes: 125 additions & 0 deletions pkg/utils/k8s/update_fields_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright Mondoo, Inc. 2026
// SPDX-License-Identifier: BUSL-1.1

package k8s

import (
"testing"

"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestUpdateCronJobFields_ImagePullSecrets(t *testing.T) {
desired := &batchv1.CronJob{
Spec: batchv1.CronJobSpec{
Schedule: "*/5 * * * *",
JobTemplate: batchv1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "test", Image: "test:latest"}},
ImagePullSecrets: []corev1.LocalObjectReference{
{Name: "my-secret"},
{Name: "another-secret"},
},
},
},
},
},
},
}

obj := &batchv1.CronJob{}
UpdateCronJobFields(obj, desired)

assert.Equal(t, desired.Spec.Schedule, obj.Spec.Schedule)
assert.Equal(t, desired.Spec.JobTemplate.Spec.Template.Spec.Containers, obj.Spec.JobTemplate.Spec.Template.Spec.Containers)
assert.Equal(t, desired.Spec.JobTemplate.Spec.Template.Spec.ImagePullSecrets, obj.Spec.JobTemplate.Spec.Template.Spec.ImagePullSecrets)
}

func TestUpdateCronJobFields_PreservesUnmanagedFields(t *testing.T) {
obj := &batchv1.CronJob{
Spec: batchv1.CronJobSpec{
JobTemplate: batchv1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
DNSPolicy: corev1.DNSClusterFirst,
SchedulerName: "custom-scheduler",
},
},
},
},
},
}

desired := &batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"app": "test"}},
Spec: batchv1.CronJobSpec{
Schedule: "*/10 * * * *",
JobTemplate: batchv1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "test"}},
},
},
},
},
},
}

UpdateCronJobFields(obj, desired)

// Managed fields are updated
assert.Equal(t, "*/10 * * * *", obj.Spec.Schedule)
// Unmanaged fields are preserved
assert.Equal(t, corev1.DNSClusterFirst, obj.Spec.JobTemplate.Spec.Template.Spec.DNSPolicy)
assert.Equal(t, "custom-scheduler", obj.Spec.JobTemplate.Spec.Template.Spec.SchedulerName)
}

func TestUpdateDeploymentFields_ImagePullSecrets(t *testing.T) {
desired := &appsv1.Deployment{
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "test", Image: "test:latest"}},
ImagePullSecrets: []corev1.LocalObjectReference{
{Name: "my-secret"},
},
},
},
},
}

obj := &appsv1.Deployment{}
UpdateDeploymentFields(obj, desired)

assert.Equal(t, desired.Spec.Template.Spec.ImagePullSecrets, obj.Spec.Template.Spec.ImagePullSecrets)
assert.Equal(t, desired.Spec.Template.Spec.Containers, obj.Spec.Template.Spec.Containers)
}

func TestUpdateDaemonSetFields_ImagePullSecrets(t *testing.T) {
desired := &appsv1.DaemonSet{
Spec: appsv1.DaemonSetSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "test", Image: "test:latest"}},
ImagePullSecrets: []corev1.LocalObjectReference{
{Name: "my-secret"},
},
},
},
},
}

obj := &appsv1.DaemonSet{}
UpdateDaemonSetFields(obj, desired)

assert.Equal(t, desired.Spec.Template.Spec.ImagePullSecrets, obj.Spec.Template.Spec.ImagePullSecrets)
assert.Equal(t, desired.Spec.Template.Spec.Containers, obj.Spec.Template.Spec.Containers)
}
69 changes: 53 additions & 16 deletions tests/e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ End-to-end tests that deploy the Mondoo operator to a real GKE cluster and verif

- **Fresh Deploy** (`run-fresh-deploy.sh`): Builds the operator from the current branch, deploys to a GKE cluster, configures scanning, and verifies everything works.
- **Upgrade** (`run-upgrade.sh`): Installs a released baseline version first, verifies it, then upgrades to the current branch and verifies again.
- **Registry Mirroring & Proxy** (`run-registry-mirroring.sh`): Deploys with an Artifact Registry mirror repo and optional Squid proxy. Verifies image references are rewritten, `imagePullSecrets` are propagated, and proxy env vars are set.

Both tests pause for manual verification at each verify step (check Mondoo console for assets/scan results). Press Enter to continue or Ctrl+C to abort.
All tests pause for manual verification at each verify step (check Mondoo console for assets/scan results). Press Enter to continue or Ctrl+C to abort.

## Prerequisites

Expand All @@ -17,6 +18,8 @@ Both tests pause for manual verification at each verify step (check Mondoo conso
- `docker`
- `kubectl`
- Go toolchain (for building the operator)
- `jq` (for registry mirroring test verification)
- `crane` CLI (for registry mirroring test): `go install github.com/google/go-containerregistry/cmd/crane@latest`

### Mondoo credentials

Expand Down Expand Up @@ -57,6 +60,8 @@ terraform apply \
| `mondoo_org_id` | yes | - | Mondoo organization ID |
| `region` | no | `europe-west3` | GCP region |
| `autopilot` | no | `true` | `true` for Autopilot, `false` for Standard cluster |
| `enable_mirror_test` | no | `false` | Create a mirror AR repo for registry mirroring/imagePullSecrets tests |
| `enable_proxy_test` | no | `false` | Provision a Squid proxy VM for proxy tests (requires `enable_mirror_test`) |

You can also set these in a `terraform.tfvars` file.

Expand Down Expand Up @@ -94,6 +99,32 @@ What it does:
6. Upgrades to the current branch image via local Helm chart
7. Waits, verifies again, pauses for manual check

### Registry Mirroring & Proxy

```bash
# Provision infrastructure with mirror AR repo (and optional proxy VM)
cd tests/e2e/terraform
terraform apply -var="project_id=MY_PROJECT" -var="mondoo_org_id=MY_ORG" \
-var="enable_mirror_test=true" -var="enable_proxy_test=true"

# Run the test
cd tests/e2e
./run-registry-mirroring.sh
```

What it does:
1. Loads Terraform outputs (including mirror AR repo and proxy IP if enabled)
2. Builds the operator image and pushes to Artifact Registry
3. Creates `imagePullSecret` from the mirror repo's GCP service account key
4. Uses `crane` to copy cnspec image from ghcr.io into the mirror AR repo
5. Deploys an nginx test workload
6. Helm installs the operator with `registryMirrors`, `imagePullSecrets`, and proxy `--values`
7. Applies MondooAuditConfig, waits 90s for reconciliation
8. Runs standard verification checks
9. Runs mirroring-specific checks: image refs rewritten, pull secrets propagated, proxy env vars set
10. Checks Squid proxy access logs (if proxy enabled)
11. Pauses for manual verification in the Mondoo console

## Cleanup

Remove all test resources from the cluster (everything except Terraform infra):
Expand All @@ -115,23 +146,29 @@ terraform destroy -var="project_id=MY_PROJECT" -var="mondoo_org_id=MY_ORG"
```
tests/e2e/
├── README.md
├── run-fresh-deploy.sh # Fresh deploy test orchestrator
├── run-upgrade.sh # Upgrade test orchestrator
├── run-fresh-deploy.sh # Fresh deploy test orchestrator
├── run-upgrade.sh # Upgrade test orchestrator
├── run-registry-mirroring.sh # Registry mirroring & proxy test orchestrator
├── terraform/
│ ├── versions.tf # Provider requirements
│ ├── variables.tf # Input variables
│ ├── main.tf # GKE cluster + Artifact Registry
│ ├── mondoo.tf # Mondoo space + service account
│ └── outputs.tf # Outputs consumed by scripts
│ ├── versions.tf # Provider requirements
│ ├── variables.tf # Input variables
│ ├── main.tf # GKE cluster + Artifact Registry
│ ├── mondoo.tf # Mondoo space + service account
│ ├── proxy.tf # Squid proxy VM (optional)
│ └── outputs.tf # Outputs consumed by scripts
├── scripts/
│ ├── common.sh # Logging, TF output loading, wait helpers
│ ├── build-and-push.sh # Build operator image, push to AR
│ ├── deploy-operator.sh # Helm install from local chart
│ ├── deploy-baseline.sh # Helm install released version
│ ├── deploy-test-workload.sh # Deploy nginx for scanning
│ ├── apply-mondoo-config.sh # Create secret + apply MondooAuditConfig
│ ├── verify.sh # Automated checks + manual verification pause
│ └── cleanup.sh # Remove all test resources from cluster
│ ├── common.sh # Logging, TF output loading, wait helpers
│ ├── build-and-push.sh # Build operator image, push to AR
│ ├── deploy-operator.sh # Helm install from local chart
│ ├── deploy-operator-mirroring.sh # Helm install with mirror/proxy values
│ ├── deploy-baseline.sh # Helm install released version
│ ├── deploy-test-workload.sh # Deploy nginx for scanning
│ ├── apply-mondoo-config.sh # Create secret + apply MondooAuditConfig
│ ├── setup-mirror-registry.sh # Create imagePullSecret for mirror AR repo
│ ├── populate-mirror-registry.sh # Copy cnspec image into mirror AR repo via crane
│ ├── verify.sh # Automated checks + manual verification pause
│ ├── verify-mirroring.sh # Mirroring/proxy-specific verification
│ └── cleanup.sh # Remove all test resources from cluster
└── manifests/
├── mondoo-audit-config.yaml.tpl # Standard cluster config (nodes enabled)
├── mondoo-audit-config-autopilot.yaml.tpl # Autopilot config (nodes disabled)
Expand Down
81 changes: 81 additions & 0 deletions tests/e2e/run-registry-mirroring.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Copyright Mondoo, Inc. 2026
# SPDX-License-Identifier: BUSL-1.1

# Test: Registry Mirroring, imagePullSecrets & Proxy
# Builds operator, deploys with AR-based mirror registry and optional proxy,
# verifies image references, pull secrets, and proxy configuration.
#
# Prerequisites:
# - Terraform infrastructure provisioned with enable_mirror_test=true
# - crane CLI installed (go install github.com/google/go-containerregistry/cmd/crane@latest)
# - gcloud authenticated, docker, helm, kubectl available
# - For proxy testing: also set enable_proxy_test=true in terraform
#
# Usage:
# ./run-registry-mirroring.sh

set -euo pipefail

E2E_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${E2E_ROOT}/scripts/common.sh"

info "=========================================="
info " Test: Registry Mirroring & Proxy"
info "=========================================="

# Step 1: Load Terraform outputs (includes mirror and proxy outputs)
load_tf_outputs

# Validate that mirror test infra is provisioned
if [[ "${ENABLE_MIRROR_TEST}" != "true" ]]; then
die "This test requires enable_mirror_test=true in Terraform. Run: terraform apply -var='enable_mirror_test=true'"
fi

# Step 2: Build and push operator image
info "--- Step: Build and Push ---"
source "${E2E_ROOT}/scripts/build-and-push.sh"

# Step 3: Create imagePullSecret for mirror AR repo
info "--- Step: Setup Mirror Registry Credentials ---"
source "${E2E_ROOT}/scripts/setup-mirror-registry.sh"

# Step 4: Populate mirror AR repo with cnspec image
info "--- Step: Populate Mirror Registry ---"
source "${E2E_ROOT}/scripts/populate-mirror-registry.sh"

# Step 5: Deploy test workload
info "--- Step: Deploy Test Workload ---"
source "${E2E_ROOT}/scripts/deploy-test-workload.sh"

# Step 6: Set proxy env vars from Terraform if enabled
if [[ "${ENABLE_PROXY_TEST}" == "true" ]]; then
info "Proxy testing enabled — Squid proxy at ${SQUID_PROXY_IP}"
else
info "Proxy testing disabled — skipping proxy configuration"
fi

# Step 7: Deploy operator with mirroring/proxy configuration
info "--- Step: Deploy Operator with Mirroring ---"
source "${E2E_ROOT}/scripts/deploy-operator-mirroring.sh"

# Step 8: Apply MondooAuditConfig
info "--- Step: Apply Mondoo Config ---"
source "${E2E_ROOT}/scripts/apply-mondoo-config.sh"

# Step 9: Wait for operator to reconcile
info "Waiting 90s for operator to reconcile with mirrored images..."
sleep 90

# Step 10: Standard verification
info "--- Step: Standard Verify ---"
source "${E2E_ROOT}/scripts/verify.sh"

# Step 11: Mirroring-specific verification
info "--- Step: Verify Mirroring & Proxy ---"
source "${E2E_ROOT}/scripts/verify-mirroring.sh"

info ""
info "=========================================="
info " Test: Registry Mirroring & Proxy - COMPLETE"
info "=========================================="
4 changes: 4 additions & 0 deletions tests/e2e/scripts/cleanup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,8 @@ fi
info "Removing mondoo Helm repo..."
helm repo remove mondoo 2>/dev/null || true

# Mirror registry resources (from registry mirroring tests)
info "Deleting mirror-registry-creds secret..."
kubectl delete secret mirror-registry-creds -n "${NAMESPACE}" --ignore-not-found

info "=== Cleanup complete ==="
11 changes: 11 additions & 0 deletions tests/e2e/scripts/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ load_tf_outputs() {
export TARGET_KUBECONFIG_PATH="$(cd "${TF_DIR}" && realpath "$(terraform output -raw target_kubeconfig_path)")"
fi

export ENABLE_MIRROR_TEST="$(terraform output -raw enable_mirror_test 2>/dev/null || echo "false")"
if [[ "${ENABLE_MIRROR_TEST}" == "true" ]]; then
export MIRROR_REGISTRY="$(terraform output -raw mirror_registry_repo)"
export MIRROR_SA_KEY_B64="$(terraform output -raw mirror_sa_key_b64)"
fi

export ENABLE_PROXY_TEST="$(terraform output -raw enable_proxy_test 2>/dev/null || echo "false")"
if [[ "${ENABLE_PROXY_TEST}" == "true" ]]; then
export SQUID_PROXY_IP="$(terraform output -raw squid_proxy_ip)"
fi

cd ->/dev/null

# Use gcloud to get cluster credentials (auto-refreshing auth)
Expand Down
Loading
Loading