Blueprint Composition

Teabar blueprints can extend and combine other blueprints, enabling modular, reusable infrastructure definitions. This promotes DRY (Don’t Repeat Yourself) principles and standardization across your organization.

Why Composition?

Instead of duplicating configuration across blueprints:

❌ Without Composition:
┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐
│ cicd-workshop       │  │ k8s-training        │  │ security-lab        │
│ ─────────────────── │  │ ─────────────────── │  │ ─────────────────── │
│ • Network config    │  │ • Network config    │  │ • Network config    │
│ • Base cluster      │  │ • Base cluster      │  │ • Base cluster      │
│ • Monitoring        │  │ • Monitoring        │  │ • Monitoring        │
│ • GitLab            │  │ • Dashboard         │  │ • Falco             │
│ • Runners           │  │ • Exercises         │  │ • Trivy             │
└─────────────────────┘  └─────────────────────┘  └─────────────────────┘
       ↓ Duplication         ↓ Duplication           ↓ Duplication

Use composition for shared foundations:

✅ With Composition:
                    ┌─────────────────────┐
                    │ base-environment    │
                    │ ─────────────────── │
                    │ • Network config    │
                    │ • Base cluster      │
                    │ • Monitoring        │
                    └──────────┬──────────┘
                               │ extends
         ┌─────────────────────┼─────────────────────┐
         │                     │                     │
         ▼                     ▼                     ▼
┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│ cicd-workshop   │  │ k8s-training    │  │ security-lab    │
│ + GitLab        │  │ + Dashboard     │  │ + Falco         │
│ + Runners       │  │ + Exercises     │  │ + Trivy         │
└─────────────────┘  └─────────────────┘  └─────────────────┘

Extending Blueprints

Use extends to inherit from one or more base blueprints:

Basic Extension

apiVersion: teabar.dev/v1
kind: Blueprint
metadata:
  name: cicd-workshop
  version: 1.0.0

spec:
  # Extend a base blueprint
  extends:
    - name: teabar/base-kubernetes
      version: "1.x"
  
  # Add workshop-specific resources
  resources:
    helm:
      - name: gitlab
        chart: gitlab/gitlab
        namespace: gitlab

Multiple Inheritance

Extend multiple blueprints (processed in order):

spec:
  extends:
    # First: base infrastructure
    - name: teabar/base-kubernetes
      version: "1.x"
    
    # Second: monitoring stack
    - name: teabar/monitoring-stack
      version: "2.x"
    
    # Third: organization standards
    - name: acme/company-standards
      version: "1.x"

Version Constraints

Specify version constraints for stability:

ConstraintMeaningExample
1.0.0Exact versionOnly 1.0.0
1.xAny 1.*.*1.0.0, 1.5.2, 1.99.0
1.2.xAny 1.2.*1.2.0, 1.2.15
>=1.2.0Minimum version1.2.0 or higher
>=1.0.0 <2.0.0Range1.* versions only
extends:
  # Pin to exact version for production
  - name: teabar/base-kubernetes
    version: "1.5.2"
  
  # Allow minor updates
  - name: teabar/monitoring-stack
    version: "2.x"
  
  # Allow patches only
  - name: acme/company-standards
    version: "1.0.x"

Merge Behavior

Understanding how blueprints merge is key to effective composition.

Resource Merging

Resources are merged by type and name:

# Base blueprint: teabar/base-kubernetes
resources:
  clusters:
    - name: main
      nodes:
        workers: 2
      addons:
        metricsServer: true

# Your blueprint
extends:
  - name: teabar/base-kubernetes

resources:
  clusters:
    - name: main              # Same name = merge
      nodes:
        workers: 5            # Override workers
      addons:
        ingressNginx: true    # Add new addon
    
    - name: secondary         # Different name = add new
      nodes:
        workers: 3

Result:

resources:
  clusters:
    - name: main
      nodes:
        workers: 5            # Overridden
      addons:
        metricsServer: true   # From base
        ingressNginx: true    # Added
    
    - name: secondary         # New cluster
      nodes:
        workers: 3

Variable Merging

Variables merge by name, with child overriding parent:

