Skip to content

Repository files navigation

Build StatusCoverage StatusConfigured by CloudTruth

Kubetruth

The CloudTruth integration for kubernetes that pushes parameter updates into 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.

CloudTruth Operator KubeTruth Workflow

Installation

helm repo add cloudtruth https://packages.cloudtruth.com/charts/
helm install \
--set appSettings.apiKey=<api_key> \
--set projectMappings.root.environment=<environment> \
kubetruth cloudtruth/kubetruth

Note that the helm release name (kubetruth above) is used in generating the names of the resources created at install time. Thus in the examples below, a name like kubetruth-root for the default installed CRD would be different in your system if you gave helm install a different release name.

Update

helm repo update
helm upgrade kubetruth cloudtruth/kubetruth

Uninstall

helm delete kubetruth
helm repo remove cloudtruth

Usage

Parameterize the helm install with --set * or --values yourConfig.yaml to control how kubetruth matches against your organization's naming conventions:

ParameterDescriptionTypeDefaultRequired
appSettings.apiKeyThe CloudTruth api key. Read only access is sufficientstringn/ayes
appSettings.pollingIntervalInterval to poll CloudTruth api for changesinteger300no
appSettings.debugDebug logging and behaviorflagfalseno
secret.createCreate the kubernetes Secret containing the supplied api-keybooleantrueno
secret.nameThe name of the kubernetes Secret to createstring<helm fullname>no
projectMappings.root.environmentThe CloudTruth environment to lookup parameter values for.stringdefaultyes
projectMappings.root.project_selectorA regexp to limit the projects acted against (client-side). This performs a substring match, so use regexp anchors to match the entire string, e.g. "^myProject$". Supplies any named matches for template evaluationstring""no
projectMappings.root.key_selectorA regexp to limit the keys acted against (client-side). This performs a substring match, so use regexp anchors to match the entire string, e.g. "^myKey$". Supplies any named matches for template evaluationstring""no
projectMappings.root.tagThe version tag used when querying for parametersstringnoneno
projectMappings.root.skipSkips the generation of resources for the selected projectsflagfalseno
projectMappings.root.suppress_namespace_inheritancePrevents the CRD from the primary namespace from being inherited by secondary namespacesflagfalseno
projectMappings.root.log_levelSets the kubetruth logging level while handling the selected projectsenum(debug, info, warn, error, fatal)as set by clino
projectMappings.root.included_projectsInclude 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.contextAdditional variables made available to the resource templates. Can also be templatesmapdefaultno
projectMappings.root.active_templatesSelects the templates that should be active, includes all templates when nil, none when emptylistnilno
projectMappings.root.resource_templatesThe templates to use in generating kubernetes resources (ConfigMap/Secrets/other)mapdefaultno
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 setsmap{}no

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:

myProject:

oneParam=value1
twoParam=value2

otherProject:

someParam=value3
mySecret=value4 (marked as a secret within CloudTruth)

Kubetruth will generate the kubernetes resources:

ConfigMap named myProject:

oneParam: value1twoParam: value2

ConfigMap named otherProject:

someParam: value3

Secret named otherProject:

mySecret: value4

These kubernetes resources can then be referenced in the standard ways.

To use them as environment variables in a pod:

envFrom:
- configMapRef:
name: otherProjectenvFrom:
- secretRef:
name: otherProject

To use them as files on disk in a pod:

containers:
- name: myProjectvolumeMounts:
- name: config-volumemountPath: /etc/myConfigvolumes:
- name: config-volumeconfigMap:
name: myProject

Note that config map updates don't get seen by a running pod. You can use something like Reloader to automate restarting the pod on a ConfigMap change, or read config from mounted volumes for configmaps/secrets, which do get updated automatically in a running pod. Alternatively, you make your ConfigMap's name include a checksum of its contents, and patch a pre-existing Deployment/Pod whenever that name changes. Similary, you can also make use of the checksum in the name when using kubetruth to deploy

Additional configuration

Kubetruth uses a CustomResourceDefinition called ProjectMapping(.kubetruth.cloudtruth.com) for additional configuration. The ProjectMapping CRD has two types identified by the scope property, the root scope and the override scope. The root scope is required, and there can be only one per namespace (see below). It sets up the global behavior for mapping the CloudTruth projects to kubernetes resources. You can edit it in the standard ways, e.g. kubectl edit projectmapping kubetruth-root. The override scope allows you to override the root scope's behavior for those CloudTruth projects whose names match its project_selector pattern.

