Skip to main content

A Python library for rendering Helm charts and Kubernetes resources with optional ArgoCD Application generation. Features an abstract Template base class for consistent template management.

Project description

Kubeman

PyPI - Version

A Python library for rendering Helm charts and Kubernetes resources with optional ArgoCD Application generation. Features an abstract Template base class for consistent template management.

Features

  • Abstract Template base class for all template types
  • HelmChart class for defining Helm charts
  • KubernetesResource class for raw Kubernetes resources (no Helm required)
  • TemplateRegistry for managing multiple templates (charts and resources)
  • Command-line interface (CLI) for rendering and applying manifests
  • Resource visualization with Graphviz DOT diagrams showing resource relationships
  • Automatic Docker image build and load steps (executed sequentially during template registration)
  • Git operations for manifest repository management
  • Docker image build and push utilities with custom Dockerfile support
  • Optional ArgoCD Application manifest generation (opt-in)

Installation

From Source

Using uv (recommended):

uv pip install -e .

Or using pip:

pip install -e .

Development Setup

Install development dependencies and format code:

uv sync --dev
uv tool run black .

Pre-commit Hooks

Install pre-commit globally:

uv tool install pre-commit
pre-commit install

This automatically formats code with black before commits. Run manually:

pre-commit run --all-files

Alternative: Install as dev dependency:

uv sync --dev
uv run python -m pre_commit install

Usage

Template Architecture

Both HelmChart and KubernetesResource inherit from the abstract Template base class, providing common functionality for ArgoCD Application generation (opt-in), manifest directory management, namespace/name properties, rendering to filesystem, and optional Docker image build steps executed sequentially during template registration.

Creating a Helm Chart

Subclass HelmChart and implement the required abstract methods:

from kubeman import HelmChart, TemplateRegistry

@TemplateRegistry.register
class MyChart(HelmChart):
    @property
    def name(self) -> str:
        return "my-chart"

    @property
    def repository(self) -> dict:
        """Return repository information"""
        return {
            "type": "classic",  # or "oci" or "none"
            "remote": "https://charts.example.com"
        }

    @property
    def namespace(self) -> str:
        return "my-namespace"

    @property
    def version(self) -> str:
        return "1.0.0"

    def generate_values(self) -> dict:
        """Generate values.yaml content"""
        return {
            "replicaCount": 3,
            "image": {
                "repository": "my-app",
                "tag": "latest"
            }
        }

Creating a Kubernetes Resource (Without Helm)

For projects that don't need Helm but want optional ArgoCD Application generation and manifest management, use KubernetesResource. Supports two patterns:

Pattern 1: Using Helper Methods (Recommended)

Use built-in helper methods to build Kubernetes resources:

from kubeman import KubernetesResource, TemplateRegistry