# Base blueprint
variables:
  - name: cluster_version
    type: string
    default: "1.28"
  - name: worker_count
    type: integer
    default: 3

# Child blueprint
extends:
  - name: base

variables:
  - name: worker_count       # Override default
    default: 5
  - name: enable_monitoring  # Add new variable
    type: boolean
    default: true

Result:

variables:
  - name: cluster_version    # From base
    type: string
    default: "1.28"
  - name: worker_count       # Overridden
    type: integer
    default: 5
  - name: enable_monitoring  # Added
    type: boolean
    default: true

Deep Merging

Nested objects are deep-merged:

# Base
resources:
  helm:
    - name: app
      values:
        replicas: 2
        image:
          repository: nginx
          tag: "1.24"

# Child
resources:
  helm:
    - name: app
      values:
        replicas: 5           # Override
        image:
          tag: "1.25"         # Override nested
          pullPolicy: Always  # Add nested

Result:

resources:
  helm:
    - name: app
      values:
        replicas: 5
        image:
          repository: nginx   # Preserved from base
          tag: "1.25"         # Overridden
          pullPolicy: Always  # Added

List Behavior

Lists are replaced by default, not merged:

# Base
resources:
  dns:
    - name: api
      type: A
    - name: web
      type: A

# Child - REPLACES the entire list
resources:
  dns:
    - name: gitlab
      type: A

Result: Only gitlab DNS entry (api and web are gone).

To preserve base list items, reference them explicitly or use named resources.


Override Strategies

Complete Override

Replace an entire resource:

extends:
  - name: teabar/base-kubernetes

resources:
  clusters:
    - name: main
      _override: replace    # Completely replace, don't merge
      provider: aws
      type: eks
      nodes:
        workers: 10

Selective Override

Override specific fields only:

extends:
  - name: teabar/base-kubernetes

resources:
  clusters:
    - name: main
      # Only these fields are overridden, rest inherited
      nodes:
        workers: 10

Disable Inherited Resources

Remove an inherited resource:

extends:
  - name: teabar/full-stack

resources:
  helm:
    - name: prometheus
      _enabled: false    # Don't deploy prometheus from base

Creating Base Blueprints

Organization Standards Blueprint

Create a blueprint enforcing your organization’s standards:

apiVersion: teabar.dev/v1
kind: Blueprint
metadata:
  name: acme-standards
  version: 1.0.0
  description: ACME Corp standard environment configuration

spec:
  # Standard variables all environments should have
  variables:
    - name: cost_center
      type: string
      required: true
      ui:
        label: "Cost Center"
        description: "Required for billing"
    
    - name: owner_email
      type: string
      required: true
      constraints:
        pattern: ".*@acme\.com$"
      ui:
        label: "Owner Email"

  # Standard environment settings
  environment:
    # All environments must have TTL
    ttl: 72h
    
    # Standard labels
    labels:
      company: acme
      managed-by: teabar

  # Standard resources
  resources:
    # Company CA certificate
    secrets:
      - name: acme-ca
        type: external
        spec:
          provider: vault
          path: pki/ca
    
    # Standard network policies
    manifests:
      - name: default-network-policy
        template: |
          apiVersion: networking.k8s.io/v1
          kind: NetworkPolicy
          metadata:
            name: default-deny-ingress
          spec:
            podSelector: {}
            policyTypes:
              - Ingress

Reusable Component Blueprint

Create blueprints for common components:

apiVersion: teabar.dev/v1
kind: Blueprint
metadata:
  name: gitlab-component
  version: 1.0.0
  description: GitLab CE deployment component

spec:
  variables:
    - name: gitlab_version
      type: string
      default: "16.8"
    - name: registry_enabled
      type: boolean
      default: true
    - name: runner_count
      type: integer
      default: 2

  resources:
    helm:
      - name: gitlab
        chart: gitlab/gitlab
        version: "7.x"
        namespace: gitlab
        values:
          global:
            edition: ce
            hosts:
              domain: "{{ .Environment.Domain }}"
            registry:
              enabled: "{{ .Variables.registry_enabled }}"
    
    # GitLab runners
    manifests:
      - name: gitlab-runners
        count: "{{ .Variables.runner_count }}"
        template: |
          # Runner configuration...

