Skip to content

Commit

Permalink
Use a list of resource_templates instead of specifically named ones f…
Browse files Browse the repository at this point in the history
…or configmaps and secrets
  • Loading branch information
wr0ngway committed Jun 16, 2021
1 parent 353d422 commit 63d961f
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 222 deletions.
87 changes: 45 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
# Kubetruth

The CloudTruth integration for kubernetes that pushes parameter updates into
kubernetes resources (usually config maps and secrets). The goal is to provide
you a mechanism that is as hands off as possible, using naming conventions to
automate the delivery of configuration so that you don't have to jump through
setup hoops for each app/service/etc that you would like to configure with
CloudTruth
kubernetes resources - usually ConfigMaps and Secrets, but any resource is
allowed. The goal is to provide you a mechanism that is as hands off as
possible, using naming conventions to automate the delivery of configuration so
that you don't have to jump through setup hoops for each app/service/etc that
you would like to configure with CloudTruth

## Installation

Expand Down Expand Up @@ -46,17 +46,16 @@ Parameterize the helm install with `--set appSettings.**` to control how kubetru
| projectMappings.root.project_selector | A regexp to limit the projects acted against (client-side). Supplies any named matches for template evaluation | string | "" | no |
| projectMappings.root.key_selector | A regexp to limit the keys acted against (client-side). Supplies any named matches for template evaluation | string | "" | no |
| projectMappings.root.skip | Skips the generation of resources for the selected projects | flag | false | no |
| projectMappings.root.skip_secrets | Prevent transfer of secrets to kubernetes Secrets | flag | false | no |
| projectMappings.root.included_projects | Include the parameters from other projects into the selected ones. This can be recursive in a depth first fashion, so if A imports B and B imports C, then A will get B's and C's parameters. For key conflicts, if A includes B and B includes C, then the precendence is A overrides B overrides C. If A includes \[B, C], then the precendence is A overrides C overrides B. | list | [] | no |
| projectMappings.root.configmap_template | The template to use in generating a kubernetes resource (ConfigMap) for non-secret parameters | string | [default](helm/kubetruth/values.yaml#L94-L108) | no |
| projectMappings.root.secret_template | The template to use in generating a kubernetes resource (Secret) for secret parameters | string | [default](helm/kubetruth/values.yaml#L110-L124) | no |
| projectMappings.root.resource_templates | The templates to use in generating kubernetes resources (ConfigMap/Secrets/other) | string | [default](helm/kubetruth/values.yaml#L94-L129) | no |
| projectMappings.<override_name>.* | Define override mappings to override settings from the root selector for specific projects. When doing this on the command-line (e.g. for `helm install`), it may be more convenient to use `--values <file>` instead of `--set` for large data sets | map | {} | no |

By default, Kubetruth maps the parameters from CloudTruth Projects into
ConfigMaps and Secrets of the same names as the Projects. Kubetruth will not
overwrite any existing kubernetes resources that do not have the label
`app.kubernetes.io/managed-by: kubetruth`. If you have some that you want
kubetruth to manage, then either add the label or delete them manually.
With the default `resource_templates`, Kubetruth maps the parameters from
CloudTruth Projects into ConfigMaps and Secrets of the same names as the
Projects. Kubetruth will not overwrite any existing kubernetes resources that do
not have the label `app.kubernetes.io/managed-by: kubetruth`. If you have some
that you want kubetruth to manage, then either add the label or delete them
manually.

For example, for a CloudTruth layout that looks like:

Expand Down Expand Up @@ -136,9 +135,11 @@ Note that Kubetruth watches for changes to ProjectMappings, so touching any of
them wakes it up from a polling sleep. This makes it quick and easy to test out
configuration changes without having a short polling interval.

To customize how the kubernetes resources are generated, edit the `*_template` properties in the
ProjectMappings. These templates are processed using the [Liquid template
language](https://shopify.github.io/liquid/), and can reference the following liquid variables:
To customize how the kubernetes resources are generated, edit the
`resource_templatse` property in the ProjectMappings. These templates are
processed using the [Liquid template
language](https://shopify.github.io/liquid/), and can reference the following
liquid variables:

* `project` - The project name
* `project_heirarchy` - The `included_projects` tree this project includes (useful to debug when using complex `included_projects`)
Expand All @@ -160,7 +161,7 @@ ones:
* `decode64` - The argument bas64 decoded
* `sha256` - The sha256 digest of the argument

The default `*_template`s add the `parameter_origins` and `project_heirarchy`
The default `resource_templates` add the `parameter_origins` and `project_heirarchy`
key as annotations on each kubernetes resource under management. This can be
disabled by removing them from the template, or wrapping them in a test for
`debug`. The data produced by these help to illustrate how project inclusion
Expand All @@ -181,14 +182,15 @@ To create kubernetes Resources in namespaces named after each Project:
```
kubectl edit pm kubetruth-root
```
and add the metadata.namespace field to configmap_template and secret_template like so:
and add the metadata.namespace field to each template in `resource_templates` like so:
```yaml
spec:
configmap_template: |
apiVersion: v1
kind: ConfigMap
metadata:
namespace: {{ project | dns_safe }}
resource_templates:
- |
apiVersion: v1
kind: ConfigMap
metadata:
namespace: {{ project | dns_safe }}
```

#### Share common data
Expand Down Expand Up @@ -226,20 +228,21 @@ metadata:
spec:
scope: override
project_selector: funkyProject
configmap_template: |
apiVersion: v1
kind: ConfigMap
metadata:
namespace: notSoFunkyNamespace
name: notSoFunkyConfigMap
<snipped>
secret_template: |
apiVersion: v1
kind: Secret
metadata:
namespace: notSoFunkyNamespace
name: notSoFunkySecret
<snipped>
resource_templates:
- |
apiVersion: v1
kind: ConfigMap
metadata:
namespace: notSoFunkyNamespace
name: notSoFunkyConfigMap
<snipped>
- |
apiVersion: v1
kind: Secret
metadata:
namespace: notSoFunkyNamespace
name: notSoFunkySecret
<snipped>
EOF
```

Expand Down Expand Up @@ -279,16 +282,16 @@ Kind: ProjectMapping
Metadata:
<snipped>
Spec:
configmap_template: |
<snipped>
secret_template: |
<snipped>
resource_templates:
- |
<snipped>
- |
<snipped>
included_projects:
key_selector:
project_selector:
scope: root
skip: false
skip_secrets: false
Events: <none>
```
Expand Down
14 changes: 5 additions & 9 deletions helm/helmv2/templates/projectmapping.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,16 @@ spec:
skip:
type: boolean
description: Skips the generation of resources for the selected projects. Useful for excluding projects that should only be included into others.
skip_secrets:
type: boolean
description: Prevent transfer of secrets to kubernetes Secrets
included_projects:
type: array
items:
type: string
description: Include the parameters from other projects into the selected ones. This can be recursive in a depth first fashion, so if A imports B and B imports C, then A will get B's and C's parameters. For key conflicts, if A includes B and B includes C, then the precendence is A overrides B overrides C. If A includes [B, C], then the precendence is A overrides C overrides B.
configmap_template:
type: string
description: The template to use in generating a kubernetes resource (ConfigMap) for non-secret parameters
secret_template:
type: string
description: The template to use in generating a kubernetes resource (Secret) for secret parameters
resource_templates:
type: array
items:
type: string
description: The templates to use in generating kubernetes resources
additionalPrinterColumns:
- name: Scope
type: string
Expand Down
14 changes: 5 additions & 9 deletions helm/kubetruth/crds/projectmapping.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,16 @@ spec:
skip:
type: boolean
description: Skips the generation of resources for the selected projects
skip_secrets:
type: boolean
description: Prevent transfer of secrets to kubernetes Secrets
included_projects:
type: array
items:
type: string
description: Include the parameters from other projects into the selected ones. This can be recursive in a depth first fashion, so if A imports B and B imports C, then A will get B's and C's parameters. For key conflicts, if A includes B and B includes C, then the precendence is A overrides B overrides C. If A includes [B, C], then the precendence is A overrides C overrides B.
configmap_template:
type: string
description: The template to use in generating a kubernetes resource (ConfigMap) for non-secret parameters
secret_template:
type: string
description: The template to use in generating a kubernetes resource (Secret) for secret parameters
resource_templates:
type: array
items:
type: string
description: The templates to use in generating kubernetes resources
required:
- scope
additionalPrinterColumns:
Expand Down
71 changes: 38 additions & 33 deletions helm/kubetruth/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,37 +88,42 @@ projectMappings:
project_selector: ""
key_selector: ""
skip: false
skip_secrets: false
included_projects: []
configmap_template: |
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ project | dns_safe }}"
labels:
version: "{{ parameters | sort | to_json | sha256 | slice: 0, 7 }}"
annotations:
kubetruth/project_heirarchy: |
{{ project_heirarchy | to_yaml | indent: 6 | lstrip }}
kubtruth/parameter_origins: |
{{ parameter_origins | to_yaml | indent: 6 | lstrip }}
data:
{% for parameter in parameters %}
{{ parameter[0] | stringify }}: {{ parameter[1] | stringify }}
{% endfor %}
secret_template: |
apiVersion: v1
kind: Secret
metadata:
name: "{{ project | dns_safe }}"
labels:
version: "{{ parameters | sort | to_json | sha256 | slice: 0, 7 }}"
annotations:
kubetruth/project_heirarchy: |
{{ project_heirarchy | to_yaml | indent: 6 | lstrip }}
kubtruth/parameter_origins: |
{{ parameter_origins | to_yaml | indent: 6 | lstrip }}
data:
{% for parameter in parameters %}
{{ parameter[0] | stringify }}: {{ parameter[1] | encode64 | stringify }}
{% endfor %}
resource_templates:
- |
{%- if parameters.size > 0 %}
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ project | dns_safe }}"
labels:
version: "{{ parameters | sort | to_json | sha256 | slice: 0, 7 }}"
annotations:
kubetruth/project_heirarchy: |
{{ project_heirarchy | to_yaml | indent: 6 | lstrip }}
kubtruth/parameter_origins: |
{{ parameter_origins | to_yaml | indent: 6 | lstrip }}
data:
{%- for parameter in parameters %}
{{ parameter[0] | stringify }}: {{ parameter[1] | stringify }}
{%- endfor %}
{%- endif %}
- |
{%- if secrets.size > 0 %}
apiVersion: v1
kind: Secret
metadata:
name: "{{ project | dns_safe }}"
labels:
version: "{{ secrets | sort | to_json | sha256 | slice: 0, 7 }}"
annotations:
kubetruth/project_heirarchy: |
{{ project_heirarchy | to_yaml | indent: 6 | lstrip }}
kubtruth/parameter_origins: |
{{ secret_origins | to_yaml | indent: 6 | lstrip }}
data:
{%- for secret in secrets %}
{{ secret[0] | stringify }}: {{ secret[1] | encode64 | stringify }}
{%- endfor %}
{%- endif %}
18 changes: 9 additions & 9 deletions lib/kubetruth/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ class DuplicateSelection < Kubetruth::Error; end
:project_selector,
:key_selector,
:skip,
:skip_secrets,
:included_projects,
:configmap_template,
:secret_template,
:resource_templates,
keyword_init: true
) do