@TemplateRegistry.register
class DogBreedsDbChart(KubernetesResource):
    """Dog Breeds PostgreSQL database resources."""

    def __init__(self):
        super().__init__()
        self.namespace = "dog-breeds"

        # Add Namespace
        self.add_namespace(
            name="dog-breeds",
            labels={"app": "dog-breeds", "component": "database"},
        )

        # Add ConfigMap for database configuration
        self.add_configmap(
            name="dog-breeds-db-config",
            namespace="dog-breeds",
            data={
                "POSTGRES_DB": "dog_breeds_db",
                "POSTGRES_USER": "airflow",
            },
            labels={"app": "dog-breeds", "component": "database"},
        )

        # Add Secret for database password
        self.add_secret(
            name="dog-breeds-db-secret",
            namespace="dog-breeds",
            string_data={
                "POSTGRES_PASSWORD": "airflow",
            },
            labels={"app": "dog-breeds", "component": "database"},
        )

        # Add PersistentVolumeClaim
        self.add_persistent_volume_claim(
            name="dog-breeds-db-pvc",
            namespace="dog-breeds",
            access_modes=["ReadWriteOnce"],
            storage="5Gi",
            labels={"app": "dog-breeds", "component": "database"},
        )

        # Add Deployment
        self.add_deployment(
            name="dog-breeds-db",
            namespace="dog-breeds",
            replicas=1,
            strategy_type="Recreate",
            labels={"app": "dog-breeds", "component": "database"},
            containers=[
                {
                    "name": "postgres",
                    "image": "postgres:16-alpine",
                    "ports": [{"name": "postgres", "containerPort": 5432}],
                    "env": [
                        {
                            "name": "POSTGRES_PASSWORD",
                            "valueFrom": {
                                "secretKeyRef": {
                                    "name": "dog-breeds-db-secret",
                                    "key": "POSTGRES_PASSWORD",
                                },
                            },
                        },
                    ],
                    "volumeMounts": [
                        {"name": "postgres-storage", "mountPath": "/var/lib/postgresql/data"},
                    ],
                }
            ],
            volumes=[
                {"name": "postgres-storage", "persistentVolumeClaim": {"claimName": "dog-breeds-db-pvc"}},
            ],
            init_containers=[
                {
                    "name": "init-db",
                    "image": "busybox:latest",
                    "command": ["sh", "-c", "echo Initializing database schema"],
                }
            ],
        )

        # Add Service
        self.add_service(
            name="dog-breeds-db",
            namespace="dog-breeds",
            service_type="ClusterIP",
            selector={"app": "dog-breeds", "component": "database"},
            ports=[{"name": "postgres", "port": 5432, "targetPort": 5432}],
            labels={"app": "dog-breeds", "component": "database"},
        )

Available Helper Methods: add_namespace(), add_configmap(), add_secret(), add_persistent_volume_claim(), add_deployment(), add_statefulset(), add_service(), add_ingress(), add_service_account(), add_role(), add_role_binding(), add_cluster_role(), add_cluster_role_binding(), add_custom_resource()

Pattern 2: Override manifests() Method

For complex logic or custom manifest generation, override the manifests() method:

from kubeman import KubernetesResource, TemplateRegistry

@TemplateRegistry.register
class MyAppResources(KubernetesResource):
    def __init__(self):
        super().__init__()
        self.namespace = "production"

    def manifests(self) -> list[dict]:
        """Return list of Kubernetes manifests"""
        return [
            {
                "apiVersion": "v1",
                "kind": "ConfigMap",
                "metadata": {
                    "name": "my-app-config",
                    "namespace": "production"
                },
                "data": {
                    "DATABASE_URL": "postgres://db:5432/myapp",
                }
            },
            {
                "apiVersion": "apps/v1",
                "kind": "Deployment",
                "metadata": {
                    "name": "my-app",
                    "namespace": "production"
                },
                "spec": {
                    "replicas": 3,
                    "selector": {"matchLabels": {"app": "my-app"}},
                    "template": {
                        "metadata": {"labels": {"app": "my-app"}},
                        "spec": {
                            "containers": [{
                                "name": "my-app",
                                "image": "gcr.io/my-project/my-app:v1.0.0",
                                "envFrom": [{
                                    "configMapRef": {"name": "my-app-config"}
                                }]
                            }]
                        }
                    }
                }
            }
        ]

Build and Load Steps

Templates can define Docker image build and load steps that execute automatically when templates are imported. Steps run sequentially in registration order: build steps first, then load steps.

Build Steps

Override the build() method to add build steps:

from kubeman import KubernetesResource, TemplateRegistry, DockerManager

@TemplateRegistry.register
class MyApp(KubernetesResource):
    def __init__(self):
        super().__init__()
        self.name = "my-app"
        self.namespace = "production"

    def build(self) -> None:
        """Build Docker images for this template."""
        docker = DockerManager()
        docker.build_image(
            component="my-app",
            context_path="./app",
            tag="latest",
            dockerfile="Dockerfile.prod"  # Optional: custom Dockerfile name
        )
        docker.tag_image(
            source_image=f"{docker.registry}/my-app",
            target_image="my-app",
            source_tag="latest"
        )

Load Steps

For local development with kind clusters, add load steps:

def load(self) -> None:
    """Load Docker images into kind cluster."""
    docker = DockerManager()
    docker.kind_load_image("my-app", tag="latest")

