Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
dbae43f
Add airgap-test example and spot instance support for AKS
Feb 4, 2026
dde7d8d
Fix airgap-test: use regular nodes instead of spot instances
Feb 4, 2026
e1a877e
Use spot instances without taints for airgap testing
Feb 4, 2026
b5abbee
fix: apply NSG after AKS cluster bootstrap completes
Feb 5, 2026
e1acfa2
Add system node pool sizing and BYOC values template for airgap test
Feb 5, 2026
6130498
Add one-button airgap test pipeline
Feb 5, 2026
684f7d2
Fix run-airgap-test.sh bugs discovered during testing
Feb 6, 2026
c90eeb4
fix: strip whitespace from node count to fix macOS arithmetic error
Feb 6, 2026
b5fe798
feat: add airgap test runner container
Feb 6, 2026
3201dcf
feat: update pipeline to use airgap-test-runner container
Feb 6, 2026
b7ee72f
fix: handle grep exit code 1 when all nodes are Ready
Feb 6, 2026
a2d2a50
fix: add yq to container and fix grep exit code handling
Feb 6, 2026
59e81d6
fix: add gawk to container
Feb 6, 2026
9a5ee35
feat: add run-in-container.sh wrapper script
Feb 7, 2026
d676bb2
fix: add diffutils (cmp) to container
Feb 7, 2026
e3567bc
fix: do ACR login inside container before running test
Feb 7, 2026
f7bdfa2
fix: use --entrypoint for bash in container
Feb 7, 2026
a60bfae
fix: mount docker config.json for ACR credentials instead of running …
Feb 7, 2026
cf7eddf
Enable azurerm backend and harden .gitignore for airgap-test
Feb 12, 2026
4282cfa
Consolidate airgap test: add precheck, wire healthcheck, delete dead …
Feb 12, 2026
07c036f
Fix smoke test issues: precheck auth, values substitution, config-awa…
Feb 13, 2026
b0ae942
Version-aware registry precheck and stale helm release cleanup
Feb 13, 2026
d92abbe
Pre-authenticate helm to ACR before BYOC install, add util-linux to c…
Feb 20, 2026
6e464f6
Set QUIX_CHART_REGISTRY to quixregistry for airgap chart downloads
Feb 20, 2026
ed00240
Bump install timeout from 30m to 60m (install takes ~30m on spot inst…
Feb 23, 2026
06a0e37
Add ANSIBLE_TIMEOUT override for monitoring role
Feb 23, 2026
6c42d4e
Fall back to job polling when dev.sh log stream disconnects
Feb 23, 2026
dd973bc
Use admin kubeconfig to prevent token expiry mid-install
Feb 23, 2026
343f536
Fix context name for admin kubeconfig
Feb 23, 2026
cb78562
Stream pod logs during job polling fallback
Feb 23, 2026
54f01b5
Fix workspace-service crash: set helmNamespace to "helm" explicitly
Feb 23, 2026
bc67b3c
Switch to fat airgap installer image with baked-in BYOC files
Feb 23, 2026
edb6321
Post-install NSG lockdown and negative airgap test
Feb 23, 2026
d98d00b
Use --no-local-files for prod-like installer path
Feb 23, 2026
5d92177
Fix set -e killing script when bg log process exits before kill
Feb 24, 2026
f513419
Precheck platform image tags from container_versions.yaml
Feb 24, 2026
a6745df
Fix lockdown NSG race and update default installer tag
Feb 24, 2026
b314b0a
Remove org-specific values from GitHub repo
Feb 25, 2026
8679760
Add .claude/ to gitignore
Feb 25, 2026
f1f08a8
minor fixes
Feb 26, 2026
1666832
Switch airgap test from spot to on-demand instances
Feb 27, 2026
fd55fee
Pass --no-sync-versions to dev.sh install in airgap test
Feb 27, 2026
77b3d20
Add -input=false and deadline-based timeout to airgap test
Feb 27, 2026
00bbccb
fix: Generate self-signed TLS cert and pass via Helm values for airga…
Mar 3, 2026
f9bf329
feat: Add Auth0 authentication configuration for airgap test
Mar 3, 2026
fd20335
feat: Allow Auth0 outbound in post-install network lockdown
Mar 3, 2026
09aa13f
feat: Extract container_versions.yaml from installer image for precheck
Mar 3, 2026
43d0321
chore: Bake crane into airgap-runner image, simplify version extraction
Mar 3, 2026
d1dac90
docs: Use git SHA for runner image tag convention in Dockerfile comments
Mar 3, 2026
cd7bc9c
fix: Replace dig with getent, make platform version checks non-fatal
Mar 3, 2026
39165b1
fix: Use GNU tar syntax for container_versions extraction
Mar 3, 2026
629e264
fix: Correct container_versions path to ansible-baked in fat installer
Mar 3, 2026
c51e1ee
feat: Add OpsGenie and EU Auth0 to post-lockdown NSG rules
Mar 4, 2026
a08880d
feat: Fixed CA for airgap test, disable external services in values
Mar 4, 2026
eebf8a2
fix: Use extfile for SAN in cert generation (OpenSSL 1.1.1 compat)
Mar 4, 2026
c1a4f23
ci: add dev environment Terraform config (dev1-dev5)
Mar 9, 2026
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: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ venv/
# kubeconfig (if generated locally here)
.kube/

# AI agent workspace
.claude/

18 changes: 18 additions & 0 deletions examples/airgap-test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Rendered values (contains secrets)
byoc-values.yaml

# Terraform state (managed remotely in CI/CD)
*.tfstate
*.tfstate.*
.terraform/
.terraform.lock.hcl

# Local vars (may contain secrets)
terraform.tfvars
*.auto.tfvars

# Backend override (generated for local runs)
backend_override.tf

# AI agent workspace
.claude/
104 changes: 104 additions & 0 deletions examples/airgap-test/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Airgap Test Runner Container
#
# Complete toolchain for running airgap tests:
# - Terraform (infrastructure provisioning)
# - Azure CLI (Azure operations)
# - kubectl (Kubernetes operations)
# - Helm (chart operations)
#
# Note: Ansible runs inside the K8s cluster via Jobs deployed by Helm,
# not on this machine.
#
# Build:
# docker build --platform linux/amd64 -t $ACR_REGISTRY/airgap-test-runner:$(git rev-parse --short HEAD) .
#
# Push (after az acr login -n $ACR_NAME):
# docker push $ACR_REGISTRY/airgap-test-runner:$(git rev-parse --short HEAD)
#
# Run (interactive):
# docker run -it --rm \
# -v ~/.azure:/root/.azure \
# -v ~/.kube:/root/.kube \
# -v $(pwd):/workspace \
# -w /workspace \
# -e QUIX_ACR_USERNAME -e QUIX_ACR_PASSWORD \
# -e ACR_REGISTRY -e ACR_ID \
# -e QUIX_LICENSE_KEY \
# $ACR_REGISTRY/airgap-test-runner:latest ./run-airgap-test.sh
#
# For Azure DevOps:
# container: $ACR_REGISTRY/airgap-test-runner:latest
# Mount credentials via pipeline variables

FROM mcr.microsoft.com/azure-cli:cbl-mariner2.0

# Tool versions - pinned for reproducibility
ARG TERRAFORM_VERSION=1.7.5
ARG KUBECTL_VERSION=1.33.0
ARG HELM_VERSION=3.14.0

# Install base packages
# shadow-utils: provides su/useradd (required by Azure DevOps container job init)
# sudo: required by Azure DevOps to grant pipeline user permissions
RUN tdnf install -y \
unzip \
curl \
tar \
gzip \
git \
jq \
gawk \
diffutils \
ca-certificates \
shadow-utils \
sudo \
util-linux \
&& tdnf clean all

# Install yq (YAML processor - required by dev.sh)
RUN curl -fsSL "https://github.com/mikefarah/yq/releases/download/v4.40.5/yq_linux_amd64" -o /usr/local/bin/yq \
&& chmod +x /usr/local/bin/yq

# Install Terraform
RUN curl -fsSL "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip" -o /tmp/terraform.zip \
&& unzip /tmp/terraform.zip -d /usr/local/bin \
&& rm /tmp/terraform.zip \
&& chmod +x /usr/local/bin/terraform

# Install kubectl
RUN curl -fsSL "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl" -o /usr/local/bin/kubectl \
&& chmod +x /usr/local/bin/kubectl

# Install Helm
RUN curl -fsSL "https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz" -o /tmp/helm.tar.gz \
&& tar -xzf /tmp/helm.tar.gz -C /tmp \
&& mv /tmp/linux-amd64/helm /usr/local/bin/helm \
&& rm -rf /tmp/helm.tar.gz /tmp/linux-amd64 \
&& chmod +x /usr/local/bin/helm

# Install crane (container registry tool - used to extract files from installer image)
ARG CRANE_VERSION=0.20.3
RUN curl -fsSL "https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" \
| tar xz -C /usr/local/bin crane \
&& chmod +x /usr/local/bin/crane

# Create workspace directory
WORKDIR /workspace

# Verify all tools are installed
RUN echo "=== Tool Versions ===" \
&& az version --output table \
&& terraform version \
&& kubectl version --client \
&& helm version --short \
&& crane version \
&& echo "=== All tools installed successfully ==="

# Set bash as default shell for better script compatibility
SHELL ["/bin/bash", "-c"]

# Azure DevOps container jobs expect /home/vsts to exist as the working directory
RUN mkdir -p /home/vsts && chmod 777 /home/vsts

# No ENTRYPOINT/CMD - Azure DevOps manages the container lifecycle for container jobs.
# For local use, run with: docker run -it ... /bin/bash
194 changes: 194 additions & 0 deletions examples/airgap-test/byoc-values.yaml.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# BYOC Values Template for Airgap Test Cluster
#
# Uses the fat airgap installer image which has all BYOC ansible files,
# helm charts, and dependencies baked in. No zip download needed at runtime.
#
# Required settings discovered during testing:
# - monitoringEnabled: "true" - deploys prometheus/grafana
# - loggingEnabled: "true" - deploys MinIO for object storage
# - minio storage class: managed-csi (Azure Disk, not Azure Files)
#
# The airgap cluster uses NSG rules to block external traffic.
# All images must come from ACR_REGISTRY

# Self-signed TLS certificate for the test domain, generated at runtime by
# run-airgap-test.sh. These flow through the cert_secrets.yaml Helm hook into
# the agent-certificates secret, which is mounted into the installer pod at
# /app/ansible/assets/custom-certificate/. The ingress role reads those files
# and creates the wildcard TLS secret for Traefik.
fullchainPemBase64: GENERATE_AT_RUNTIME
privkeyPemBase64: GENERATE_AT_RUNTIME
customCaPemBase64: GENERATE_AT_RUNTIME

global:
byocZipVersion: ""
cloudProvider: azure
customerName: airgap-test-CHANGEME
destinationDirectory: /app/ansible
environment: test
externalByocVersionsRepository:
enabled: false
pat: ""
tag: refs/tags/stable-release
kubeContext: agent-internal
namespace: quix
namespacePrefix: quix
# Disabled: airgap installer has everything baked in, no zip download needed
privateByocStorageAccount:
clientId: ""
clientSecret: ""
enabled: false
secretName: zip-secret
tenantId: ""
privateDockerRegistryPassword: CHANGEME
privateDockerRegistryUrl: ACR_REGISTRY
privateDockerRegistryUsername: CHANGEME
rootDomain: airgap-test.internal
tier: BringYourOwnCluster
# Helm chart OCI path prefix within the registry.
# Charts live at ACR_REGISTRY/helm/<chart-name>
# Ansible installer defaults empty string to "helm" via Jinja2 default(value, true),
# but workspace-service reads this literally via platform_values.yaml.j2 default(value)
# which does NOT treat empty string as falsy. So we must set this explicitly.
helmNamespace: "helm"
image:
containerRegistry: ACR_REGISTRY
pullPolicy: IfNotPresent
service: quixplatform-ansible-builder
# Substituted by run-airgap-test.sh from INSTALLER_TAG env var
tag: INSTALLER_TAG
env:
# Increase per-role timeout from default 600s (10m) to 2400s (40m).
# kube-prometheus-stack needs >20 minutes (helm install alone is 15m,
# plus setup/diff/post-install tasks eat into budget).
ANSIBLE_TIMEOUT: "2400"
imagePullSecret:
enabled: true
name: registrypullsecret
licenseKey: CHANGEME
platformVariables:
infrastructure:
storage:
class:
premiumRwo: managed-csi
standardRwo: managed-csi
standardRwx: azurefile-csi
# IMPORTANT: MinIO needs managed-csi (Azure Disk) not azurefile-csi
# Azure Files doesn't support chmod which MinIO requires
minio: managed-csi
size:
gitea: 10Gi
internalRegistry: 10Gi
minio: 10Gi
containerCache:
enabled: false
platform:
subdomain: portal
customDomain: airgap-test.internal
# Disable samples library - it tries to clone from github.com
samplesLibrary:
enabled: false
featureFlags:
customCertificateAuthoritySupplied: "true"
allowDirectSignups: "true"
allowSocialSignups: "false"
# Disable external service references unreachable in airgap
portal:
bookADemoLink: ""
smtp:
host: ""
security:
provider: "auth0"
auth0:
genericAuth0Identity: "Quix-Dev"
tenantName: "quix-byoc"
tenantRegion: "eu"
ingress:
serviceType: LoadBalancer
certClusterIssuerCreate: "false"
kafka:
streaming:
instances:
- adminAuthSaslMechanism: ScramSha512
adminAuthSecurityMode: SaslSsl
adminBrokerList: kafka-streaming-airgap-kafka-streaming-airgap-pool-sha-0.quix-kafka-streaming-airgap.svc.cluster.local:9093
authSaslMechanism: SCRAM-SHA-512,PLAIN
brokerList: kafka-streaming-airgap-kafka-streaming-airgap-pool-sha-0.quix-kafka-streaming-airgap.svc.cluster.local:9093
brokerStorageSize: 10Gi
certType: self
chartProvider: strimzi
externalStartingPort: "30095"
extraVars:
certificate: {}
ingressRouteTcp: {}
kafka: {}
middleware: {}
namespace: {}
nodepool: {}
ipAllowList: ""
isDefault: true
kafkaExporterExpose: false
kafkaProvider: selfHosted
kafkaSubdomain: kafka-streaming-airgap
kafkaVersion: 3.9.0
listenerConfig:
name: sha
port: 9093
msk:
clusterArn: ""
kmsKeyId: ""
name: kafka-streaming-airgap
replicaCount: "1"
resources:
limits:
cpu: 2000m
memory: 4Gi
requests:
cpu: 500m
memory: 2Gi
securityMode: SaslSsl
storageClass: "managed-csi"
zookeeperServers: ""
mongo:
replicaCount: "1"
storageSize: 8Gi
rolesFlags:
acmeEnabled: "false"
giteaEnabled: "true"
ingressEnabled: "true"
installLoadbalancer: "false"
internalRegistryEnabled: "false"
# IMPORTANT: loggingEnabled must be true - it deploys MinIO for object storage
loggingEnabled: "true"
mongoDbEnabled: "true"
# IMPORTANT: monitoringEnabled must be true for prometheus/grafana
monitoringEnabled: "true"
platformEnabled: "true"
streamingKafkaEnableInternalHosting: "true"
streamingKafkaEnableStrimziOperator: "true"
secrets:
secretName: platform-secrets
auth0:
clientId: CHANGEME_AUTH0_CLIENT_ID
clientSecret: CHANGEME_AUTH0_CLIENT_SECRET
cliAuth0ClientId: ""
dockerHub:
enabled: false
mongodb:
replicasetkey: HOOK_ALPHANUMERIC_16
rootAdminName: root
rootAdminPassword: HOOK_ALPHANUMERIC_16
gitea:
password: HOOK_ALPHANUMERIC_16
token: HOOK_ALPHANUMERIC_32
streamingKafka:
keystorePassword: HOOK_ALPHANUMERIC_16
saslPassword: HOOK_ALPHANUMERIC_16
saslUsername: HOOK_ALPHANUMERIC_16
truststorePassword: HOOK_ALPHANUMERIC_16
customerBuilds:
registry: "registry.airgap-test.internal"
registryPassword: HOOK_ALPHANUMERIC_16
registryUsername: HOOK_ALPHANUMERIC_16
serviceAccount:
name: quix-agent-account
31 changes: 31 additions & 0 deletions examples/airgap-test/certs/ca.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFSzCCAzOgAwIBAgIUJnzhe+RQiGlJgrXRA1cNv9AONaEwDQYJKoZIhvcNAQEL
BQAwLTEcMBoGA1UEAwwTQWlyZ2FwIFRlc3QgUm9vdCBDQTENMAsGA1UECgwEUXVp
eDAeFw0yNjAzMDQwODIxMTNaFw0zNjAzMDEwODIxMTNaMC0xHDAaBgNVBAMME0Fp
cmdhcCBUZXN0IFJvb3QgQ0ExDTALBgNVBAoMBFF1aXgwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQC4SYh6gkAHcOwBB/MMBciA4KFcQP/mB/YhTPHLl6uY
oOzGg3+eF3g3MY6+bRh3QxJgFWE/tSbbeHNu0exUpzeTuA7XXgHdPuoG7nXtSWQG
VDgzRTo8RYCg7P4SLb67VENwoQs1v6chnyd7pXm0Y1U/Ai2pCMSixCMkRX56OO4X
67PVQ/FRCJS823uwnDNbDqiMwy4m+tP/u+STaYHFeLQtduoi/42S6Mlr3CxhWw25
cuRt62443jp8LRDT2XVjBuH4ctPwIpZzMzfRGRVY4r9F3r7r2y00JDoRQmk4jfjq
sym+rxWn/Mhz8UnbfO01DeJZ8OtSoT9Kypa6JrJIyMbE7L0sSeDf7dDOu0OKYCWZ
/Fme3dpUcAOjaJuplsZIPbLA4EiJUr1L14kBF2WKWXmHNVxZnTEe3y/2EjgOlvKq
fzBMGvCMPd5s3ZS+IUD9qnO8JGlM7nIwnrnEOLJjKqjyaFeeEuVkD8oQ3U0DtagN
5XWv3oswAjQ5GUOLx/q43xp+M703KNH7kNepUdDwFKJwB2m5dPeuEnrwuNeu4tng
V5cbdI57JWQYJDHaQWFBLasiW0GTBeuVgExh8W9eRdAPPsA3UcPaiNRxVoiNKWLa
8AA+GcIlZWATP6tdxnHwPhJLmCAuIREejmEflNRm0kpMJUoXQ9gx3GOUem/1h8FM
2QIDAQABo2MwYTAdBgNVHQ4EFgQUnB34Y19MjV0VXFYg9NQTsci709gwHwYDVR0j
BBgwFoAUnB34Y19MjV0VXFYg9NQTsci709gwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAKmw8MC+eTTiDNghSM/7sojz
XcYxsfv22MOeJnL4NcWWOnnqGtcQkezpjUKtqKEO0jB0uz1wEyGxNHb8GbBnap6t
McNz2tIRKUjuEkQoh3cHNdYfhq3FYeFRRJTJa1Ve0R9aLvOkBqk3WJDjAqDf8ylB
6GtYKXtGaCZU+uZnNioP7unD09EF09tMuYt1YKi1+yixAHdvldHXKuN/Bul9nkyZ
Q0gyGrkfjBsPMWIEqFjMwhzX1L6ITQBfZo9Ic6xrjBWlAtlsG5NB2EYZD70MVVRX
5R/9qeYYdzVPp0DobxpImnPsd6fKLFh81WXjPKHtGJp76gEUVbyCdVe4SpjRLHsz
qLBwMhLq5MUpb8rU158/zUq5nUU8DuQk/3sAhDziwiglOhxJaJF1M2WcJa+SzVJm
gQMHvfaoaR56//0lCelcgDJ15gg/tB50kM2J6WljBQuHmj7Deiiud9hwepJLRkgv
k1lX4HuOBDg7N8+MukhiRAJOZaMVntg5+dll6kj13OED0rYV/ltIaO45UBsT52vT
VLoPgl597MZxXQX9k5Rm7f3kucQ/gihmi0oOZW63eVjCif9mFPMRTu2eXdHPAlBh
qj+XWfo9fiW/CjN9HIZPpskmMQP/0wtsFFLzf7SE86QciVj0BtPcW9vLV/0SNozm
kbooQH+O4e7xL/y4XaUG
-----END CERTIFICATE-----
Loading