Dagger
Search

argocd

Argo CD on Kubernetes, including KCL-based cluster registration
(clusterbook) rendering.

Installation

dagger install github.com/stuttgart-things/blueprints/argocd@v2.4.1

Entrypoint

Return Type
Argocd
Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
func (m *MyModule) Example() *dagger.Argocd  {
	return dag.
			Argocd()
}
@function
def example() -> dagger.Argocd:
	return (
		dag.argocd()
	)
@func()
example(): Argocd {
	return dag
		.argocd()
}

Types

Argocd 🔗

applyConfig() 🔗

ApplyConfig applies a rendered config file to the cluster. The target namespace is created (server-side, kubectl apply on a Namespace doc) before the manifests are applied.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
configFileFile !-

Rendered config file (from render-clusterbook-cluster-config or local YAML)

kubeConfigSecret !-

Kubeconfig secret for cluster access

namespaceString "argocd"

Target namespace

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 apply-config --config-file file:path --kube-config env:MYSECRET
func (m *MyModule) Example(ctx context.Context, configFile *dagger.File, kubeConfig *dagger.Secret) string  {
	return dag.
			Argocd().
			ApplyConfig(ctx, configFile, kubeConfig)
}
@function
async def example(config_file: dagger.File, kube_config: dagger.Secret) -> str:
	return await (
		dag.argocd()
		.apply_config(config_file, kube_config)
	)
@func()
async example(configFile: File, kubeConfig: Secret): Promise<string> {
	return dag
		.argocd()
		.applyConfig(configFile, kubeConfig)
}

bootstrapClusterbookCluster() 🔗

BootstrapClusterbookCluster orchestrates the full cluster-registration workflow: render the clusterbook config, optionally apply it to a cluster (–deploy), and optionally commit it to a Git repo with optional PR and merge (–commit-to-git).

Returns the rendered file so callers can also export --path=... it.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
nameString -

Cluster name — required unless valuesFile is provided

networkKeyString -

/24 network key — required unless valuesFile is provided

valuesFileFile -

YAML/JSON values file (KCL –parametersFile)

ociSourceString "ghcr.io/stuttgart-things/clusterbook-cluster-gen:0.3.0"

OCI KCL module source

clusterNameString -

Argo CD-side cluster name

createDnsBoolean -No description provided
preserveKubeconfigServerBoolean -No description provided
releaseOnDeleteBoolean -No description provided
kubeconfigSecretNameString -No description provided
kubeconfigSecretNamespaceString -No description provided
argocdNamespaceString -No description provided
providerConfigNameString -No description provided
clusterLabelsString -

JSON object literal, e.g. {“env”:“lab”}

entrypointString "main.k"No description provided
deployBoolean false

Apply the rendered config to a cluster

kubeConfigSecret -

Kubeconfig secret — required when deploy=true or detect-network-key=true

deployNamespaceString "argocd"

Target namespace for apply

detectNetworkKeyBoolean false

Run kubectl get nodes -o json against –kube-config and use the dominant /24 InternalIP prefix as –network-key. Only fires when –network-key is empty.

renderKubeconfigSecretBoolean false

Render a v1/Secret wrapping a SOPS-encrypted kubeconfig source file; applied alongside the cluster config on –deploy=true and committed alongside it on –commit-to-git=true.

kubeconfigSourceFileFile -

SOPS-encrypted kubeconfig source — required when render-kubeconfig-secret=true // pragma: allowlist secret

sopsKeySecret -

AGE private key for decrypting kubeconfig-source-file

agePublicKeySecret -

AGE public key for re-encrypting the rendered Secret — required when render-kubeconfig-secret=true and commit-to-git=true // pragma: allowlist secret

sopsConfigFileFile -

SOPS config file (.sops.yaml) used during re-encryption

kubeconfigDataKeyString "kubeconfig"

Data key under data: in the rendered Secret

kubeconfigFileNameString "kubeconfig.yaml"

File name to use when committing the kubeconfig Secret (joined with destination-path)

commitToGitBoolean false

Commit the rendered config to a Git repository

repositoryString -

Repository in “owner/repo” — required when commitToGit=true

gitTokenSecret -

GitHub token — required when commitToGit=true

branchNameString "main"

Branch to commit to

destinationPathString "argocd/clusters/"

Destination folder within the repository

fileNameString "cluster.yaml"

File name to write under destinationPath

commitMessageString "Add Argo CD cluster registration"

Commit message

createPrBoolean false

Open a PR from branchName into baseBranch

baseBranchString "main"

PR base branch