Key points: Build steps execute automatically when templates are imported. Load steps execute after build steps. Use --skip-build flag to skip build/load steps during render/apply. If a build or load fails, template registration fails with a clear error message.

Rendering Charts and Resources

Render templates using the CLI or Python API.

Using the CLI (Recommended)

# Render all templates from a Python file
kubeman render --file kubeman.py

# Render without executing build steps
kubeman render --file kubeman.py --skip-build

# Render and apply to Kubernetes cluster (builds execute automatically)
kubeman apply --file kubeman.py

# Apply without executing build steps
kubeman apply --file kubeman.py --skip-build

The CLI imports your template file (containing @TemplateRegistry.register decorated classes), discovers all registered templates, renders each to the manifests/ directory, and for apply, runs kubectl apply on the rendered manifests.

Example template file (kubeman.py):

from kubeman import KubernetesResource, TemplateRegistry

@TemplateRegistry.register
class MyAppResources(KubernetesResource):
    def __init__(self):
        super().__init__()
        self.namespace = "production"
        # ... add resources using helper methods ...

Using the Python API

from kubeman import TemplateRegistry

# Get all registered templates (charts and resources)
templates = TemplateRegistry.get_registered_templates()

# Render each template
for template_class in templates:
    template = template_class()
    template.render()  # Generates manifests and ArgoCD Application

For HelmChart: Renders to manifests/{chart-name}/{chart-name}-manifests.yaml, writes extra manifests to manifests/{chart-name}/, and generates ArgoCD Application to manifests/apps/{chart-name}-application.yaml (if enabled).

Visualizing Resource Relationships

Generate Graphviz DOT diagrams to visualize Kubernetes resources and their relationships:

# Generate visualization and save to file
kubeman visualize --file kubeman.py --output diagram.dot

# Output to stdout
kubeman visualize --file kubeman.py

# Include CustomResourceDefinitions in visualization
kubeman visualize --file kubeman.py --output diagram.dot --show-crds

# Use custom manifests directory
kubeman visualize --file kubeman.py --output-dir ./custom-manifests --output diagram.dot

The visualization command:

  1. Renders templates to generate manifests (build steps are automatically skipped)
  2. Analyzes rendered manifests to detect relationships (ConfigMap references, Service selectors, network connections, etc.)
  3. Generates a Graphviz DOT diagram showing resource hierarchy and relationships
  4. Outputs to a file or stdout

Rendering the diagram:

After generating the DOT file, render it to an image using Graphviz:

# Generate PNG image
dot -Tpng diagram.dot -o diagram.png

# Generate SVG image
dot -Tsvg diagram.dot -o diagram.svg

Visualization features:

  • Shows resources grouped by namespace
  • Displays relationships between resources (uses, selects, connects-to)
  • Includes connection addresses (e.g., database URLs from ConfigMaps)
  • Filters to only show resources from templates in your kubeman.py file
  • Optionally includes CustomResourceDefinitions (hidden by default)

For KubernetesResource: Writes each manifest to manifests/{name}/{manifest-name}-{kind}.yaml and generates ArgoCD Application to manifests/apps/{name}-application.yaml (if enabled).

Advanced Chart Configuration

Custom Repository Package Name

@property
def repository_package(self) -> str:
    return "different-package-name"

OCI Registry Support

@property
def repository(self) -> dict:
    return {
        "type": "oci",
        "remote": "oci://registry.example.com/charts"
    }

Extra Manifests

def extra_manifests(self) -> list[dict]:
    return [
        {
            "apiVersion": "v1",
            "kind": "ConfigMap",
            "metadata": {"name": "my-config"},
            "data": {"key": "value"}
        }
    ]

Enabling ArgoCD Application Generation (Opt-In)

ArgoCD Application generation is disabled by default. Enable via environment variable:

export ARGOCD_APP_REPO_URL="https://github.com/org/manifests-repo"

Or override enable_argocd() method:

@TemplateRegistry.register
class MyChart(HelmChart):
    def enable_argocd(self) -> bool:
        """Enable ArgoCD Application generation for this chart"""
        return True

If ARGOCD_APP_REPO_URL is not set and enable_argocd() returns True, override application_repo_url() to provide the repository URL.