Composition Patterns

Layer Pattern

Stack blueprints in layers:

# infrastructure-layer.yaml
spec:
  resources:
    clusters:
      - name: main
        # Base cluster configuration

# platform-layer.yaml  
spec:
  extends:
    - name: org/infrastructure-layer
  resources:
    helm:
      - name: argocd
      - name: cert-manager

# application-layer.yaml
spec:
  extends:
    - name: org/platform-layer
  resources:
    helm:
      - name: my-app

Mixin Pattern

Combine feature blueprints:

spec:
  extends:
    # Base infrastructure
    - name: teabar/base-kubernetes
    
    # Add monitoring (mixin)
    - name: teabar/monitoring-mixin
    
    # Add logging (mixin)
    - name: teabar/logging-mixin
    
    # Add security scanning (mixin)
    - name: teabar/security-mixin

Profile Pattern

Create profiles for different scales:

# small-profile.yaml
spec:
  variables:
    - name: worker_count
      default: 2
    - name: worker_size
      default: cx21

# large-profile.yaml
spec:
  variables:
    - name: worker_count
      default: 10
    - name: worker_size
      default: cx41

# workshop.yaml
spec:
  extends:
    - name: org/base-workshop
    - name: org/large-profile  # Use large profile

Viewing Resolved Blueprints

See the final merged result:

# Render with all extensions resolved
teactl blueprint render my-blueprint --resolve

# Show inheritance tree
teactl blueprint info my-blueprint --show-extends

# Output:
# my-blueprint v1.0.0
# └── extends: teabar/base-kubernetes v1.5.0
#     └── extends: teabar/base-network v1.2.0
# └── extends: acme/company-standards v1.0.0

# Diff against base
teactl blueprint diff my-blueprint --base teabar/base-kubernetes

Publishing Reusable Blueprints

Organization Blueprints

Share within your organization:

# Push to organization registry
teactl blueprint push -f base-standards.yaml --org acme

# Others can now extend it
# extends:
#   - name: acme/base-standards

Public Catalog

Share with the Teabar community (Pro feature):

# Publish to public catalog
teactl blueprint publish -f my-blueprint.yaml 
  --public 
  --description "Reusable GitLab CI/CD setup"

Best Practices

1. Keep Base Blueprints Stable

Base blueprints should change infrequently:

# Use semantic versioning
metadata:
  version: 1.0.0  # Major.Minor.Patch

# Document breaking changes
# v2.0.0 - Changed cluster naming convention

2. Use Version Constraints Wisely

extends:
  # Development: allow updates
  - name: teabar/base-kubernetes
    version: "1.x"
  
  # Production: pin exact versions
  - name: teabar/base-kubernetes
    version: "1.5.2"

3. Document Override Points

# Base blueprint with clear extension points
variables:
  # Override this to customize cluster size
  - name: worker_count
    type: integer
    default: 3
    ui:
      description: "Number of worker nodes. Override in extending blueprint."

resources:
  helm:
    # Override 'values' in extending blueprint to customize
    - name: nginx-ingress
      chart: ingress-nginx/ingress-nginx
      values:
        # Default values - extend to customize
        controller:
          replicaCount: 2

4. Test Inheritance Chains

# Validate the resolved blueprint
teactl blueprint validate -f child.yaml --resolve

# Test with actual deployment
teactl env create --blueprint child --name test-inheritance --dry-run

5. Avoid Deep Inheritance

Keep inheritance chains shallow (2-3 levels max):

✅ Good: base → specialized → final
❌ Avoid: base → layer1 → layer2 → layer3 → layer4 → final

Troubleshooting

“Blueprint not found” in extends

# Check if base blueprint exists
teactl blueprint get teabar/base-kubernetes

# Check version availability
teactl blueprint versions teabar/base-kubernetes

Unexpected merge results

# See exactly what's being merged
teactl blueprint render my-blueprint --resolve --verbose

# Show merge operations
teactl blueprint render my-blueprint --show-merge-log

Circular dependency

Error: circular dependency detected: a → b → c → a

Restructure to eliminate cycles. Use composition for shared components instead of circular extends.


Next Steps

ende