From 42cb0948d2839be61c0cbe6f355c34aa2c5d0357 Mon Sep 17 00:00:00 2001 From: michaeljguarino Date: Tue, 16 Jul 2024 11:59:28 -0400 Subject: [PATCH] Document Service Templating more Clearly, move to more prominent location This is a workflow that definitely needs documentation, but also noticed there's a lot of doc rearrangement we could do so added that. --- pages/deployments/templating.md | 117 +++++++++++++++++++------------ src/NavData.tsx | 118 +++++++++++++------------------- 2 files changed, 122 insertions(+), 113 deletions(-) diff --git a/pages/deployments/templating.md b/pages/deployments/templating.md index 47742a48..72f1baf1 100644 --- a/pages/deployments/templating.md +++ b/pages/deployments/templating.md @@ -1,35 +1,27 @@ --- title: Service Templating -description: How to deploy and configure services in Plural +description: Applying On-The-Fly Configuration to Your Plural Services --- # Overview Plural allows a number of different mechanism to template service configuration into your yaml before applying to the destination cluster. There are a few key usecases for this: -- injecting cluster-level configuration into a service, like IRSA role ARNS or other information needed to configure authentication to cloud services +- injecting cluster-level configuration into a service, like IRSA role ARNs or other information needed to configure authentication to cloud services - injecting Plural secrets into service manifests -- injecting information from other tools passed from service contexts +- injecting information from other tools passed from service contexts or Stack outputs All templating is done via Shopify's [liquid](https://shopify.github.io/liquid/) template engine. It's a mature templating language with generally better documentation than go's native text/template. We also inject a subset of the Sprig library into the engine, you can see how it's configured [here](https://github.com/pluralsh/deployment-operator/blob/main/pkg/manifests/template/raw.go#L22). It's important to know where templates can be applied and what data is available. -## Templatable files and Disabling templating +## Templatable files The following files can have liquid templating applied to them: -- yaml files in raw (non-helm/kustomize/etc) services +- yaml files in raw (non-helm/kustomize/etc) services with a `.liquid` extension - helm values files ending in `.liquid` -If you don't want to apply templating at all, which is usually only necessary if your yaml has template strings embedded w/in them, you can set: - -```yaml -spec: - templated: false -``` - -on your ServiceDeployment spec to tell the deployment agent to bypass all templating on that service. ## Available Data @@ -37,6 +29,7 @@ You will have the following data fields available for templating: - `configuration` - a `map[string]string` which contains any secrets configured for that service - `cluster` - a stripped down struct containing metadata about a cluster +- `imports` - imported data from Plural Stacks - `contexts` - a `map[string]map[string]interface{}` containing a map of maps, keyed on context name. The contexts are usually created in other tools like terraform, and can be bound to a service by name. See our [docs](/deployments/terraform-interop) on terraform interoperability to learn more You can access them using `{{ }}`. As an example, if you wanted to template a service secret into a kubernetes secret, it might look something like: @@ -45,37 +38,75 @@ You can access them using `{{ }}`. As an example, if you wanted to template a se apiVersion: v1 kind: Secret stringData: - MY_SECRET: { { configuration.secret } } + MY_SECRET: {{ configuration.secret }} ``` -## Safeguarding Sensitive Configurations in Terraform - -In some cases you might want to reserve secrets for manual input in the Plural Console, yet configure others in the Terraform definition of your service. -This example demonstrates the exclusion of certain configuration secrets, such as passwords and usernames, allowing manual entry exclusively within the Plural Console by leveraging Terraform's `ignore_changes` feature. - -```tf -resource "plural_service_deployment" "monitoring" { - name = "monitoring" - namespace = "monitoring" - repository = {...} - cluster = { - id = "cluster-id" - } - - configuration = { - monitoringRepo = plural_git_repository.monitoring.id - repoUrl = local.repo_url - namespace = kubernetes_namespace.monitoring.metadata[0].name - } - - # enter these secrets in the service UI safely without risking the next `terraform apply` overwriting them - lifecycle { - ignore_changes = [ - configuration["basicAuthPassword"], - configuration["basicAuthUser"], - ] - } -} +## Available Functions + +Our code is open source, so you can reference the code to find the deinitive function list we support is defined here: https://github.com/pluralsh/polly/blob/main/template/liquid.go#L35 + +That said there are a few good to know functions: + +* `b64enc` - encodes a string as base64 +* `b64dec` - decodes a string from base64 +* `to_json` - converts a map to json +* `from_json` - parses a json map +* `default` - uses a default value if the first argument is falsy, used like `{{ configuration.cluster.metadata.role_arn | default: "dummy-arn" }}` +* `ternary` - returns one of two values depending on whether a condition is truthy + +Additionally, we support virtually all the functions in the Sprig library used by helm. See the full documentation [here](https://masterminds.github.io/sprig/). + +## End-to-End Example + +To get an idea of how this might work with a service secret, say we've already added the secret data to the management cluster like so: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: example-api-secrets + namespace: infra +stringData: + api-key: example-api-key ``` -In this example, sensitive configurations like `basicAuthUser` and `basicAuthPassword` are excluded from Terraform's lifecycle management using the `ignore_changes` parameter. +We then want to install a helm chart that needs that api key as a helm value, that would be done with our `ServiceDeployment` CRD, like so: + +```yaml +apiVersion: deployments.plural.sh/v1alpha1 +kind: ServiceDeployment +metadata: + name: example + namespace: infra +spec: + namespace: example + git: + folder: helm-values + ref: main + configurationRef: + name: example-api-secrets # <- points to secret above + namespace: infra # <- needs to be in the same namespace as the CR since we need to set a k8s owner reference + repositoryRef: + kind: GitRepository + name: infra + namespace: infra + helm: + version: "0.x.x" + chart: example-chart + valuesFiles: + - example-chart.yaml.liquid # <- enable liquid templating in this values file + repository: + namespace: infra + name: example-repo + clusterRef: + kind: Cluster + name: my-cluster + namespace: infra +``` + +Then at `helm-values/example-chart.yaml.liquid` (must be at that path since `spec.git.folder` is pointing to the `helm-values` folder), we'd just add a values file sort of like this: + +```yaml +example: + apiKey: {{ configuration.api-key }} # the keys from the secret are available in the `configuration` map here +``` \ No newline at end of file diff --git a/src/NavData.tsx b/src/NavData.tsx index 9fff28a5..f687b93a 100644 --- a/src/NavData.tsx +++ b/src/NavData.tsx @@ -130,36 +130,36 @@ const rootNavData: NavMenu = deepFreeze([ }, ], }, - { - href: '/deployments/clusters', - title: 'Cluster Management', - sections: [ - { - title: 'Import An Existing Cluster', - href: '/deployments/import-cluster', - }, - { - title: 'Create Provider Credentials', - href: '/deployments/credentials', - }, - { - href: '/deployments/cluster-create', - title: 'Create Cluster API Workload Clusters', - }, - // { - // href: '/deployments/cluster-config', - // title: 'Configure my Cluster', - // }, - { - title: 'Destroy the Cluster Safely', - href: '/deployments/cluster-destroy', - }, - { - title: 'Optimize Cluster Costs', - href: '/deployments/cluster-cost', - }, - ], - }, + // { + // href: '/deployments/clusters', + // title: 'Cluster Management', + // sections: [ + // { + // title: 'Import An Existing Cluster', + // href: '/deployments/import-cluster', + // }, + // { + // title: 'Create Provider Credentials', + // href: '/deployments/credentials', + // }, + // { + // href: '/deployments/cluster-create', + // title: 'Create Cluster API Workload Clusters', + // }, + // // { + // // href: '/deployments/cluster-config', + // // title: 'Configure my Cluster', + // // }, + // { + // title: 'Destroy the Cluster Safely', + // href: '/deployments/cluster-destroy', + // }, + // { + // title: 'Optimize Cluster Costs', + // href: '/deployments/cluster-cost', + // }, + // ], + // }, { href: '/stacks/', title: 'Iac Management with Stacks', @@ -198,10 +198,6 @@ const rootNavData: NavMenu = deepFreeze([ href: '/deployments/services-deploy', title: 'Deploy Services', }, - { - href: '/deployments/templating', - title: 'Templating Variable Configuration', - }, { href: '/deployments/helm', title: 'Helm Repository Sources', @@ -216,10 +212,6 @@ const rootNavData: NavMenu = deepFreeze([ }, ], }, - { - href: '/deployments/multi-tenancy', - title: 'Projects and Multi-Tenancy', - }, { href: '/deployments/pr-automation', title: 'Pull Request Automation', @@ -235,22 +227,24 @@ const rootNavData: NavMenu = deepFreeze([ ], }, { - href: '/deployments/addons', - title: 'Managed Kubernetes Add-Ons', - sections: [ - { - title: 'Network Stack', - href: '/deployments/network-addons', - }, - { - title: 'Monitoring', - href: '/deployments/monitoring-addons', - }, - { - title: 'Security', - href: '/deployments/security-addons', - }, - ], + href: '/deployments/multi-tenancy', + title: 'Projects and Multi-Tenancy', + }, + { + href: '/deployments/dashboard', + title: 'Kubernetes Dashboard', + }, + { + href: '/deployments/templating', + title: 'Service Templating', + }, + { + href: '/deployments/terraform-interop', + title: 'Service Contexts and Terraform', + }, + { + href: '/deployments/notifications', + title: 'Notifications', }, { href: '/deployments/ci', @@ -266,22 +260,6 @@ const rootNavData: NavMenu = deepFreeze([ }, ], }, - { - href: '/deployments/dashboard', - title: 'Kubernetes Dashboard', - }, - { - href: '/deployments/terraform-interop', - title: 'Service Contexts and Terraform', - }, - { - href: '/deployments/notifications', - title: 'Notifications', - }, - // { - // href: '/deployments/pipelines', - // title: 'Pipelines', - // }, { href: '/deployments/operations', title: 'Advanced Operations',