Custom ArgoCD Application Settings

def enable_argocd(self) -> bool:
    """Enable ArgoCD Application generation (opt-in)"""
    return True

def application_repo_url(self) -> str:
    """Override the repository URL for ArgoCD applications"""
    return "https://github.com/org/manifests-repo"

def application_target_revision(self) -> str:
    """Override the target revision (defaults to current branch)"""
    return "main"

def managed_namespace_metadata(self) -> dict:
    """Add labels to managed namespaces"""
    return {
        "app.kubernetes.io/managed-by": "argocd"
    }

def argo_ignore_spec(self) -> list:
    """Configure ArgoCD ignore differences"""
    return [
        {
            "group": "apps",
            "kind": "Deployment",
            "jsonPointers": ["/spec/replicas"]
        }
    ]

Git Operations

The GitManager class provides utilities for working with Git repositories:

from kubeman import GitManager

git = GitManager()

# Get current commit hash (from STABLE_GIT_COMMIT env var)
commit_hash = git.fetch_commit_hash()

# Get current branch name (from STABLE_GIT_BRANCH env var)
branch_name = git.fetch_branch_name()

# Push rendered manifests to a repository
git.push_manifests(repo_url="https://github.com/org/manifests-repo")

The push_manifests() method clones the manifests repository, checks out or creates the branch matching STABLE_GIT_BRANCH, copies rendered manifests from RENDERED_MANIFEST_DIR, and commits and pushes the changes.

Docker Operations

The DockerManager class helps build and push Docker images:

from kubeman import DockerManager

# Initialize (uses DOCKER_REGISTRY environment variable if set)
# Set DOCKER_REGISTRY="us-central1-docker.pkg.dev/my-project/my-repo" to use a registry
docker = DockerManager()

# Or pass registry directly
docker = DockerManager(registry="us-central1-docker.pkg.dev/my-project/my-repo")

# Build an image
image_name = docker.build_image(
    component="frontend",
    context_path="./frontend",
    tag="v1.0.0"
)

# Build with custom Dockerfile name
image_name = docker.build_image(
    component="frontend",
    context_path="./frontend",
    tag="v1.0.0",
    dockerfile="Dockerfile.prod"  # Optional: defaults to "Dockerfile"
)

docker.tag_image(
    source_image=f"{docker.registry}/frontend",
    target_image="frontend",
    source_tag="v1.0.0",
    target_tag="latest"  # Optional: defaults to source_tag
)

# Load image into kind cluster (for local development)
docker.kind_load_image(
    image_name="frontend",
    tag="latest",
    cluster_name="my-cluster"  # Optional: auto-detected from kubectl context
)

# Push an image
docker.push_image(component="frontend", tag="v1.0.0")

# Build and push in one step
image_name = docker.build_and_push(
    component="backend",
    context_path="./backend",
    tag="latest",
    dockerfile="Dockerfile"  # Optional: custom Dockerfile name
)

Environment Variables

Required for Git Operations

  • STABLE_GIT_COMMIT - Current git commit hash
  • STABLE_GIT_BRANCH - Current git branch name
  • RENDERED_MANIFEST_DIR - Path to directory containing rendered manifests
  • MANIFEST_REPO_URL - Git repository URL for pushing manifests (optional if passed to push_manifests())

Optional for ArgoCD Applications

ArgoCD Application generation is opt-in and disabled by default. To enable:

  • ARGOCD_APP_REPO_URL - Repository URL for ArgoCD applications (or override application_repo_url()). Required if ArgoCD is enabled.
  • ARGOCD_APPS_SUBDIR - Subdirectory for applications (defaults to "apps")

You can also enable ArgoCD by overriding the enable_argocd() method in your template class to return True.

Required for Docker Operations (when pushing images)

  • DOCKER_REGISTRY - Full registry URL (e.g., "us-central1-docker.pkg.dev/my-project/my-repo")
  • GITHUB_REPOSITORY - GitHub repository name (optional)

Note: DOCKER_REGISTRY is only required when pushing images. Building images works without it (uses local image names).

Complete Example

Complete example using both HelmChart and KubernetesResource:

