1 Commits

Author SHA1 Message Date
unkinben 2254a39d77 feat: add artifact-keeper
- converted the artifact-keeper helm-chart into kustomization manifests
- converted postgres to cnpg
- moved secrets to vault
2026-04-19 18:43:56 +10:00
24 changed files with 1500 additions and 0 deletions
@@ -0,0 +1,94 @@
---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: artifact-keeper-postgres
namespace: artifact-keeper
spec:
affinity:
podAntiAffinityType: preferred
bootstrap:
initdb:
database: artifact_registry
encoding: UTF8
localeCType: C
localeCollate: C
owner: registry
secret:
name: postgres-credentials
postInitSQL:
- CREATE DATABASE dependency_track OWNER registry;
- GRANT ALL PRIVILEGES ON DATABASE dependency_track TO registry;
enablePDB: true
enableSuperuserAccess: false
failoverDelay: 0
imageName: ghcr.io/cloudnative-pg/postgresql:17-minimal-trixie
instances: 3
logLevel: info
maxSyncReplicas: 0
minSyncReplicas: 0
monitoring:
customQueriesConfigMap:
- key: queries
name: cnpg-default-monitoring
disableDefaultQueries: false
enablePodMonitor: false
postgresql:
parameters:
archive_mode: "on"
archive_timeout: 5min
dynamic_shared_memory_type: posix
effective_cache_size: 256MB
full_page_writes: "on"
log_destination: csvlog
log_directory: /controller/log
log_filename: postgres
log_rotation_age: "0"
log_rotation_size: "0"
log_truncate_on_rotation: "false"
logging_collector: "on"
max_connections: "200"
max_parallel_workers: "16"
max_replication_slots: "16"
max_worker_processes: "16"
shared_buffers: 128MB
shared_memory_type: mmap
ssl_max_protocol_version: TLSv1.3
ssl_min_protocol_version: TLSv1.3
wal_keep_size: 256MB
wal_level: logical
wal_log_hints: "on"
wal_receiver_timeout: 5s
wal_sender_timeout: 5s
syncReplicaElectionConstraint:
enabled: false
primaryUpdateMethod: restart
primaryUpdateStrategy: unsupervised
probes:
liveness:
isolationCheck:
connectionTimeout: 1000
enabled: true
requestTimeout: 1000
replicationSlots:
highAvailability:
enabled: true
slotPrefix: _cnpg_
synchronizeReplicas:
enabled: true
updateInterval: 30
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: 250m
memory: 256Mi
smartShutdownTimeout: 180
startDelay: 3600
stopDelay: 1800
storage:
resizeInUseVolumes: true
size: 20Gi
storageClass: cephrbd-fast-delete
switchoverDelay: 3600
@@ -0,0 +1,33 @@
---
apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
name: artifact-keeper-postgres-pooler
namespace: artifact-keeper
spec:
cluster:
name: artifact-keeper-postgres
instances: 2
pgbouncer:
parameters:
default_pool_size: "100"
max_client_conn: "400"
paused: false
poolMode: session
template:
metadata:
labels:
app: artifact-keeper-pooler
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- artifact-keeper-pooler
topologyKey: kubernetes.io/hostname
containers: []
type: rw
@@ -0,0 +1,20 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
data:
BIND_ADDRESS: "0.0.0.0:8080"
LOG_LEVEL: "info,artifact_keeper=debug"
STORAGE_BACKEND: "s3"
MEILISEARCH_URL: "http://meilisearch:7700"
TRIVY_URL: "http://trivy:8090"
DEPENDENCY_TRACK_URL: "http://dtrack:8080"
DEPENDENCY_TRACK_ENABLED: "true"
SCAN_WORKSPACE_PATH: "/scan-workspace"
PLUGINS_DIR: "/data/plugins"
@@ -0,0 +1,15 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: s3-env
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
data:
S3_ENDPOINT: "https://radosgw.service.consul"
S3_BUCKET: "artifact-keeper"
S3_REGION: "ap-southeast-2"
S3_PATH_STYLE: "true"
@@ -0,0 +1,171 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: backend
annotations:
reloader.stakater.com/auto: "true"
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: backend
template:
metadata:
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: backend
spec:
serviceAccountName: backend
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: In
values:
- backend
topologyKey: kubernetes.io/hostname
initContainers:
- name: wait-for-postgres
image: postgres:16-alpine
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 100m
memory: 64Mi
command: ["/bin/sh", "-c"]
args:
- |
echo "Waiting for PostgreSQL..."
until pg_isready -h artifact-keeper-postgres-pooler -p 5432 -U registry; do
sleep 3
done
echo "PostgreSQL is ready"
- name: wait-for-meilisearch
image: alpine:3.20
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: 10m
memory: 16Mi
ephemeral-storage: 32Mi
limits:
cpu: 100m
memory: 64Mi
ephemeral-storage: 64Mi
command: ["/bin/sh", "-c"]
args:
- |
echo "Waiting for Meilisearch..."
until wget -qO- http://meilisearch:7700/health >/dev/null 2>&1; do
sleep 3
done
echo "Meilisearch is ready"
containers:
- name: backend
image: "ghcr.io/artifact-keeper/artifact-keeper-backend:dev"
imagePullPolicy: Always
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
command: ["/bin/sh", "-c"]
args:
- |
if [ -f /shared/dtrack-api-key ] && [ -s /shared/dtrack-api-key ]; then
export DEPENDENCY_TRACK_API_KEY="$(cat /shared/dtrack-api-key)"
fi
exec /usr/local/bin/artifact-keeper
ports:
- name: http
containerPort: 8080
protocol: TCP
- name: grpc
containerPort: 9090
protocol: TCP
envFrom:
- configMapRef:
name: config
- configMapRef:
name: s3-env
- secretRef:
name: s3-credentials
- secretRef:
name: app-secrets
resources:
limits:
cpu: "2"
memory: 2Gi
requests:
cpu: 250m
memory: 256Mi
livenessProbe:
httpGet:
path: /livez
port: http
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 5
volumeMounts:
- name: tmp
mountPath: /tmp
- name: storage
mountPath: /data/storage
subPath: storage
- name: storage
mountPath: /data/backups
subPath: backups
- name: storage
mountPath: /data/plugins
subPath: plugins
- name: scan-workspace
mountPath: /scan-workspace
- name: shared-config
mountPath: /shared
readOnly: true
volumes:
- name: tmp
emptyDir:
sizeLimit: 256Mi
- name: storage
persistentVolumeClaim:
claimName: storage
- name: scan-workspace
persistentVolumeClaim:
claimName: scan-workspace
- name: shared-config
persistentVolumeClaim:
claimName: shared-config
@@ -0,0 +1,111 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dtrack
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: dependency-track
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: dependency-track
template:
metadata:
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: dependency-track
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
initContainers:
- name: wait-for-postgres
image: postgres:16-alpine
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 100m
memory: 64Mi
command: ["/bin/sh", "-c"]
args:
- |
echo "Waiting for PostgreSQL..."
until pg_isready -h artifact-keeper-postgres-pooler -p 5432 -U registry; do
sleep 3
done
echo "PostgreSQL is ready"
containers:
- name: dtrack-api
image: "dependencytrack/apiserver:4.11.4"
imagePullPolicy: IfNotPresent
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: ALPINE_DATABASE_MODE
value: "external"
- name: ALPINE_DATABASE_URL
value: "jdbc:postgresql://artifact-keeper-postgres-pooler:5432/dependency_track"
- name: ALPINE_DATABASE_DRIVER
value: "org.postgresql.Driver"
- name: ALPINE_DATABASE_USERNAME
value: "registry"
- name: ALPINE_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: password
- name: ALPINE_DATA_DIRECTORY
value: "/data"
- name: ALPINE_ENFORCE_AUTHENTICATION
value: "true"
- name: ALPINE_CORS_ENABLED
value: "true"
- name: ALPINE_CORS_ALLOW_ORIGIN
value: "*"
- name: JAVA_OPTIONS
value: "-Xmx4g"
resources:
limits:
cpu: "2"
memory: 6Gi
requests:
cpu: 250m
memory: 4Gi
volumeMounts:
- name: dtrack-data
mountPath: /data
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 256Mi
- name: dtrack-data
persistentVolumeClaim:
claimName: dtrack
@@ -0,0 +1,154 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: meilisearch
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: meilisearch
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: meilisearch
template:
metadata:
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: meilisearch
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
initContainers:
- name: fix-ownership
image: busybox:1.37
securityContext:
runAsNonRoot: false
runAsUser: 0
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
add:
- CHOWN
- FOWNER
resources:
requests:
cpu: 10m
memory: 16Mi
ephemeral-storage: 32Mi
limits:
cpu: 100m
memory: 64Mi
ephemeral-storage: 64Mi
command: ["sh", "-c", "chown -R 1000:1000 /meili_data"]
volumeMounts:
- name: meilisearch-data
mountPath: /meili_data
- name: version-guard
image: busybox:1.37
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 100m
memory: 64Mi
command: ["sh", "-c"]
args:
- |
EXPECTED="v1.12"
VERSION_FILE="/meili_data/data.ms/VERSION"
if [ ! -f "$VERSION_FILE" ]; then
echo "No existing database, fresh start"
exit 0
fi
CURRENT=$(cat "$VERSION_FILE" 2>/dev/null || echo "unknown")
echo "Current DB version: $CURRENT, expected image: $EXPECTED"
if echo "$CURRENT" | grep -qv "$(echo $EXPECTED | sed 's/^v//')"; then
echo "Version mismatch — wiping data.ms for clean re-index"
rm -rf /meili_data/data.ms
echo "Done. Backend will re-index automatically."
else
echo "Versions match, keeping existing data"
fi
volumeMounts:
- name: meilisearch-data
mountPath: /meili_data
containers:
- name: meilisearch
image: "getmeili/meilisearch:v1.12"
imagePullPolicy: IfNotPresent
securityContext:
readOnlyRootFilesystem: false
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
ports:
- name: http
containerPort: 7700
protocol: TCP
env:
- name: MEILI_MASTER_KEY
valueFrom:
secretKeyRef:
name: app-secrets
key: MEILISEARCH_API_KEY
- name: MEILI_ENV
value: "production"
- name: MEILI_MAX_INDEXING_THREADS
value: "4"
resources:
limits:
cpu: "1"
memory: 8Gi
requests:
cpu: 250m
memory: 512Mi
readinessProbe:
httpGet:
path: /health
port: 7700
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
livenessProbe:
httpGet:
path: /health
port: 7700
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
volumeMounts:
- name: meilisearch-data
mountPath: /meili_data
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 256Mi
- name: meilisearch-data
persistentVolumeClaim:
claimName: meilisearch
@@ -0,0 +1,87 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: trivy
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: trivy
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: trivy
template:
metadata:
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: trivy
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 10000
fsGroup: 10000
containers:
- name: trivy
image: "aquasec/trivy:0.62.1"
imagePullPolicy: IfNotPresent
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
command: ["trivy"]
args: ["server", "--listen", "0.0.0.0:8090", "--cache-dir", "/home/trivy/.cache"]
ports:
- name: http
containerPort: 8090
protocol: TCP
resources:
limits:
cpu: "1"
memory: 2Gi
requests:
cpu: 250m
memory: 256Mi
readinessProbe:
tcpSocket:
port: 8090
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
livenessProbe:
tcpSocket:
port: 8090
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
volumeMounts:
- name: trivy-cache
mountPath: /home/trivy/.cache
- name: tmp
mountPath: /tmp
- name: scan-workspace
mountPath: /scan-workspace
readOnly: true
volumes:
- name: tmp
emptyDir:
sizeLimit: 256Mi
- name: trivy-cache
persistentVolumeClaim:
claimName: trivy-cache
- name: scan-workspace
persistentVolumeClaim:
claimName: scan-workspace
@@ -0,0 +1,98 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: web
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: web
template:
metadata:
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: web
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/component
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
containers:
- name: web
image: "ghcr.io/artifact-keeper/artifact-keeper-web:dev"
imagePullPolicy: Always
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
ports:
- name: http
containerPort: 3000
protocol: TCP
env:
- name: NEXT_PUBLIC_API_URL
value: ""
- name: BACKEND_URL
value: "http://backend:8080"
- name: NODE_ENV
value: "production"
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: 250m
memory: 256Mi
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 20
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 5
volumeMounts:
- name: tmp
mountPath: /tmp
- name: nextjs-cache
mountPath: /app/.next/cache
volumes:
- name: tmp
emptyDir:
sizeLimit: 256Mi
- name: nextjs-cache
emptyDir:
sizeLimit: 1Gi
+286
View File
@@ -0,0 +1,286 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: artifact-keeper
namespace: artifact-keeper
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/proxy-body-size: 10g
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
cert-manager.io/cluster-issuer: vault-issuer
cert-manager.io/common-name: artifacts.k8s.syd1.au.unkin.net
cert-manager.io/private-key-size: "4096"
external-dns.alpha.kubernetes.io/hostname: artifacts.k8s.syd1.au.unkin.net
external-dns.alpha.kubernetes.io/target: 198.18.200.0
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
ingressClassName: nginx
rules:
- host: artifacts.k8s.syd1.au.unkin.net
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /health
pathType: Exact
backend:
service:
name: backend
port:
number: 8080
- path: /ready
pathType: Exact
backend:
service:
name: backend
port:
number: 8080
- path: /v2
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /maven
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /npm
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /pypi
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /nuget
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /cargo
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /gems
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /go
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /helm
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /debian
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /rpm
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /alpine
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /composer
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /conan
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /conda
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /swift
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /terraform
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /cocoapods
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /hex
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /pub
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /lfs
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /ivy
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /chef
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /puppet
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /ansible
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /cran
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /huggingface
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /jetbrains
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /vscode
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /proto
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /incus
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /ext
pathType: Prefix
backend:
service:
name: backend
port:
number: 8080
- path: /dtrack
pathType: Prefix
backend:
service:
name: dtrack
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 3000
tls:
- hosts:
- artifacts.k8s.syd1.au.unkin.net
secretName: artifacts-tls
@@ -0,0 +1,70 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: dtrack-init
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: dependency-track
spec:
backoffLimit: 3
template:
metadata:
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: dependency-track
spec:
restartPolicy: OnFailure
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: dtrack-init
image: alpine:3.20
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: 10m
memory: 32Mi
ephemeral-storage: 64Mi
limits:
cpu: 200m
memory: 128Mi
ephemeral-storage: 128Mi
command: ["/bin/sh", "-c"]
args:
- |
apk add --no-cache curl jq >/dev/null 2>&1
/bin/sh /scripts/init-dtrack.sh
env:
- name: DEPENDENCY_TRACK_URL
value: "http://dtrack:8080"
- name: DEPENDENCY_TRACK_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DEPENDENCY_TRACK_ADMIN_PASSWORD
volumeMounts:
- name: init-script
mountPath: /scripts
readOnly: true
- name: shared-config
mountPath: /shared
volumes:
- name: init-script
configMap:
name: dtrack-init
defaultMode: 0755
- name: shared-config
persistentVolumeClaim:
claimName: shared-config
@@ -0,0 +1,33 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- serviceaccount_backend.yaml
- cnpg_cluster.yaml
- cnpg_pooler.yaml
- vaultauth.yaml
- vaultstaticsecret.yaml
- configmap_app-config.yaml
- configmap_s3-env.yaml
- persistentvolumeclaims.yaml
- service_backend.yaml
- service_dtrack.yaml
- service_meilisearch.yaml
- service_trivy.yaml
- service_web.yaml
- deployment_backend.yaml
- deployment_dtrack.yaml
- deployment_meilisearch.yaml
- deployment_trivy.yaml
- deployment_web.yaml
- job_dtrack-init.yaml
- ingress.yaml
configMapGenerator:
- name: dtrack-init
files:
- resources/init-dtrack.sh
options:
disableNameSuffixHash: true
+7
View File
@@ -0,0 +1,7 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
@@ -0,0 +1,78 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: storage
namespace: artifact-keeper
spec:
accessModes:
- ReadWriteMany
storageClassName: cephfs-raid5-delete
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: scan-workspace
namespace: artifact-keeper
spec:
accessModes:
- ReadWriteMany
storageClassName: cephfs-raid5-delete
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-config
namespace: artifact-keeper
spec:
accessModes:
- ReadWriteMany
storageClassName: cephfs-raid5-delete
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dtrack
namespace: artifact-keeper
spec:
accessModes:
- ReadWriteOnce
storageClassName: cephrbd-fast-delete
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: meilisearch
namespace: artifact-keeper
spec:
accessModes:
- ReadWriteOnce
storageClassName: cephrbd-fast-delete
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: trivy-cache
namespace: artifact-keeper
spec:
accessModes:
- ReadWriteOnce
storageClassName: cephrbd-fast-delete
resources:
requests:
storage: 5Gi
+43
View File
@@ -0,0 +1,43 @@
#!/bin/sh
set -e
DT_URL="${DEPENDENCY_TRACK_URL:-http://ak-artifact-keeper-dtrack:8080}"
DT_ADMIN_USER="admin"
DT_DEFAULT_PASS="admin"
DT_NEW_PASS="${DEPENDENCY_TRACK_ADMIN_PASSWORD}"
API_KEY_FILE="/shared/dtrack-api-key"
echo "[dtrack-init] Waiting for Dependency-Track at $DT_URL ..."
for i in $(seq 1 60); do
if curl -sf "$DT_URL/api/version" > /dev/null 2>&1; then break; fi
if [ "$i" -eq 60 ]; then echo "[dtrack-init] ERROR: timeout"; exit 1; fi
sleep 5
done
if [ -f "$API_KEY_FILE" ] && [ -s "$API_KEY_FILE" ]; then
echo "[dtrack-init] API key already provisioned -- skipping"
exit 0
fi
TOKEN=$(curl -sf -X POST "$DT_URL/api/v1/user/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=${DT_ADMIN_USER}&password=${DT_NEW_PASS}" 2>/dev/null || true)
if [ -z "$TOKEN" ] || echo "$TOKEN" | grep -qi "FORCE_PASSWORD_CHANGE"; then
curl -sf -o /dev/null -X POST "$DT_URL/api/v1/user/forceChangePassword" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=${DT_ADMIN_USER}&password=${DT_DEFAULT_PASS}&newPassword=${DT_NEW_PASS}&confirmPassword=${DT_NEW_PASS}"
TOKEN=$(curl -sf -X POST "$DT_URL/api/v1/user/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=${DT_ADMIN_USER}&password=${DT_NEW_PASS}" 2>/dev/null || true)
fi
if [ -z "$TOKEN" ]; then echo "[dtrack-init] ERROR: auth failed"; exit 1; fi
API_KEY=$(curl -sf "$DT_URL/api/v1/team" \
-H "Authorization: Bearer $TOKEN" | \
jq -r '.[] | select(.name == "Automation") | .apiKeys[0].key // empty')
if [ -z "$API_KEY" ]; then echo "[dtrack-init] ERROR: no API key"; exit 1; fi
echo "$API_KEY" > "$API_KEY_FILE"
echo "[dtrack-init] Done"
@@ -0,0 +1,26 @@
---
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: backend
spec:
type: ClusterIP
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
- name: grpc
port: 9090
targetPort: grpc
protocol: TCP
selector:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: backend
@@ -0,0 +1,22 @@
---
apiVersion: v1
kind: Service
metadata:
name: dtrack
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: dependency-track
spec:
type: ClusterIP
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
selector:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: dependency-track
@@ -0,0 +1,22 @@
---
apiVersion: v1
kind: Service
metadata:
name: meilisearch
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: meilisearch
spec:
type: ClusterIP
ports:
- name: http
port: 7700
targetPort: http
protocol: TCP
selector:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: meilisearch
@@ -0,0 +1,22 @@
---
apiVersion: v1
kind: Service
metadata:
name: trivy
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: trivy
spec:
type: ClusterIP
ports:
- name: http
port: 8090
targetPort: http
protocol: TCP
selector:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: trivy
@@ -0,0 +1,22 @@
---
apiVersion: v1
kind: Service
metadata:
name: web
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: web
spec:
type: ClusterIP
ports:
- name: http
port: 3000
targetPort: http
protocol: TCP
selector:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/component: web
@@ -0,0 +1,11 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: backend
namespace: artifact-keeper
labels:
app.kubernetes.io/name: artifact-keeper
app.kubernetes.io/instance: ak
app.kubernetes.io/part-of: artifact-keeper
app.kubernetes.io/component: backend
+18
View File
@@ -0,0 +1,18 @@
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: default
namespace: artifact-keeper
spec:
allowedNamespaces:
- artifact-keeper
kubernetes:
audiences:
- vault
role: default
serviceAccount: default
tokenExpirationSeconds: 600
method: kubernetes
mount: k8s/au/syd1
vaultConnectionRef: vso-system/default
@@ -0,0 +1,51 @@
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: postgres-credentials
namespace: artifact-keeper
spec:
destination:
create: true
name: postgres-credentials
overwrite: true
hmacSecretData: true
mount: kv
path: kubernetes/namespace/artifact-keeper/default/postgres-credentials
refreshAfter: 5m
type: kv-v2
vaultAuthRef: default
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: app-secrets
namespace: artifact-keeper
spec:
destination:
create: true
name: app-secrets
overwrite: true
hmacSecretData: true
mount: kv
path: kubernetes/namespace/artifact-keeper/default/app-secrets
refreshAfter: 5m
type: kv-v2
vaultAuthRef: default
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: s3-credentials
namespace: artifact-keeper
spec:
destination:
create: true
name: s3-credentials
overwrite: true
hmacSecretData: true
mount: kv
path: kubernetes/namespace/artifact-keeper/default/s3-credentials
refreshAfter: 5m
type: kv-v2
vaultAuthRef: default
@@ -0,0 +1,6 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../base/artifact-keeper