Expand All @@ -25,13 +23,17 @@ def initialize(*args, **kwargs)

def convert_types(hash)
selector_key_pattern = /_selector$/
template_key_pattern = /_template$/
template_key_pattern = /_templates?$/
hash.merge(hash) do |k, v|
case k
when selector_key_pattern
Regexp.new(v)
when template_key_pattern
Kubetruth::Template.new(v)
if k.ends_with?('s')
v.collect {|t| Kubetruth::Template.new(t) }
else
Kubetruth::Template.new(v)
end
else
v
end
Expand All @@ -45,10 +47,8 @@ def convert_types(hash)
project_selector: '',
key_selector: '',
skip: false,
skip_secrets: false,
included_projects: [],
configmap_template: "",
secret_template: ""
resource_templates: []
}.freeze

def initialize(project_mapping_crds)
Expand Down Expand Up @@ -92,7 +92,7 @@ def spec_for_project(project_name)
logger.debug {"Using root spec for project '#{project_name}'"}
when 1
spec = specs.first
logger.debug {"Using override spec '#{spec.project_selector}' for project '#{project_name}'"}
logger.debug {"Using override spec '#{spec.project_selector.source}' for project '#{project_name}'"}
else
dupes = specs.collect {|s| "'#{s.project_selector}'" }
raise DuplicateSelection, "Multiple configuration specs (#{dupes.inspect}) match the project '#{project_name}': }"
Expand Down
42 changes: 17 additions & 25 deletions lib/kubetruth/etl.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'benchmark'
require 'yaml'
require_relative 'config'
require_relative 'ctapi'
require_relative 'kubeapi'
Expand Down Expand Up @@ -119,31 +120,24 @@ def apply
config_origins = Hash[param_origins_parts[true] || []]
secret_origins = Hash[param_origins_parts[false] || []]