prTitleString -No description provided
prBodyString -No description provided
mergePrBoolean false

Auto-merge the PR after creation

mergeMethodString "squash"

squash | merge | rebase

cacheBusterString -

Arbitrary string mixed into the function-level cache key. Pass a timestamp (e.g. date +%s%N) from CI to force a fresh execution when the function’s other args are deterministic but the side effects (git push, kubectl apply) must replay. Inner CACHE_BUSTER env vars on individual containers don’t help here — Dagger short- circuits the whole function on input-hash match before any container in the body is instantiated.

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 bootstrap-clusterbook-cluster
func (m *MyModule) Example() *dagger.File  {
	return dag.
			Argocd().
			BootstrapClusterbookCluster()
}
@function
def example() -> dagger.File:
	return (
		dag.argocd()
		.bootstrap_clusterbook_cluster()
	)
@func()
example(): File {
	return dag
		.argocd()
		.bootstrapClusterbookCluster()
}

commitConfig() 🔗

CommitConfig commits configFile to / on in . Optionally opens a pull request against and optionally merges that PR.

Returns a multi-line summary of what was committed / opened / merged.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
configFileFile !-

Rendered config file to commit

repositoryString !-

Repository in “owner/repo” format

gitTokenSecret !-

GitHub token for git operations

branchNameString "main"

Branch to commit the file to

destinationPathString "argocd/clusters/"

Destination folder within the repository (without leading slash)

fileNameString "cluster.yaml"

File name to write under destinationPath (used as the committed basename)

commitMessageString "Add Argo CD cluster registration"

Commit message

createPrBoolean false

Open a pull request from branchName into baseBranch

baseBranchString "main"

Base branch for the PR

prTitleString -

PR title (defaults to commitMessage when empty)

prBodyString -

PR body

mergePrBoolean false

Merge the PR after creation

mergeMethodString "squash"

Merge method: “squash”, “merge”, or “rebase”

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 commit-config --config-file file:path --repository string --git-token env:MYSECRET
func (m *MyModule) Example(ctx context.Context, configFile *dagger.File, repository string, gitToken *dagger.Secret) string  {
	return dag.
			Argocd().
			CommitConfig(ctx, configFile, repository, gitToken)
}
@function
async def example(config_file: dagger.File, repository: str, git_token: dagger.Secret) -> str:
	return await (
		dag.argocd()
		.commit_config(config_file, repository, git_token)
	)
@func()
async example(configFile: File, repository: string, gitToken: Secret): Promise<string> {
	return dag
		.argocd()
		.commitConfig(configFile, repository, gitToken)
}

commitFiles() 🔗

CommitFiles commits every file in sourceDir into on in as a single commit / single push. Optionally opens a pull request against and optionally merges it.

Distinct from CommitConfig in two ways:

  1. It accepts a Directory (multiple files) so callers can push e.g. cluster.yaml + kubeconfig.yaml in one commit. Two back-to-back CommitConfig calls (one file each) hit a Dagger CloneGithub cache race — the second clone is served from cache and pre-dates the first push, so git push is rejected as non-fast-forward. Bundling into one commit avoids it entirely.
  2. destinationFolder is a folder (without filename); each file in sourceDir is placed under that folder. Filenames in the source directory are preserved verbatim.

Returns a multi-line summary of what was committed / opened / merged.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
sourceDirDirectory !-

Source directory whose files (top level) should be committed

repositoryString !-

Repository in “owner/repo” format

gitTokenSecret !-

GitHub token for git operations

branchNameString "main"

Branch to commit the files to

destinationFolderString "argocd/clusters/"

Destination folder within the repository (without leading slash)

commitMessageString "Add Argo CD cluster registration"

Commit message

createPrBoolean false

Open a pull request from branchName into baseBranch

baseBranchString "main"

Base branch for the PR

prTitleString -

PR title (defaults to commitMessage when empty)

prBodyString -

PR body

mergePrBoolean false

Merge the PR after creation

mergeMethodString "squash"

Merge method: “squash”, “merge”, or “rebase”

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 commit-files --source-dir DIR_PATH --repository string --git-token env:MYSECRET
func (m *MyModule) Example(ctx context.Context, sourceDir *dagger.Directory, repository string, gitToken *dagger.Secret) string  {
	return dag.
			Argocd().
			CommitFiles(ctx, sourceDir, repository, gitToken)
}
@function
async def example(source_dir: dagger.Directory, repository: str, git_token: dagger.Secret) -> str:
	return await (
		dag.argocd()
		.commit_files(source_dir, repository, git_token)
	)
