Finish revising the resource creations to allow dynamic prop assignments to enable adjusting even the default behavior if needed

This commit is contained in:
2025-10-08 23:59:12 -05:00
parent 032421e4c5
commit 2ea3b31cd5

View File

@@ -11,6 +11,11 @@
(defn assoc-ins [m path-vals]
(reduce (fn [acc [path val]] (assoc-in acc path val)) m path-vals))
(defn deep-merge [a b]
(merge-with (fn [x y]
(if (map? y) (deep-merge x y) y))
a b))
(defn make-transformer
"Given f that takes {:app-name .. :secrets ..}, where :secrets is a plain map
(already unwrapped inside .apply), return a Helm transformer."
@@ -24,73 +29,81 @@
after (clj->js (assoc-ins base-values updates))]
after)))))
(defn create-secret [provider app-namespace app-name secrets-data]
(new (.. k8s -core -v1 -Secret) (str app-name "-secrets")
(clj->js {:metadata {:name (str app-name "-secrets")
:namespace app-namespace}
:stringData secrets-data})
(clj->js {:provider provider})))
(defn create-image [app-name]
(let [context-path (.. path (join "." (-> cfg :resource-path)))
dockerfile-path (.. path (join context-path (str app-name ".dockerfile")))]
(new (.. docker -Image) app-name
(clj->js {:build {:context context-path
:dockerfile dockerfile-path}
:imageName (str (-> cfg :docker-repo) "/" app-name ":latest")}))))
(defn create-ns [provider app-namespace]
(new (.. k8s -core -v1 -Namespace) app-namespace
(clj->js {:metadata {:name app-namespace}})
(clj->js {:provider provider})))
(defn create-chart [provider app-namespace app-name chart-repo helm-fn dependencies transformations ]
(new (.. k8s -helm -v3 -Chart) app-name
(clj->js {:chart app-name
:fetchOpts {:repo chart-repo}
:namespace app-namespace
:values helm-fn
:transformations (if transformations [transformations] [])})
(clj->js {:provider provider
(defn new-resource [resource-type resource-name final-args provider dependencies]
(new resource-type resource-name
(clj->js final-args)
(clj->js {:provider provider
:enableServerSideApply false
:dependsOn dependencies})))
:dependsOn dependencies})))
(defn create-deployment [provider app-namespace app-name app-labels image image-port dependencies]
(new (.. k8s -apps -v1 -Deployment) app-name
(clj->js {:metadata {:namespace app-namespace
:name app-name}
:spec {:selector {:matchLabels app-labels}
:replicas 1
:template {:metadata {:labels app-labels}
:spec {:containers
[{:name app-name
:image image
:ports [{:containerPort image-port}]}]}}}})
(clj->js {:provider provider
:dependsOn dependencies})))
(defn create-service [provider app-namespace app-name app-labels image-port dependencies]
(new (.. k8s -core -v1 -Service) app-name
(clj->js {:metadata {:namespace app-namespace
:name app-name}
:spec {:selector app-labels
:ports [{:port 80 :targetPort image-port}]}})
(clj->js {:provider provider :dependsOn dependencies})))
(defn create-secret [provider app-namespace app-name dependencies secret-options]
(let [base-args {:metadata {:name (str app-name "-secrets")
:namespace app-namespace}}
final-args (deep-merge base-args secret-options)]
(new-resource (.. k8s -core -v1 -Secret) app-name final-args provider dependencies)))
(defn create-ingress [provider app-namespace app-name full-snippet host image-port dependencies]
(new (.. k8s -networking -v1 -Ingress) app-name
(clj->js
{:metadata {:name app-name
:namespace app-namespace
:annotations {"caddy.ingress.kubernetes.io/snippet" full-snippet}}
:spec
{:ingressClassName "caddy"
:rules [{:host host
:http {:paths [{:path "/"
:pathType "Prefix"
:backend {:service {:name app-name
:port {:number image-port}}}}]}}]}})
(clj->js {:provider provider :dependsOn dependencies})))
(defn create-image [app-name image-options]
(let [context-path (.. path (join "." (-> cfg :resource-path)))
dockerfile-path (.. path (join context-path (str app-name ".dockerfile")))
base-args {:build {:context context-path
:dockerfile dockerfile-path}
:imageName (str (-> cfg :docker-repo) "/" app-name ":latest")}
final-args (deep-merge base-args image-options)]
(new (.. docker -Image) app-name (clj->js final-args))))
(defn create-namespace [provider app-namespace dependencies ns-options]
(let [base-args {:metadata {:name app-namespace}}
final-args (deep-merge base-args ns-options)]
(new-resource (.. k8s -core -v1 -Namespace) app-namespace final-args provider dependencies)))
(defn create-deployment [provider app-namespace app-name app-labels image-name image-port dependencies deployment-options]
(let [base-args {:metadata {:namespace app-namespace
:name app-name}
:spec {:selector {:matchLabels app-labels}
:replicas 1
:template {:metadata {:labels app-labels}
:spec {:containers
[{:name app-name
:image image-name
:ports [{:containerPort image-port}]}]}}}}
final-args (deep-merge base-args deployment-options)]
(new-resource (.. k8s -apps -v1 -Deployment) app-name final-args provider dependencies)))
(defn create-service [provider app-namespace app-name app-labels image-port dependencies service-options]
(let [base-args {:metadata {:namespace app-namespace
:name app-name}
:spec {:selector app-labels
:ports [{:port 80 :targetPort image-port}]}}
final-args (deep-merge base-args service-options)]
(new-resource (.. k8s -core -v1 -Service) app-name final-args provider dependencies)))
(defn create-chart [provider app-namespace app-name dependencies chart-options]
(let [base-args {:chart app-name
:namespace app-namespace}
final-args (deep-merge base-args chart-options)]
(new-resource (.. k8s -helm -v3 -Chart) app-name final-args provider dependencies)))
(defn create-ingress [provider app-namespace app-name host image-port dependencies ingress-options]
(let [base-args {:metadata {:name app-name
:namespace app-namespace
:annotations {"caddy.ingress.kubernetes.io/snippet"
(str "tls {\n dns cloudflare {env.CLOUDFLARE_API_TOKEN}\n}\n" (:caddy-snippet ingress-options))}}
:spec
{:ingressClassName "caddy"
:rules [{:host host
:http {:paths [{:path "/"
:pathType "Prefix"
:backend {:service {:name app-name
:port {:number image-port}}}}]}}]}
}
final-args (deep-merge base-args ingress-options)]
(new-resource (.. k8s -networking -v1 -Ingress) app-name final-args provider dependencies)))
(defn create-storage-class [provider app-name dependencies storage-options]
(let [base-args {:metadata {:name app-name}}
final-args (deep-merge base-args storage-options)]
(new-resource (.. k8s -storage -v1 -StorageClass) app-name final-args provider dependencies)))
(defn deploy-stack
"Deploys a versatile stack of K8s resources, including optional Helm charts."
@@ -98,12 +111,11 @@
(let [[component-kws [options]] (split-with keyword? args)
requested-components (set component-kws)
{:keys [provider vault-provider pulumi-cfg app-namespace app-name image image-port caddy-snippet vault-load-yaml chart-repo transformations exec-fn helm-values-fn]
:or {vault-load-yaml false image-port 80 caddy-snippet "" helm-values-fn #(:base-values %) transformations nil}} options
{:keys [provider vault-provider pulumi-cfg app-namespace app-name image image-port vault-load-yaml exec-fn
storage-class-opts secret-opts ns-opts image-opts ingress-opts service-opts deployment-opts chart-opts]
:or {vault-load-yaml false image-port 80}} options
app-labels {:app app-name}
full-snippet (str "tls {\n dns cloudflare {env.CLOUDFLARE_API_TOKEN}\n}\n" caddy-snippet)
prepared-vault-data (when (requested-components :vault-secrets)
(vault-utils/prepare {:provider provider
:vault-provider vault-provider
@@ -113,38 +125,47 @@
{:keys [secrets yaml-values bind-secrets]} (or prepared-vault-data {:secrets nil :yaml-values nil :bind-secrets nil})
helm-fn (helm-values-fn {:base-values yaml-values
:secrets (if (some? prepared-vault-data) secrets nil)
:app-name app-name})
host (when secrets (.apply secrets #(aget % "host")))
ns (when (requested-components :namespace)
(create-ns provider app-namespace))
(create-namespace provider app-namespace nil ns-opts))
docker-image (when (requested-components :docker-image)
(create-image app-name))
(create-image app-name image-opts))
image (if (some? docker-image) (str (-> cfg :docker-repo) "/" app-name ":latest") image)
secret (when (requested-components :secret)
(create-secret provider app-namespace app-name nil secret-opts))
storage-class (when (requested-components :storage-class)
(create-storage-class provider app-name nil storage-class-opts))
image (if (some? image) image (str (-> cfg :docker-repo) "/" app-name ":latest"))
chart (when (requested-components :chart)
(create-chart provider app-namespace app-name chart-repo helm-fn (vec (filter some? [ns docker-image bind-secrets])) transformations))
(create-chart provider app-namespace app-name (vec (filter some? [ns docker-image bind-secrets]))
(let [helm-values-fn (get chart-opts :helm-values-fn (fn [ctx] (:base-values ctx)))
context {:base-values yaml-values
:secrets (if (some? prepared-vault-data) secrets nil)
:app-name app-name}
calculated-values (helm-values-fn context)
transformations-fn (if (get chart-opts :transformations) [(get chart-opts :transformations)] [])]
(-> chart-opts
(assoc :values calculated-values)
(assoc :transformations transformations-fn)))))
deployment (when (requested-components :deployment)
(create-deployment provider app-namespace app-name app-labels image image-port [docker-image]))
(create-deployment provider app-namespace app-name app-labels image image-port [docker-image] deployment-opts))
service (when (requested-components :service)
(create-service provider app-namespace app-name app-labels image-port [deployment]))
(create-service provider app-namespace app-name app-labels image-port [deployment] service-opts))
app-dependency (or service chart bind-secrets)
ingress (when (requested-components :ingress) (create-ingress provider app-namespace app-name full-snippet host image-port [app-dependency]))
ingress (when (requested-components :ingress) (create-ingress provider app-namespace app-name host image-port [app-dependency] ingress-opts))
execute (when (requested-components :execute)
(exec-fn (assoc options :dependencies (vec (filter some? [chart ns deployment service ingress docker-image])))))]
(exec-fn (assoc options :dependencies (vec (filter some? [chart ns secret storage-class deployment service ingress docker-image])))))]
{:namespace ns, :vault-secrets prepared-vault-data, :chart chart, :deployment deployment, :service service, :ingress ingress :execute execute}))
{:namespace ns, :vault-secrets prepared-vault-data, :secret secret, :docker-image docker-image :storage-class storage-class, :chart chart, :deployment deployment, :service service, :ingress ingress :execute execute}))