Skip to main content
Version: v0.8.0

Installing Kubernetes Operators


Welcome to Nova Control Plane's guide on managing Kubernetes Operators! Nova provides a simplified way to manage Kubernetes Operators across different clusters. This guide will walk you through the steps to install and manage Kubernetes Operators using Nova. We'll use the Prometheus Operator as an example, but the principles can be applied to other operators as well. This guide is based on the official Prometheus Operator documentation.

Note: The examples provided are for illustrative purposes. In a real-world setup, make sure to customize the SchedulePolicy, deployment, CRDs, and values according to your specific needs.


Before proceeding, you'll need to set labels on the resources you're applying. We've created a Python helper script for this purpose. Save it as and make it executable with chmod +x

Click here to view the script
#!/usr/bin/env python3

This script adds labels to Kubernetes resource YAML files. It supports three modes:
- "cluster": Adds labels only to cluster-scoped resources.
- "namespace": Adds labels only to namespace-scoped resources.
- "all": Adds labels to all resources.

Usage: mode label_key label_value < input.yaml > output.yaml

- PyYAML: A YAML parser for Python.

Author: Elotl Inc.
Date: 28.08.2023

import sys
import yaml
import subprocess
import re
import argparse
from typing import List, Set, Union, Dict

CMD_FETCH_CLUSTER_RESOURCES = "kubectl api-resources --namespaced=false | awk '{print $NF}' | tail -n +2"

def preprocess_yaml(yaml_str: str) -> str:
"""Escape special characters in a YAML string."""
lines = yaml_str.split("\n")
for i, line in enumerate(lines):
if re.match(r"^\s*-\s*=\s*$", line):
lines[i] = line.replace("=", "\"=\"")
elif re.match(r"^\s*-\s*=~\s*$", line):
lines[i] = line.replace("=~", "\"=~\"")
return "\n".join(lines)

def get_cluster_resources() -> Set[str]:
"""Fetch the list of cluster-scoped resources from the Kubernetes cluster."""
cluster_resources = subprocess.check_output(CMD_FETCH_CLUSTER_RESOURCES, shell=True, text=True)
return set(cluster_resources.strip().split("\n"))
except subprocess.CalledProcessError:
sys.stderr.write("Error fetching cluster-scoped resources. Make sure kubectl is installed and configured.\n")

def should_add_label(doc_kind: str, mode: str, cluster_resources: Set[str]) -> bool:
"""Determine if a label should be added based on the mode and kind of the resource."""
return (
mode == "all" or
(mode == "cluster" and doc_kind in cluster_resources) or
(mode == "namespace" and doc_kind not in cluster_resources)

def add_labels(documents: List[Union[None, Dict]], mode: str, label_key: str, label_value: str) -> List[Union[None, Dict]]:
"""Add labels to Kubernetes YAML documents."""
cluster_resources = get_cluster_resources() if mode != "all" else set()

for doc in documents:
if doc is None or 'kind' not in doc:
doc.setdefault('metadata', {}).setdefault('labels', {})

if should_add_label(doc['kind'], mode, cluster_resources):
doc['metadata']['labels'][label_key] = label_value

return documents

def main() -> None:
"""Main function."""
parser = argparse.ArgumentParser(description='Add labels to Kubernetes YAML files.')
parser.add_argument('mode', choices=['cluster', 'namespace', 'all'], help='Mode to apply labels. Can be "cluster", "namespace", or "all".')
parser.add_argument('label_key', help='The key of the label to add.')
parser.add_argument('label_value', help='The value of the label to add.')

args = parser.parse_args()

yaml_str = preprocess_yaml(
documents = list(yaml.safe_load_all(yaml_str))
documents = add_labels(documents, args.mode, args.label_key, args.label_value)

for doc in documents:
if doc is not None:
yaml.safe_dump(doc, sys.stdout, default_flow_style=False)
except yaml.YAMLError as e:
sys.stderr.write(f"YAML Error: {e}\n")
except Exception as e:
sys.stderr.write(f"Unexpected Error: {e}\n")

if __name__ == "__main__":

Steps to Install Operators

1. Define Your SchedulePolicy

Start by defining a SchedulePolicy tailored to your specific clusters and namespaces. This sets the stage for deploying your Operator.

kind: SchedulePolicy
name: prometheus-operator-policy
matchLabels: default
matchLabels: kind-workload-1
- matchLabels: prometheus-operator-policy

Apply the policy:

kubectl create -f example-operator-policy.yaml

2. Install Prometheus Operator and Resources

Use the following one-liner to download, label, and apply all the necessary Prometheus Operator resources. This command uses a helper script,, to add the appropriate labels for Nova management:

LATEST=$(curl -s | jq -cr .tag_name)
curl -sL${LATEST}/bundle.yaml | \
./ all prometheus-operator-policy | \
kubectl create -f -

3. Verify the Operator is Running

After applying the one-liner, you can verify that the Prometheus Operator is running successfully:

kubectl get deploy

Wait for the Prometheus Operator's to show running pods.

Congratulations! You've successfully installed the Prometheus Operator and its resources in your kind-workload-1 cluster using Nova, and all of this was done with a convenient one-liner.

4. Deploying Prometheus

Now we need to setup RBAC for our Prometheus and deploy Prometheus itself. First, save this as prom-rbac.yaml

apiVersion: v1
kind: ServiceAccount
labels: prometheus-operator-policy
name: prometheus
kind: ClusterRole
labels: prometheus-operator-policy
name: prometheus
- apiGroups: [""]
- nodes
- nodes/metrics
- services
- endpoints
- pods
verbs: ["get", "list", "watch"]
- apiGroups: [""]
- configmaps
verbs: ["get"]
- apiGroups:
- ingresses
verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
kind: ClusterRoleBinding
labels: prometheus-operator-policy
name: prometheus
kind: ClusterRole
name: prometheus
- kind: ServiceAccount
name: prometheus
namespace: default

And run:

kubectl create -f prom-rbac.yaml

This will setup RBAC needed by Prometheus deployment.

With that out of the way, we can finally deploy Prometheus itself!

Save this as prometheus.yaml:

kind: Prometheus
labels: prometheus-operator-policy
name: prometheus
serviceAccountName: prometheus
team: frontend
memory: 400Mi
enableAdminAPI: false


kubectl create -f prometheus.yaml

And thats it! You now have a running Prometheus stack inside your kind-workload-1 cluster!

Learn more about our SchedulePolicies and how you can deploy operators across multiple clusters in our Tutorials section.

Reminder: Customize According to Your Needs

As mentioned before, the examples provided here are solely for illustrative purposes. For your specific needs, make sure to adapt the SchedulePolicy, Operator deployment, CRDs, and custom resources.