For the project_selector, which is defined under the Usage section, you use a regex to match against CloudTruth project names, e.g. "^service" will match any project that starts with service. Since this does substring matching, if you want to match a specific CloudTruth project name you should use regex anchors to ensure you don't match more than expected. For example, if you have a CloudTruth project called kubernetes_secrets, you'll want the project_selector value to be "^kubernetes_secrets$" so you don't accidentally match another project named not_kubernetes_secrets.

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. You can also force a wakeup by execing the wakeup script in the running container:

kubectl exec deployment/kubetruth -- wakeup

To customize how the kubernetes resources are generated, edit the resource_templates property in the ProjectMappings. These templates are processed using the Liquid template language, and can reference the following liquid variables:

Liquid VariablesDescription
templateThe name of the template currently being rendered.
kubetruth_namespaceThe namespace kubetruth is installed in.
mapping_namespaceThe namespace that the current set of mappings exist in.
projectThe project name.
project_heirarchyThe included_projects tree that this project includes. (useful to debug when using complex included_projects)
debugIndicates if kubetruth is operating in debug (logging) mode.
parametersThe CloudTruth parameters from the project.
parameter_originsThe projects each parameter originates from. (useful to debug when using complex included_projects)
secretsThe CloudTruth secrets from the project.
secret_originsThe projects each secret originates from. (useful to debug when using complex included_projects)
templatesThe CloudTruth templates from the project keyed by their name, e.g. templates.mytemplate . Evaluated with the environment from the active ProjectMapping
templates.namesThe names of the CloudTruth templates for the project
contextA hash of context variables supplied from ProjectMappings. (useful to override portions of templates without having to replace them completely in an override)

In addition to the built in liquid filters, kubetruth also define a few custom ones:

Custom FiltersDescription
dns_safeEnsures the string is safe for use as a kubernetes resource name (i.e. Namespace/ConfigMap/Secret names)
env_safeEnsures the string is safe for setting as a shell environment variable
key_safeEnsures the string is safe for use as a key inside a ConfigMap/Secret data hash
indent: countIndents each line in the argument by count spaces
nindent: countAdds a leading newline, then indents each line in the argument by count spaces
stringifyConverts argument to a string safe to use in yaml (escapes quotes and surrounds with the quote character)
parse_yamlParses yaml string into a structured representation
to_yamlConverts object to a yaml representation
parse_jsonParses json string into a structured representation
to_jsonConverts object to a json representation
encode64The argument bas64 encoded
decode64The argument bas64 decoded
sha256The sha256 digest of the argument
inflateConverts a map of key/values into a nested data structure based on a delimiter in the key name, e.g. {foo.baz.bum: 2} => {foo: {bar: {baz: 2}}} Inverse of deflate
deflateConverts a nested data structure into a single level map using a delimiter to indicate level in the key name, e.g. {foo: {bar: {baz: 2}}} => {foo.baz.bum: 2} Inverse of inflate
typifyConverts string values into primitive types (int, float, bool) where applicable for a nested data structure
mergeCombines two hashes into one, a.l.a ruby merge
re_replaceRegexp search and replace, a.l.a ruby gsub, e.g. "foo" | re_replace: "o+", "X"
re_containsReturns a boolean for the regexp compare against the string target, e.g. "foo" | re_contains: "o+" => true

The default resource_templates make use of the context attribute to allow simpler modification of some common fields. These include:

Context VariablesDescription
context.resource_nameset this in a ProjectMapping to supply a different name to the the default templates
context.resource_namespaceset this in a ProjectMapping to supply a different namespace to the default templates
context.skip_secretsset this in a ProjectMapping to prevent output of a Secret resource even when secrets are present

Since the context is a freeform map type, you can add custom items to it, as well as architect any custom templates to make use of those custom items in the same way the default templates do. If the value of a context entry is a string, it is treated as a template, and can reference and set variables in the resource_template that is evaluating it. If the value is of some other yaml type (e.g. boolean/number/list/map), it will get passed through as that type to the template that is referencing it, so you can use it in more complex template logic like {% foreach item in context.my_items %}

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 affects the project the resources were written for. It currently shows the project heirarchy and the project each parameter originates from, for example an entry like timeout: myService (commonService -> common) indicates that the timeout parameter is getting its value from the myService project, and if you removed it from there, it would then get it from the commonService project, and if you removed that, it would then get it from the common project.

Multi Instance Config

By default, Kubetruth is setup with a single set of ProjectMapping CRDs installed into the same namespace it was installed to. These are the primary CRDs. For systems that use independent kubernetes clusters per environment, this is all that you need. If, however, you'd like to be able to run multiple environments in the same cluster, you can make use of the multi-instance feature of kubetruth.