@func()
async example(sourceDir: Directory, repository: string, gitToken: Secret): Promise<string> {
	return dag
		.argocd()
		.commitFiles(sourceDir, repository, gitToken)
}

createVaultIssuer() 🔗

CreateVaultIssuer prepares the cluster-side prerequisites cert-manager needs to use a remote Vault PKI as a ClusterIssuer. It does NOT create the ClusterIssuer itself — that’s the cert-manager-vault-pki AppSet’s job, which references the two Secrets this function lands on the target cluster.

What it does (in one Dagger session): 1. Decrypts the vault env yaml. 2. Talks to Vault directly (HTTP API) to upsert an ACL policy a renewable token + read the PKI CA. (No Terraform.) 3. Decrypts the target cluster’s kubeconfig. 4. Applies a 3-document YAML directly to the target cluster: - Namespace/<target-namespace> (default cert-manager) - Secret/<token-secret-name> (default cert-manager-vault-token, data.token = freshly-minted vault token) - Secret/<ca-secret-name> (default vault-pki-ca, data["ca.crt"] = live-fetched PKI root CA PEM)

Only plain core/v1 resources are created; no cert-manager CRDs, no admission-webhook coupling. The AppSet that creates the ClusterIssuer runs whenever it’s ready — order-independent.

Closes #162.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
clusterNameString !-

Target cluster name; used in the Vault token’s display_name.

kubeconfigSourceFileFile !-

SOPS-encrypted kubeconfig of the target cluster.

vaultEnvFileFile !-

SOPS-encrypted KV-YAML with vaultAddr, vaultToken, optional vaultSkipVerify (defaults to true), and optional vaultCaBundle (base64-encoded PKI root CA PEM; when set, the function uses it directly instead of live-fetching from ${vaultAddr}/v1/pki/ca/pem).

sopsKeySecret !-

AGE private key for decrypting both files.

pkiRoleString "sthings-vsphere"

Vault PKI role name (matches the Vault PKI mount’s pre-existing role; the rendered ClusterIssuer references this role).

policyNameString "pki-issue"

Vault ACL policy name to upsert.

targetNamespaceString "cert-manager"

Namespace to create + place the cert-manager Secrets in.

tokenSecretNameString "cert-manager-vault-token"

Name of the Secret containing the Vault token.

caSecretNameString "vault-pki-ca"

Name of the Secret containing the PKI CA bundle.

tokenTtlString "8760h"

TTL for the minted Vault token. Renewable.

cacheBusterString -

Arbitrary string mixed into the function-level cache key. Pass a timestamp (e.g. date +%s%N) from CI to force a fresh execution when the function’s other args are deterministic but the side effects (Vault token mint, kubectl apply) must replay. Inner CACHE_BUSTER env vars on individual containers don’t help here — Dagger short-circuits the whole function on input-hash match before any container in the body is instantiated.

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 create-vault-issuer --cluster-name string --kubeconfig-source-file file:path --vault-env-file file:path --sops-key env:MYSECRET
func (m *MyModule) Example(ctx context.Context, clusterName string, kubeconfigSourceFile *dagger.File, vaultEnvFile *dagger.File, sopsKey *dagger.Secret) string  {
	return dag.
			Argocd().
			CreateVaultIssuer(ctx, clusterName, kubeconfigSourceFile, vaultEnvFile, sopsKey)
}
@function
async def example(cluster_name: str, kubeconfig_source_file: dagger.File, vault_env_file: dagger.File, sops_key: dagger.Secret) -> str:
	return await (
		dag.argocd()
		.create_vault_issuer(cluster_name, kubeconfig_source_file, vault_env_file, sops_key)
	)
@func()
async example(clusterName: string, kubeconfigSourceFile: File, vaultEnvFile: File, sopsKey: Secret): Promise<string> {
	return dag
		.argocd()
		.createVaultIssuer(clusterName, kubeconfigSourceFile, vaultEnvFile, sopsKey)
}

createVaultKubernetesAuth() 🔗

CreateVaultKubernetesAuth provisions the cluster-side prerequisites and the Vault-side Kubernetes auth backend an in-cluster ServiceAccount uses to authenticate to Vault and consume one or more pre-existing policies. Mirrors the layout in CreateVaultIssuer: decrypt SOPS inputs → kubectl apply core/v1 resources → drive the Vault HTTP API directly. No Terraform, no Helm.