from kubeman import HelmChart, KubernetesResource, TemplateRegistry, GitManager, DockerManager

# Define a Helm chart for a third-party application
@TemplateRegistry.register
class PostgresChart(HelmChart):
    @property
    def name(self) -> str:
        return "postgres"

    @property
    def repository(self) -> dict:
        return {
            "type": "classic",
            "remote": "https://charts.bitnami.com/bitnami"
        }

    @property
    def namespace(self) -> str:
        return "database"

    @property
    def version(self) -> str:
        return "12.5.0"

    def generate_values(self) -> dict:
        return {
            "auth": {
                "postgresPassword": "changeme"
            },
            "persistence": {
                "enabled": True,
                "size": "10Gi"
            }
        }

    def enable_argocd(self) -> bool:
        """Enable ArgoCD Application generation (opt-in)"""
        return True

# Define custom Kubernetes resources for your application
@TemplateRegistry.register
class MyAppResources(KubernetesResource):
    @property
    def name(self) -> str:
        return "my-app"

    @property
    def namespace(self) -> str:
        return "production"

    def manifests(self) -> list[dict]:
        return [
            {
                "apiVersion": "v1",
                "kind": "ConfigMap",
                "metadata": {"name": "my-app-config", "namespace": "production"},
                "data": {"DATABASE_HOST": "postgres.database.svc.cluster.local"}
            },
            {
                "apiVersion": "apps/v1",
                "kind": "Deployment",
                "metadata": {"name": "my-app", "namespace": "production"},
                "spec": {
                    "replicas": 3,
                    "selector": {"matchLabels": {"app": "my-app"}},
                    "template": {
                        "metadata": {"labels": {"app": "my-app"}},
                        "spec": {
                            "containers": [{
                                "name": "my-app",
                                "image": "gcr.io/my-project/my-app:v1.0.0",
                                "envFrom": [{"configMapRef": {"name": "my-app-config"}}]
                            }]
                        }
                    }
                }
            }
        ]

# Option 1: Use CLI to render and apply
# Build steps execute automatically when templates are imported
# kubeman render --file kubeman.py
# kubeman apply --file kubeman.py

# Option 2: Skip build steps if images are already built
# kubeman render --file kubeman.py --skip-build
# kubeman apply --file kubeman.py --skip-build

# Option 3: Render programmatically
for template_class in TemplateRegistry.get_registered_templates():
    template = template_class()
    template.render()

# Push manifests to repository
git = GitManager()
git.push_manifests()

Publishing

This package is automatically published to PyPI via GitHub Actions when:

  1. A new release is published on GitHub
  2. Manual trigger via the GitHub Actions workflow

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kubeman-0.6.0.tar.gz (38.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kubeman-0.6.0-py3-none-any.whl (42.7 kB view details)

Uploaded Python 3

File details

Details for the file kubeman-0.6.0.tar.gz.

File metadata

  • Download URL: kubeman-0.6.0.tar.gz
  • Upload date:
  • Size: 38.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for kubeman-0.6.0.tar.gz
Algorithm Hash digest
SHA256 70af5728b7d253caa4f07db05a7c71eca5f49f910de35aceeb48cb42f4586544
MD5 575d6895b0845928af7e23455542f32a
BLAKE2b-256 f32f2115440c5c42881c915b9c3d9bdd5b4ec96a0a60d5f13f0385ccf24c5f72

See more details on using hashes here.

Provenance

The following attestation bundles were made for kubeman-0.6.0.tar.gz:

Publisher: publish.yml on jasonzh0/kubeman

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file kubeman-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: kubeman-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 42.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for kubeman-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c55cd2975c563f3373ba369c9de8c2fb2cefdea4f078dad662310faafc2ec7ee
MD5 d3f282d20cbf8297f67af0cf2956cda7
BLAKE2b-256 f5e18472d0c1a4253cb9a66cf9396af5b498274bd2ef2b7e54806a3bbe68f2d4

See more details on using hashes here.

Provenance

The following attestation bundles were made for kubeman-0.6.0-py3-none-any.whl:

Publisher: publish.yml on jasonzh0/kubeman

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page