From 0625bfcd1c59af06b74cb2dbbfc55e87bd4d50b0 Mon Sep 17 00:00:00 2001 From: Jared Allard Date: Tue, 4 Feb 2025 18:52:55 -0800 Subject: [PATCH] feat: initial import from rgst-io/rgst --- argocd.libsonnet | 69 ++++++++++++++++++++++++ external-secrets.libsonnet | 99 ++++++++++++++++++++++++++++++++++ k.libsonnet | 107 +++++++++++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 argocd.libsonnet create mode 100644 external-secrets.libsonnet create mode 100644 k.libsonnet diff --git a/argocd.libsonnet b/argocd.libsonnet new file mode 100644 index 0000000..3bac62a --- /dev/null +++ b/argocd.libsonnet @@ -0,0 +1,69 @@ +// Copyright (C) 2024 Jared Allard +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +local k = import './k.libsonnet'; + +{ + Application(name, install_namespace=name, project='default'):: k._Object('argoproj.io/v1alpha1', 'Application', name, 'argocd') { + // For ease of accesing elsewhere + namespace:: install_namespace, + spec: { + project: project, + destination: { + [if install_namespace != null then 'namespace']: install_namespace, + server: 'https://kubernetes.default.svc', + }, + syncPolicy: { + syncOptions: [ + 'CreateNamespace=true', + ], + automated: { + prune: true, + selfHeal: true, + }, + }, + }, + }, + + HelmApplication(chart, repoURL, version, values={}, install_namespace=chart, release_name=null, app_name=null):: $.Application(name=if app_name == null then chart else app_name, install_namespace=install_namespace) { + spec+: { + source+: { + chart: chart, + repoURL: repoURL, + targetRevision: version, + helm: { + [if release_name != null then 'releaseName']: release_name, + values: std.manifestYamlDoc(values, true), + }, + }, + }, + }, + + JsonnetApplication(name, path=('./manifests/services/' + name), install_namespace=name, extVars=null):: $.Application(name=name, install_namespace=install_namespace) { + spec+: { + source+: { + directory: { + jsonnet: { + [if extVars != null then 'extVars']: [{ name: k, value: extVars[k] } for k in std.objectFields(extVars)], + }, + recurse: true, + }, + path: path, + repoURL: 'https://github.com/rgst-io/rgst', + targetRevision: 'HEAD', + }, + }, + }, +} diff --git a/external-secrets.libsonnet b/external-secrets.libsonnet new file mode 100644 index 0000000..64d2081 --- /dev/null +++ b/external-secrets.libsonnet @@ -0,0 +1,99 @@ +// Copyright (C) 2024 Jared Allard +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +local k = import './k.libsonnet'; + +{ + // SecretStoreRef is a reference to a secret store. + SecretStoreRef(secStore):: { + kind: secStore.kind, + name: secStore.metadata.name, + }, + + ExternalSecret(name, namespace):: k._Object('external-secrets.io/v1beta1', 'ExternalSecret', name, namespace) { + keys:: {}, + all_keys:: false, + assert std.length(self.keys) > 0 || self.all_keys : 'Either keys or all_keys must be set', + + secret_store:: {}, + assert self.secret_store != null : 'secret_store must be set', + + target:: '', + assert self.target != '' : 'target must be set', + + local this = self, + spec: { + secretStoreRef: $.SecretStoreRef(this.secret_store), + [if std.length(this.keys) > 0 then 'data']: k.mapToNamedList(this.keys, 'secretKey'), + [if this.all_keys then 'dataFrom']: [{ find: { name: { regexp: '.*' } } }], + target: { name: this.target }, + }, + }, + + SecretStore(name, namespace):: k._Object('external-secrets.io/v1beta1', 'SecretStore', name, namespace) { + local this = self, + doppler_:: { + secret: { + name: '', + namespace: '', + key: '', + }, + }, + + spec: { + provider: { + [if this.doppler_.secret.name != '' then 'doppler']: { + auth: { + secretRef: { + dopplerToken: { + name: this.doppler_.secret.name, + [if std.objectHas(this.doppler_.secret, 'namespace') then 'namespace']: this.doppler_.secret.namespace, + key: this.doppler_.secret.key, + }, + }, + }, + }, + }, + }, + }, + ClusterSecretStore(name):: $.SecretStore(name, '') { + kind: 'ClusterSecretStore', + metadata: std.mergePatch(super.metadata, { + namespace: null, + }), + }, + + DopplerSecretStore(name, project=name, namespace=name):: k.Container { + secret_store: $.SecretStore(name, namespace) { + doppler_:: { + secret: { + name: 'doppler', + key: 'token', + }, + }, + }, + external_secret: $.ExternalSecret('doppler', namespace) { + secret_store:: $.ClusterSecretStore('kubernetes'), + keys:: { + token: { + remoteRef: { + key: 'DOPPLER_TOKEN_%s' % std.asciiUpper(std.join('_', std.split(project, '-'))), + }, + }, + }, + target:: 'doppler', + }, + }, +} diff --git a/k.libsonnet b/k.libsonnet new file mode 100644 index 0000000..688255b --- /dev/null +++ b/k.libsonnet @@ -0,0 +1,107 @@ +// Copyright (C) 2024 Jared Allard +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Lightly based off of https://github.com/bitnami-labs/kube-libsonnet/blob/master/kube.libsonnet +{ + local containerKey = '_|type|_', + local containerValue = 'container', + + // getHiddenFieldValue returns a field only if it is hidden on an object + getHiddenFieldValue(o, k):: if std.get(o, k, inc_hidden=true) == null then + // We didn't find the field when including hidden fields, so return null. + null + else + // We found the key when we included hidden fields, so now check if it is + // also returned when we don't include hidden fields. + if std.get(o, k, default=null, inc_hidden=false) != null then + // The key was also returned when we didn't include hidden fields (not hidden), so return null. + null + else + // The key was not returned when we didn't include hidden fields, so return the value. + std.get(o, k, inc_hidden=true), + + // flattenMixedArrays flattens items in an array that are not the same type (e.g. objects + arrays) + // which std.flattenArrays() does not work on. + flattenMixedArrays(arrs):: std.foldl(function(a, b) if std.isArray(b) then a + b else a + [b], arrs, []), + + // Returns an array of each distinct key in the given object. If a object is a container + // it will return the keys of the container as well. See "Container". + objectValues(o):: $.flattenMixedArrays([ + // If we're an object, check if we have the container key + if std.isObject(v) then + // If we have the container key, run objectValues again on the object + // so that we include those as top level objects in the list. + if $.getHiddenFieldValue(v, containerKey) == containerValue then + $.objectValues(v) + else v + else v + for v in [o[k] for k in std.objectFields(o)] + ]), + + // Returns true if a value is not equal to null + isNotNull(v):: v != null, + + hyphenate(s):: std.join('-', std.split(s, '_')), + + // mapToNamedList takes a map of objects and returns a list of objects with + // the key as the name field. + mapToNamedList(o, nameKey='name'):: [{ [nameKey]: n } + o[n] for n in std.objectFields(o)], + + // envList takes a map of environment variables and returns a list of + // objects with the key as the name field and the value as the value + // field. + envList(map):: [ + if std.type(map[x]) == 'object' then { name: x, valueFrom: map[x] } else { name: x, value: map[x] } + for x in std.objectFields(map) + ], + + // List returns a list of Kubernetes Objects. Filters out null entries. + List():: $._Object('v1', 'List') { + items_:: {}, + // Filter out null objects + items: std.filter($.isNotNull, $.objectValues(self.items_)), + }, + + // Container is a container of objects. This is useful for creating sub-objects and + // having them also be included into a list created from an object (e.g. List()). This + // works anywhere objectValues() is used. + Container:: { + [containerKey]:: containerValue, + assert self[containerKey] == containerValue : 'Container "%s" field was mutated' % containerKey, + }, + + // Object creates a Kubernetes Object + _Object(apiVersion, kind, name=null, namespace=null):: { + apiVersion: apiVersion, + kind: kind, + // Only include metadata if name or namespace is set. + [if name != null || namespace != null then 'metadata']: { + [if name != null then 'name']: name, + [if namespace != null then 'namespace']: namespace, + }, + }, + + // ConfigMap creates a configmap with string guarantees if the data_ + // subfield is used. + ConfigMap(name, namespace):: $._Object('v1', 'ConfigMap', name, namespace) { + local this = self, + data_:: {}, + data: { + // ConfigMap keys must be strings. + [key]: std.toString(this.data_[key]) + for key in std.objectFields(this.data_) + }, + }, +}