What it creates (in one Dagger session):

  • On the target cluster: Namespace/<namespace>, ServiceAccount/<auth-name>, non-expiring SA-token Secret/<auth-name> (type kubernetes.io/service-account-token), ClusterRoleBinding/<auth-name>system:auth-delegator (so Vault can call TokenReview with the SA’s JWT).

  • On Vault: a Kubernetes auth backend mounted at <cluster-name>-<auth-name>, configured with the kubeconfig’s API server + the SA’s CA + reviewer JWT (disable_iss_validation=true, disable_local_ca_jwt=true — matches the vault-base-setup Terraform module so newer K8s won’t reject the validation), and one auth role bound to the SA with the given token policies.

Policies are NOT created by this function — they must already exist in Vault (typically owned by a separate per-KV-mount pipeline like vault-homerun2-secrets). Pass --token-policies as a comma-separated list; one cluster can bind to one policy, multiple policies, or share a policy with other clusters.

Idempotent: re-runs upsert the config + role and skip the auth-mount step when the path is already in use.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
clusterNameString !-

Target cluster name; prefixes the Vault auth backend path (<cluster-name>-<auth-name>).

kubeconfigSourceFileFile !-

SOPS-encrypted kubeconfig of the target cluster.

vaultEnvFileFile !-

SOPS-encrypted env YAML with vaultAddr, vaultToken, optional vaultSkipVerify (defaults true). Same shape as create-vault-issuer.

sopsKeySecret !-

AGE private key for decrypting both files.

authNameString "eso"

Auth backend + role name. The Vault mount path becomes <cluster-name>-<auth-name>. The SA, the SA-token Secret, the CRB and the role on the Vault side all reuse this name.

namespaceString "external-secrets"

Namespace on the target cluster for the SA + SA-token Secret.

tokenPoliciesString "read-homerun2-pr"

Comma-separated Vault policies to bind to the role. Must already exist in Vault. Per-cluster — each caller supplies its own list.

tokenTtlString "3600"

TTL (seconds) for tokens minted via this role.

cacheBusterString -

Cache buster — pass a timestamp/run-id from CI to force a fresh execution. Same reason as CreateVaultIssuer: Dagger short-circuits the whole function on input-hash match before any container in the body is instantiated.

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 create-vault-kubernetes-auth --cluster-name string --kubeconfig-source-file file:path --vault-env-file file:path --sops-key env:MYSECRET
func (m *MyModule) Example(ctx context.Context, clusterName string, kubeconfigSourceFile *dagger.File, vaultEnvFile *dagger.File, sopsKey *dagger.Secret) string  {
	return dag.
			Argocd().
			CreateVaultKubernetesAuth(ctx, clusterName, kubeconfigSourceFile, vaultEnvFile, sopsKey)
}
@function
async def example(cluster_name: str, kubeconfig_source_file: dagger.File, vault_env_file: dagger.File, sops_key: dagger.Secret) -> str:
	return await (
		dag.argocd()
		.create_vault_kubernetes_auth(cluster_name, kubeconfig_source_file, vault_env_file, sops_key)
	)
@func()
async example(clusterName: string, kubeconfigSourceFile: File, vaultEnvFile: File, sopsKey: Secret): Promise<string> {
	return dag
		.argocd()
		.createVaultKubernetesAuth(clusterName, kubeconfigSourceFile, vaultEnvFile, sopsKey)
}

detectNetworkKey() 🔗

DetectNetworkKey runs kubectl get nodes -o json against the target cluster and returns the /24 prefix shared by the nodes’ InternalIP addresses (e.g. “10.31.102”). This is the format expected by render-clusterbook-cluster-config / bootstrap-clusterbook-cluster as –network-key.

When nodes span multiple /24 subnets (rare but possible with mixed pools), the most frequently observed prefix wins. IPv6 addresses are ignored.

Return Type
String !
Arguments
NameTypeDefault ValueDescription
kubeConfigSecret !-

Kubeconfig secret for cluster access

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 detect-network-key --kube-config env:MYSECRET
func (m *MyModule) Example(ctx context.Context, kubeConfig *dagger.Secret) string  {
	return dag.
			Argocd().
			DetectNetworkKey(ctx, kubeConfig)
}
@function
async def example(kube_config: dagger.Secret) -> str:
	return await (
		dag.argocd()
		.detect_network_key(kube_config)
	)
@func()
async example(kubeConfig: Secret): Promise<string> {
	return dag
		.argocd()
		.detectNetworkKey(kubeConfig)
}

renderClusterbookClusterConfig() 🔗

RenderClusterbookClusterConfig renders the clusterbook-cluster-gen KCL module, which produces the manifests needed to register a cluster with Argo CD (kubeconfig Secret reference, provider config, cluster labels, optional DNS).