template_vars = {
project: project.name,
project_heirarchy: project.heirarchy,
debug: logger.debug?
}

configmap_yml = project.spec.configmap_template.render(
**template_vars.merge(
project.spec.resource_templates.each_with_index do |template, i|
logger.debug { "Processing template #{i}/#{project.spec.resource_templates.size}" }
resource_yml = template.render(
project: project.name,
project_heirarchy: project.heirarchy,
debug: logger.debug?,
parameters: config_param_hash,
parameter_origins: config_origins
)
)
secret_yml = project.spec.secret_template.render(
**template_vars.merge(
parameters: secret_param_hash,
parameter_origins: secret_origins
parameter_origins: config_origins,
secrets: secret_param_hash,
secret_origins: secret_origins
)
)

kube_apply(configmap_yml)

if ! project.spec.skip_secrets
kube_apply(secret_yml)
parsed_yml = YAML.safe_load(resource_yml)
if parsed_yml
kube_apply(parsed_yml)
else
logger.debug {"Skipping empty template"}
end
end

end

end
Expand All @@ -152,9 +146,7 @@ def params_to_hash(param_list)
Hash[param_list.collect {|param| [param.key, param.value]}]
end

def kube_apply(yml)
parsed_yml = YAML.load(yml)
logger.debug parsed_yml
def kube_apply(parsed_yml)
kind = parsed_yml["kind"]
namespace = parsed_yml["metadata"]["namespace"] || kubeapi.namespace
name = parsed_yml["metadata"]["name"]
Expand Down
Loading

0 comments on commit 63d961f

Please sign in to comment.