To do so, one simply needs to create ProjectMapping CRDs in namespaces other than the primary. These CRDs will automatically inherit the contents of the CRD of the same name from the primary namespace, and you can then selectively override the attributes you need to change for the supplemental instance. This allows you to reuse all the templates/logic/etc that you setup in the primary, and only have to change the differing dimension. See the environmentexample below

Example Config

The projectmapping resource has a shortname of pm for convenience when using kubectl.

Namespace per Project

To create kubernetes Resources in namespaces named after each Project:

kubectl edit pm kubetruth-root

and set the context.resource_namespace field:

spec:
context:
resource_namespace: '{{ project | dns_safe }}'

Or to do it with kubectl patch:

kubectl patch pm kubetruth-root --type json --patch '[{"op": "replace", "path": "/spec/context/resource_namespace", "value": "{{ project | dns_safe }}"}]'

Or to do it at install time, add the following to the helm install command:

--set projectMappings.root.context.resource_namespace="\{\{ project | dns_safe \}\}"

Share common data

To include the parameters from a Project named Base into all other projects, without creating Resources for Base itself:

# Set the included_project in the root mapping
kubectl patch pm kubetruth-root --type json --patch '[{"op": "replace", "path": "/spec/included_projects", "value": ["Base"]}]'
# Exclude the Base project by creating an override mapping that skips it:
kubectl apply -f - <<EOF
apiVersion: kubetruth.cloudtruth.com/v1
kind: ProjectMapping
metadata:
name: exclude-base
spec:
scope: override
project_selector: "^Base$"
skip: true
included_projects: []
EOF

Customize naming of Resources

To override the naming of kubernetes Resources on a per-Project basis:

kubectl apply -f - <<EOF
apiVersion: kubetruth.cloudtruth.com/v1
kind: ProjectMapping
metadata:
name: funkyproject-special-naming
spec:
scope: override
project_selector: funkyProject
context:
resource_name: notSoFunkyConfigMap
resource_namespace: notSoFunkyNamespace
EOF

Environment per namespace

To setup an environment per namespace:

# Optionally disable output from the primary. This is only neccessary if
# kubetruth is installed to a namespace that you don't want to generate
# resources in
kubectl patch pm kubetruth-root --type json --patch '[{"op": "replace", "path": "/spec/skip", "value": "true"}]'
# Tag each namespace that you'd like to have its own environment
# The name should be the same name as the primary root mapping created by the
# kubetruth installer
kubectl --namespace <your_namespace> apply -f - <<EOF
apiVersion: kubetruth.cloudtruth.com/v1
kind: ProjectMapping
metadata:
name: <your_root_mapping_name>
spec:
scope: root
environment: <your_environment>
skip: false
EOF

More specific project selection

To limit the Projects processed to those whose names start with service, except for serviceOddball:

kubectl patch pm kubetruth-root --type json --patch '[{"op": "replace", "path": "/spec/project_selector", "value": "^service"}]'
kubectl apply -f - <<EOF
apiVersion: kubetruth.cloudtruth.com/v1
kind: ProjectMapping
metadata:
name: funkyproject-special-naming
spec:
scope: override
project_selector: serviceOddball
skip: true
EOF

To see the ProjectMappings that have been setup

$ kubectl get pm
NAME SCOPE PROJECT AGE
exclude-base override ^Base$ 7m6s
funkyproject-special-naming override serviceOddball 13s
kubetruth-root root ^service 27m
$ kubectl describe pm kubetruth-root
Name: kubetruth-root
Namespace: default
<snipped>

Additional examples

More examples are available in the examples directory

Development

After checking out the repo, run bundle to install dependencies. Then, run bundle exec rspec to run the tests. You can also run bundle exec rake console for an interactive prompt that will allow you to experiment.

To install and run via helm in a local cluster:

mkdir local
cat > local/values.yml <<-EOF
image:
repository: kubetruth
pullPolicy: Never
tag: latest
appSettings:
debug: true
apiKey: <your_api_key>
EOF
# requires minikube to be installed and running
rake install
# OR
# If using minikube, ensure that docker builds the image into the minikube container
# with the command:
# eval $(minikube docker-env)
#
docker build --release development -t kubetruth . && helm install \
--set image.repository=kubetruth --set image.pullPolicy=Never --set image.tag=latest \
--set appSettings.debug=true --set appSettings.apiKey=$CLOUDTRUTH_API_KEY \
kubetruth ./helm/kubetruth/

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/cloudtruth/kubetruth.

About

The CloudTruth integration for kubernetes that pushes parameter updates into kubernetes config maps and secrets

Topics

Resources

Security policy

Stars

10 stars

Watchers

2 watching

Forks

Releases

Contributors

Languages