Skip to content

Commit

Permalink
Document Service Templating more Clearly, move to more prominent loca…
Browse files Browse the repository at this point in the history
…tion

This is a workflow that definitely needs documentation, but also noticed there's a lot of doc rearrangement we could do so added that.
  • Loading branch information
michaeljguarino committed Jul 16, 2024
1 parent 5d6a8d3 commit 42cb094
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 113 deletions.
117 changes: 74 additions & 43 deletions pages/deployments/templating.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,35 @@
---
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

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:
Expand All @@ -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
```
118 changes: 48 additions & 70 deletions src/NavData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand Down

0 comments on commit 42cb094

Please sign in to comment.