Skip to content

operator-framework/combo

Repository files navigation

combo

combo is a Kubernetes controller that generates and applies resources for all combinations of a manifest template and its arguments.

What on earth does "all combinations" mean!?

For example, say combo is given a template containing 1 parameter -- i.e. distinct token to replaced -- and 2 arguments for that parameter (value to replace a parameter with), then combo will output 2 evaluations (one for each argument).

e.g.

template:

PARAM_1

arguments:

PARAM_1:
- a
- b

evaluations:

---
a
---
b

Simple enough, right? What about something more advanced...

Suppose we give combo a template with 2 parameters and 3 arguments for each parameter.

e.g.

template:

PARAM_1: PARAM_2

arguments:

PARAM_1:
- a
- b
PARAM_2:
- c
- d
- e

evaluations:

---
a: c
---
a: d
---
a: e
---
b: c
---
b: d
---
b: e

Wait, can't Helm do this?

It could, but getting this experience from Helm would require:

  • the use of nested Go Template loops
  • carrying along the rest of the ecosystem; e.g. I don't want to think about Helm charts for something this simple

Can I generate combinations locally?

Yes! There is a built in CLI interaction via the binary. Let's look at the same example we used for the controller in this context.

First, create a simple YAML file that defines two arguments.

# ./sample_input.yaml
PARAM_1: PARAM_2

Next, go ahead and run the eval subcommand.

make build-cli
./combo eval -r PARAM_1=a,b -r PARAM_2=c,d,e sample_input.yaml

This will run the same logic that the controller utilizes to generate combinations and output them to stdout. The above command will produce the following:

---
a: c
---
a: d
---
a: e
---
b: c
---
b: d
---
b: e

Primary use cases

To parameterize RBAC and other namespace-scoped resources so they can be stamped out as necessary later on.

$ cat <<EOF | kubectl create -f -
apiVersion: combo.io/v1alpha1
kind: Template
metadata:
  name: feature
spec:
  body: |
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: feature-controller
      namespace: TARGET_NAMESPACE
    subjects:
    - kind: ServiceAccount
      name: controller
      namespace: feature
    roleRef:
      kind: ClusterRole
      name: feature-controller
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      generateName: feature-user-
      namespace: TARGET_NAMESPACE
    subjects:
    - kind: Group
      name: TARGET_GROUP
      namespace: TARGET_NAMESPACE
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: ClusterRole
      name: feature-user
      apiGroup: rbac.authorization.k8s.io
  parameters:
  - TARGET_GROUP
  - TARGET_NAMESPACE
EOF

Assuming the existence of the feature-controller and feature-user ClusterRoles as well as the feature, staging, and prod Namespaces, instantiate all resource/argument combinations with a Combination:

$ cat <<EOF | kubectl create -f -
apiVersion: combo.io/v1alpha1
kind: Combination
metadata:
  name: enable-feature
spec:
  template: feature
  arguments:
  - key: TARGET_GROUP
    values:
    - "sre"
    - "system:serviceaccounts:ci"
  - key: TARGET_NAMESPACE
    values:
    - staging
    - prod
EOF

combo then surfaces the evaluated template in the status:

$ kubectl get combination -o yaml
apiVersion: combo.io/v1alpha1
kind: Combination
metadata:
  name: enable-feature
spec:
  template:
    name: feature
  arguments:
  - key: TARGET_GROUP
    values:
    - "sre"
    - "system:serviceaccounts:ci"
  - key: TARGET_NAMESPACE
    values:
    - staging
    - prod
status:
  evaluated:
  - apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: feature-controller
      namespace: staging
    subjects:
    - kind: ServiceAccount
      name: controller
      namespace: feature
    roleRef:
      kind: ClusterRole
      name: feature-controller
      apiGroup: rbac.authorization.k8s.io
  - apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      generateName: feature-user-
      namespace: staging
    subjects:
    - kind: Group
      name: sre
      namespace: staging
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: ClusterRole
      name: feature-user
      apiGroup: rbac.authorization.k8s.io
      ...

Ulterior motives

Our "hidden" agenda with combo is for it to:

  • serve as an example of best-practices when building operators
  • be used to dog food operator-framework packaging formats (e.g. OLM, rukpak, etc)
  • become a dependency of operators that require post-install configuration (e.g. RBAC scoping, secret creation, etc)
  • serve as a cruft-free way to up-skill new maintainers through easy contributions

Planned Features

The combo project is currently in active development and you can find up-to-date, tracked work in the Combo Project Board.

Introducing or discussing new roadmap items can be done by opening an issue.

Before opening an issue, it's recommended to check whether a similar issue is already open to avoid duplicating roadmap efforts.