Values can come from a YAML/JSON file (–values-file) and/or individual CLI flags. When both are provided, CLI flags override matching keys in the file (KCL –parameters takes precedence over –parametersFile).

Returns the rendered multi-document YAML as a Dagger File.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
nameString -

Cluster name (KCL -D name) — required unless valuesFile is provided

networkKeyString -

/24 network key, e.g. 10.31.101 — required unless valuesFile is provided

valuesFileFile -

YAML/JSON file with KCL parameters (passed as –parametersFile); CLI flags below override matching keys

ociSourceString "ghcr.io/stuttgart-things/clusterbook-cluster-gen:0.3.0"

OCI KCL module source

clusterNameString -

Argo CD-side cluster name; defaults to name when neither is set via file

createDnsBoolean -

Create a DNS record for the cluster (default true when no values file)

preserveKubeconfigServerBoolean -

Preserve the existing server field from the kubeconfig Secret (default true when no values file)

releaseOnDeleteBoolean -

Release the Argo CD cluster Secret on resource delete (default true when no values file)

kubeconfigSecretNameString -

Name of the Secret holding the cluster kubeconfig; defaults to name when no values file

kubeconfigSecretNamespaceString -

Namespace of the kubeconfig Secret (default “argocd” when no values file)

argocdNamespaceString -

Argo CD namespace (default “argocd” when no values file)

providerConfigNameString -

Crossplane ProviderConfig name (default “default” when no values file)

clusterLabelsString -

Cluster labels as a JSON object literal, e.g. {“env”:“lab”,“role”:“mgmt”}

entrypointString "main.k"

KCL entrypoint file

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 render-clusterbook-cluster-config
func (m *MyModule) Example() *dagger.File  {
	return dag.
			Argocd().
			RenderClusterbookClusterConfig()
}
@function
def example() -> dagger.File:
	return (
		dag.argocd()
		.render_clusterbook_cluster_config()
	)
@func()
example(): File {
	return dag
		.argocd()
		.renderClusterbookClusterConfig()
}

renderKubeconfigSecret() 🔗

RenderKubeconfigSecret turns a SOPS-encrypted source file (typically a cluster kubeconfig under stuttgart-things/secrets/kubeconfigs/) into a Kubernetes v1/Secret manifest. Equivalent to:

sops --decrypt <sourceFile> > kubeconfig.yaml
kubectl create secret generic <name> -n <namespace> \
  --from-file=<dataKey>=kubeconfig.yaml \
  --dry-run=client -o yaml

By default the rendered Secret is re-encrypted with SOPS using agePublicKey so it can be safely committed to git (e.g. via commit-config). Pass –encrypt=false to get the plaintext manifest for direct kubectl apply (e.g. via apply-config).

Returns the manifest as a Dagger File so it composes with apply-config and commit-config without shell round-trips.

Return Type
File !
Arguments
NameTypeDefault ValueDescription
sourceFileFile !-

SOPS-encrypted source file (e.g. kubeconfigs/kind-dev-test1.yaml)

sopsKeySecret !-

AGE private key for decrypting sourceFile (AGE-SECRET-KEY-…)

nameString !-

Secret name (e.g. “kind-dev-test1” or “target-cluster-kubeconfig”)

namespaceString "argocd"

Target namespace

dataKeyString "kubeconfig"

Data key under data: in the rendered Secret (data.)

encryptBoolean true

Re-encrypt the rendered Secret with SOPS using agePublicKey

agePublicKeySecret -

AGE public key for SOPS re-encryption (required when encrypt=true)

sopsConfigFile -

SOPS config file (.sops.yaml) used during re-encryption

Example
dagger -m github.com/stuttgart-things/blueprints/argocd@d971d0368adf4058aba3760308b6cd89db92068f call \
 render-kubeconfig-secret --source-file file:path --sops-key env:MYSECRET --name string
func (m *MyModule) Example(sourceFile *dagger.File, sopsKey *dagger.Secret, name string) *dagger.File  {
	return dag.
			Argocd().
			RenderKubeconfigSecret(sourceFile, sopsKey, name)
}
@function
def example(source_file: dagger.File, sops_key: dagger.Secret, name: str) -> dagger.File:
	return (
		dag.argocd()
		.render_kubeconfig_secret(source_file, sops_key, name)
	)
@func()
example(sourceFile: File, sopsKey: Secret, name: string): File {
	return dag
		.argocd()
		.renderKubeconfigSecret(sourceFile, sopsKey, name)
}