Compare commits
10
Commits
cab110c268
...
5aa0439a20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5aa0439a20 | ||
|
|
bf814854fd | ||
|
|
6d3f1643db | ||
|
|
3aebc99635 | ||
|
|
dde2622149 | ||
|
|
dcd3583656 | ||
|
|
4a5f4d94ab | ||
|
|
9014a170d7 | ||
|
|
94a055b341 | ||
|
|
89a0aacbd7 |
@@ -17,7 +17,7 @@ metadata:
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: 'http://gitea.192.168.1.7.nip.io/mukul/devops-infra-argo-config.git'
|
||||
repoURL: 'http://gitea-http.gitea.svc.cluster.local:3000/gitadmin/devops-infra-argo-config-gcp.git'
|
||||
targetRevision: main
|
||||
path: projects
|
||||
directory:
|
||||
|
||||
@@ -57,10 +57,39 @@ rules:
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
# A one-off command run against an app executes in a throwaway Job built
|
||||
# from the app's own image — see internal/deploy.Client.RunCommand. Delete
|
||||
# is needed because the Job is removed once its output has been captured;
|
||||
# without it they accumulate one per command forever.
|
||||
- apiGroups: ["batch"]
|
||||
resources: ["jobs"]
|
||||
verbs: ["get", "list", "watch", "create", "delete"]
|
||||
# An app with persistent storage is a StatefulSet instead of a Deployment
|
||||
# (internal/deploy.Client.ensureStatefulSet). Delete is needed on both:
|
||||
# switching an app between stateless and stateful must remove whichever
|
||||
# controller it no longer is, or the two fight over the same pods.
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["statefulsets"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
# Read-only. The claims themselves are created by the StatefulSet's own
|
||||
# volumeClaimTemplates, never directly here — this is only to observe
|
||||
# them, and deleting one would destroy an app's data.
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# The policy that stops one app reaching another.
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources: ["networkpolicies"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
# Created only for an app with autoscaling enabled (max replicas set
|
||||
# above min); removed again if it's turned back off. See toolshed's own
|
||||
# internal/deploy.Client.ensureAutoscaler. Added alongside that feature —
|
||||
# keep this file and toolshed's deploy/helm/toolshed/templates/rbac.yaml
|
||||
# in sync, per internal/deploy/kubernetes.go's own package doc warning
|
||||
# that the two are unsynchronized copies in two repositories.
|
||||
- apiGroups: ["autoscaling"]
|
||||
resources: ["horizontalpodautoscalers"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
@@ -76,3 +105,47 @@ roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: toolshed-deployer
|
||||
---
|
||||
# A custom domain's Ingress and TLS secret live in the gateway's own
|
||||
# namespace (toolshed, same as above) — never an app's namespace. Scoped
|
||||
# with a namespaced Role/RoleBinding rather than widening the ClusterRole
|
||||
# above: Ingress management here only ever targets this one fixed
|
||||
# namespace, unlike the per-app namespaces the ClusterRole necessarily
|
||||
# spans. Added alongside toolshed's custom-domains feature — see
|
||||
# internal/deploy.Client.EnsureDomainIngress/DomainCertReady/
|
||||
# RemoveDomainIngress and this file's own header comment about keeping it
|
||||
# and deploy/helm/toolshed/templates/rbac.yaml in sync.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: toolshed-deployer-ingress
|
||||
namespace: toolshed
|
||||
labels:
|
||||
app.kubernetes.io/part-of: toolshed
|
||||
rules:
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
# Reads the TLS secret cert-manager's ingress-shim populates for a domain
|
||||
# Ingress, and deletes it (and the Ingress above) when a domain is
|
||||
# removed. Never create/update — cert-manager, not deployer, writes this
|
||||
# secret.
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list", "watch", "delete"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: toolshed-deployer-ingress
|
||||
namespace: toolshed
|
||||
labels:
|
||||
app.kubernetes.io/part-of: toolshed
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: toolshed-deployer
|
||||
namespace: toolshed
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: toolshed-deployer-ingress
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: incubator-infra-gke-toolshed-prd-usc1
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: 'http://gitea-http.gitea.svc.cluster.local:3000/gitadmin/devops-infra-argo-config-gcp.git'
|
||||
targetRevision: main
|
||||
path: generic-argo-apps-chart
|
||||
helm:
|
||||
valueFiles:
|
||||
- ../values/incubator-infra-gke-toolshed-prd-usc1-values.yaml
|
||||
destination:
|
||||
name: in-cluster
|
||||
namespace: argocd
|
||||
@@ -3,13 +3,12 @@ kind: AppProject
|
||||
metadata:
|
||||
name: devops
|
||||
# Single ArgoCD instance — projects live in the same namespace as
|
||||
# ArgoCD itself (see the argocd/argocd-admin note in
|
||||
# values/incubator-infra-k8s-admin-prd-ase1-values.yaml).
|
||||
# ArgoCD itself (see values/incubator-infra-gke-toolshed-prd-usc1-values.yaml).
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
description: "devops project for infra apps on the homelab VM (team: devops in the appSpec labels)."
|
||||
description: "devops project for infra apps on the GKE cluster (team: devops in the appSpec labels)."
|
||||
sourceRepos:
|
||||
- '*'
|
||||
destinations:
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Anthropic API key for quant-agent's Claude review stage.
|
||||
#
|
||||
# Without this the agent still runs — it logs the absence at startup and
|
||||
# operates as a pure quantitative strategy, with every filter and risk
|
||||
# limit still enforced. It simply never asks Claude for a second opinion.
|
||||
#
|
||||
# One-time setup:
|
||||
# kubectl -n vault exec -i vault-0 -- \
|
||||
# vault kv put secret/quant-agent/anthropic apiKey="sk-ant-..."
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: quant-agent-anthropic
|
||||
namespace: quant-agent
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault-backend
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: quant-agent-anthropic
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: apiKey
|
||||
remoteRef:
|
||||
key: quant-agent/anthropic
|
||||
property: apiKey
|
||||
@@ -0,0 +1,41 @@
|
||||
# Zerodha Kite Connect credentials for quant-agent.
|
||||
#
|
||||
# Entirely optional. With no apiKey present the agent uses its built-in
|
||||
# simulated NSE feed, which is what lets the whole system run with no
|
||||
# broker account.
|
||||
#
|
||||
# The access token is the awkward one: Kite tokens are single-session
|
||||
# and expire every trading day around 06:00 IST, and minting a new one
|
||||
# requires an interactive browser redirect that cannot be automated from
|
||||
# inside a pod. Two options, neither automatic:
|
||||
# - re-run the vault kv put below each morning, or
|
||||
# - paste the fresh token into the agent's UI, which applies it live
|
||||
# without a restart (POST /api/kite-token).
|
||||
#
|
||||
# One-time setup:
|
||||
# kubectl -n vault exec -i vault-0 -- \
|
||||
# vault kv put secret/quant-agent/kite apiKey="..." accessToken="..."
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: quant-agent-kite
|
||||
namespace: quant-agent
|
||||
spec:
|
||||
# Shorter than the usual 1h so a token refreshed in Vault mid-morning
|
||||
# reaches the pod reasonably quickly.
|
||||
refreshInterval: 15m
|
||||
secretStoreRef:
|
||||
name: vault-backend
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: quant-agent-kite
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: apiKey
|
||||
remoteRef:
|
||||
key: quant-agent/kite
|
||||
property: apiKey
|
||||
- secretKey: accessToken
|
||||
remoteRef:
|
||||
key: quant-agent/kite
|
||||
property: accessToken
|
||||
@@ -0,0 +1,49 @@
|
||||
# Harbor management-API credentials for toolshed's builder, used only to
|
||||
# delete a repository (every tagged image an app's builds ever pushed) when
|
||||
# the app itself is deleted.
|
||||
#
|
||||
# Deliberately a separate robot account from harbor-robot-dockerconfig
|
||||
# (secretstores/harbor-robot-dockerconfig.yaml), not a reuse of it. That one
|
||||
# is mounted into build pods, which run arbitrary user-supplied Dockerfiles —
|
||||
# scoped to push+pull only. This one is held by builder itself, a trusted
|
||||
# platform service that never executes user code directly, and needs a
|
||||
# permission the other should never carry: delete. Keeping them on separate
|
||||
# credentials means a leaked build-pod credential can never delete anything,
|
||||
# and a leaked cleanup credential can never push.
|
||||
#
|
||||
# Create the robot in Harbor's UI: homelab project -> Robot Accounts ->
|
||||
# New Robot Account. Scope it to that one project, permission "Delete
|
||||
# repository" (and "Delete artifact") only — leave push/pull unchecked, it
|
||||
# never needs them. Harbor shows the generated secret exactly once.
|
||||
#
|
||||
# kubectl -n vault exec -i vault-0 -- sh -lc '
|
||||
# vault login <root-token> >/dev/null &&
|
||||
# vault kv put secret/harbor/toolshed-cleanup \
|
||||
# username="robot\$homelab+toolshed-cleanup" \
|
||||
# password="<the generated secret>"'
|
||||
#
|
||||
# Until this exists, builder logs "no management credentials, leaving its
|
||||
# image alone" for every app deletion and does nothing destructive — see
|
||||
# internal/builder/builder.go's cleanupImage in the toolshed repo.
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: toolshed-harbor-cleanup
|
||||
namespace: toolshed
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault-backend
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: toolshed-harbor-cleanup
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: username
|
||||
remoteRef:
|
||||
key: harbor/toolshed-cleanup
|
||||
property: username
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: harbor/toolshed-cleanup
|
||||
property: password
|
||||
@@ -0,0 +1,54 @@
|
||||
# Redis admin password, backing toolshed's managed cache add-on.
|
||||
#
|
||||
# Only one consumer, unlike the Postgres credential next door: the Redis
|
||||
# server itself needs it to seed its ACL file on first boot. toolshed's api
|
||||
# reads it from the *connection* an operator configures in the dashboard
|
||||
# (encrypted in toolshed's own database via the secretbox keyring), not from
|
||||
# a Kubernetes Secret — so there is deliberately no second ExternalSecret
|
||||
# into the toolshed namespace here.
|
||||
#
|
||||
# Put the credential in Vault BEFORE syncing this. External Secrets cannot
|
||||
# create a Secret for a path that does not exist, and the Redis pod's init
|
||||
# container will sit in CreateContainerConfigError until it can:
|
||||
#
|
||||
# kubectl -n vault exec -i vault-0 -- sh -lc '
|
||||
# vault login <root-token> >/dev/null &&
|
||||
# vault kv put secret/toolshed/redis \
|
||||
# password=<a long alphanumeric password>'
|
||||
#
|
||||
# Use an alphanumeric password. It is written into the ACL file as
|
||||
# `user default on ><password> ...` by the init container, where a space or
|
||||
# a quote would split the directive and produce a server that either fails
|
||||
# to start or, worse, starts with different rules than intended.
|
||||
#
|
||||
# Remember that `kubectl exec` into Vault is unauthenticated by default —
|
||||
# without the `vault login` the commands fail with a "preflight capability
|
||||
# check" error that reads like a permissions bug rather than a missing
|
||||
# login.
|
||||
#
|
||||
# Rotating this password later does NOT propagate to a running server: the
|
||||
# init container only ever writes the ACL file when it is absent, precisely
|
||||
# so it cannot delete the per-app users toolshed has provisioned into it.
|
||||
# To rotate, update Vault and then, against the running server:
|
||||
#
|
||||
# ACL SETUSER default >newpassword
|
||||
# ACL SAVE
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: redis-credentials
|
||||
namespace: redis
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault-backend
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: redis-credentials
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: toolshed/redis
|
||||
property: password
|
||||
@@ -0,0 +1,49 @@
|
||||
# GKE counterpart of incubator-infra-k8s-admin-prd-ase1-values.yaml. Starts
|
||||
# with only the three components installed by hand at bootstrap; every
|
||||
# other homelab component is added here as it is ported, each with its own
|
||||
# helm-overrides/gke-toolshed-prd-usc1/<name>/custom-values.yaml.
|
||||
clusterSpec:
|
||||
destination:
|
||||
server: ""
|
||||
name: "in-cluster"
|
||||
|
||||
argocdSpec:
|
||||
namespace: argocd
|
||||
|
||||
teamSpec:
|
||||
devops:
|
||||
source:
|
||||
# Cluster DNS, not an Ingress: ArgoCD must be able to read this before
|
||||
# Contour exists, because Contour is itself one of these Applications.
|
||||
repoURL: http://gitea-http.gitea.svc.cluster.local:3000/gitadmin/devops-infra-helm-charts-gcp.git
|
||||
targetRevision: main
|
||||
path: helm-templates
|
||||
valueFiles: ../../helm-overrides/gke-toolshed-prd-usc1
|
||||
labels:
|
||||
bu: infra
|
||||
team: devops
|
||||
env: prd
|
||||
cluster: gke-toolshed-prd-usc1
|
||||
|
||||
# nameOverride on every entry must equal the Helm release name used for the
|
||||
# manual bootstrap install. ArgoCD uses the Application name as the release
|
||||
# name, so a mismatch renders a second copy instead of adopting the first.
|
||||
appSpec:
|
||||
- name: argocd
|
||||
nameOverride: argocd-admin-prd
|
||||
namespace: argocd
|
||||
chartDir: argo-cd
|
||||
valuesDir: argocd-admin-prd
|
||||
- name: gitea
|
||||
nameOverride: gitea
|
||||
namespace: gitea
|
||||
chartDir: gitea
|
||||
valuesDir: gitea
|
||||
- name: cert-manager
|
||||
nameOverride: cert-manager
|
||||
namespace: cert-manager
|
||||
chartDir: cert-manager
|
||||
valuesDir: cert-manager
|
||||
# cert-manager's CRDs embed large OpenAPI schemas; SSA avoids the
|
||||
# 256KiB last-applied-configuration limit, same as external-secrets.
|
||||
serverSideApply: true
|
||||
@@ -132,6 +132,27 @@ appSpec:
|
||||
namespace: postgres
|
||||
chartDir: postgresql
|
||||
valuesDir: postgresql
|
||||
- name: redis
|
||||
# Backs toolshed's managed cache add-on — toolshed provisions a per-app
|
||||
# ACL user, scoped to its own key prefix, on request. Own namespace for
|
||||
# the same reason postgresql has one: addressed over cluster DNS like
|
||||
# any other platform component, outliving whatever consumes it:
|
||||
# redis.redis.svc.cluster.local:6379
|
||||
#
|
||||
# Hand-written chart, not Bitnami's, for the same reason as postgresql
|
||||
# (infra issue #4) — Redis ships no official chart either.
|
||||
#
|
||||
# Authentication is defined by an ACL file with no requirepass, which
|
||||
# is a security property rather than a preference: see the chart's own
|
||||
# values.yaml, where getting it wrong leaves the server open to
|
||||
# unauthenticated access after its first restart.
|
||||
#
|
||||
# Requires secretstores/toolshed-redis-credentials.yaml to have synced
|
||||
# first — the init container cannot seed the ACL file without it.
|
||||
nameOverride: redis
|
||||
namespace: redis
|
||||
chartDir: redis
|
||||
valuesDir: redis
|
||||
- name: victoria-metrics-single
|
||||
# Replaces the Prometheus server this entry briefly was (see git
|
||||
# history on this file) — same job, lower RAM/disk footprint for the
|
||||
|
||||
Reference in New Issue
Block a user