container

package
v0.0.0-...-1b78e83 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 17, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

README

Container-Based IaC Module Execution

This package implements container-based execution for IaC modules (Pulumi, OpenTofu). Modules are packaged as self-contained container images that bundle the IaC code with its runtime, eliminating the need for Pulumi or OpenTofu to be installed on the deployment host.

Overview

The container approach provides several benefits:

  1. Portability: Deploy hosts only need Docker, not individual IaC tools
  2. Version Isolation: Each module can use its own IaC runtime version
  3. Reproducibility: Identical execution environment across all hosts
  4. Security: Modules run in isolated containers with controlled access

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        cldctl host                               │
│                                                                  │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐      │
│  │   Executor   │───▶│   Docker     │───▶│   Module     │      │
│  │              │    │   Engine     │    │   Container  │      │
│  └──────────────┘    └──────────────┘    └──────────────┘      │
│         │                                       │               │
│         │  input.json                           │               │
│         ▼                                       ▼               │
│  ┌──────────────┐                        ┌──────────────┐      │
│  │   Request    │                        │  Entrypoint  │      │
│  │   {action,   │                        │  (translates │      │
│  │    inputs}   │                        │   to IaC)    │      │
│  └──────────────┘                        └──────────────┘      │
│                                                 │               │
│                                                 ▼               │
│                                          ┌──────────────┐      │
│                                          │ Pulumi/Tofu  │      │
│                                          │   Runtime    │      │
│                                          └──────────────┘      │
└─────────────────────────────────────────────────────────────────┘

Module Container Interface

Input (JSON)

The executor writes a JSON request to /workspace/input.json:

{
  "action": "apply",
  "inputs": {
    "name": "my-database",
    "version": "16"
  },
  "environment": {
    "AWS_REGION": "us-east-1"
  },
  "stack_name": "prod-api-database"
}
Output (JSON)

The container writes a JSON response to /workspace/output.json:

{
  "success": true,
  "action": "apply",
  "outputs": {
    "host": { "value": "db.example.com" },
    "port": { "value": 5432 },
    "url": { "value": "postgresql://..." }
  }
}

Building Module Images

Automatic Detection

The builder detects the module type from the source directory:

  • Pulumi: Contains Pulumi.yaml
  • OpenTofu: Contains .tf files
Generated Dockerfiles
Pulumi (Node.js example)
FROM pulumi/pulumi-nodejs:latest
WORKDIR /app
COPY . .
RUN npm ci --production
COPY --from=cldctl-entrypoint /cldctl-entrypoint /cldctl-entrypoint
ENTRYPOINT ["/cldctl-entrypoint"]
OpenTofu
FROM ghcr.io/opentofu/opentofu:latest
WORKDIR /app
COPY . .
RUN tofu init -backend=false
COPY --from=cldctl-entrypoint /cldctl-entrypoint /cldctl-entrypoint
ENTRYPOINT ["/cldctl-entrypoint"]

Usage

Building a Module
builder, _ := container.NewBuilder()
defer builder.Close()

result, err := builder.Build(ctx, container.BuildOptions{
    ModuleDir: "./modules/postgres",
    Tag:       "myregistry.io/modules/postgres:v1.0.0",
})
Executing a Module
executor, _ := container.NewExecutor()
defer executor.Close()

response, err := executor.Execute(ctx, container.ExecuteOptions{
    Image: "myregistry.io/modules/postgres:v1.0.0",
    Request: &container.ModuleRequest{
        Action: "apply",
        Inputs: map[string]interface{}{
            "name": "my-db",
        },
    },
    Credentials: map[string]string{
        "AWS_ACCESS_KEY_ID":     "...",
        "AWS_SECRET_ACCESS_KEY": "...",
    },
})
Using the IaC Plugin
// Register automatically on import
import _ "github.com/davidthor/cldctl/pkg/iac/container"

// Get plugin from registry
plugin, _ := iac.DefaultRegistry.Get("container")

// Execute via standard interface
result, err := plugin.Apply(ctx, iac.RunOptions{
    ModuleSource: "myregistry.io/modules/postgres:v1.0.0",
    Inputs: map[string]interface{}{
        "name": "my-db",
    },
})

Supported Actions

Action Description
preview Show planned changes without applying
apply Create or update resources
destroy Remove all resources
refresh Read current state from infrastructure

Cloud Provider Credentials

The executor automatically passes through common cloud credentials:

AWS
  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_SESSION_TOKEN
  • AWS_REGION
GCP
  • GOOGLE_APPLICATION_CREDENTIALS
  • GOOGLE_PROJECT
  • GOOGLE_REGION
Azure
  • AZURE_SUBSCRIPTION_ID
  • AZURE_TENANT_ID
  • AZURE_CLIENT_ID
  • AZURE_CLIENT_SECRET
Kubernetes
  • KUBECONFIG

Entrypoint Program

The entrypoint/main.go program runs inside the container and:

  1. Reads the JSON request from /workspace/input.json
  2. Detects whether to use Pulumi or OpenTofu
  3. Translates inputs to the IaC tool's native format
  4. Executes the requested action
  5. Captures outputs and writes response to /workspace/output.json

Building the Entrypoint

The entrypoint needs to be compiled and included in module images:

cd pkg/iac/container/entrypoint
GOOS=linux GOARCH=amd64 go build -o cldctl-entrypoint .

For production, this is built as a multi-architecture binary and published as a scratch image that module Dockerfiles copy from.

Documentation

Overview

Package container implements container-based IaC module execution. This allows IaC modules (Pulumi, OpenTofu) to be packaged as self-contained container images that include both the IaC code and runtime, eliminating the need for Pulumi/OpenTofu to be installed on the deployment host.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BackendConfig

type BackendConfig struct {
	// Type is the backend type (e.g., "s3", "gcs", "azurerm", "local")
	Type string `json:"type"`

	// Config contains backend-specific configuration
	Config map[string]string `json:"config"`
}

BackendConfig configures state storage for the module.

type BuildOptions

type BuildOptions struct {
	// ModuleDir is the directory containing the IaC module
	ModuleDir string

	// ModuleType is the IaC framework (auto-detected if empty)
	ModuleType ModuleType

	// Tag is the image tag
	Tag string

	// Output for build logs
	Output io.Writer
}

BuildOptions configures module image building.

type BuildResult

type BuildResult struct {
	// Image is the built image tag
	Image string

	// Digest is the image digest
	Digest string

	// ModuleType is the detected/specified module type
	ModuleType ModuleType
}

BuildResult contains the result of a module build.

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

Builder builds container images for IaC modules.

func NewBuilder

func NewBuilder() (*Builder, error)

NewBuilder creates a new module builder.

func (*Builder) Build

func (b *Builder) Build(ctx context.Context, opts BuildOptions) (*BuildResult, error)

Build builds a container image for an IaC module.

func (*Builder) Close

func (b *Builder) Close() error

Close releases resources.

type ExecuteOptions

type ExecuteOptions struct {
	// Image is the container image to run
	Image string

	// Request is the module request
	Request *ModuleRequest

	// WorkDir is where to store temporary files
	WorkDir string

	// Credentials for cloud providers
	Credentials map[string]string

	// Stdout for streaming output
	Stdout io.Writer

	// Stderr for streaming errors
	Stderr io.Writer
}

ExecuteOptions configures module execution.

type Executor

type Executor struct {
	// contains filtered or unexported fields
}

Executor runs containerized IaC modules.

func NewExecutor

func NewExecutor() (*Executor, error)

NewExecutor creates a new container executor.

func (*Executor) Close

func (e *Executor) Close() error

Close releases resources.

func (*Executor) Execute

func (e *Executor) Execute(ctx context.Context, opts ExecuteOptions) (*ModuleResponse, error)

Execute runs a containerized module and returns the response.

type ModuleRequest

type ModuleRequest struct {
	// Action is the operation to perform: "preview", "apply", "destroy", "refresh"
	Action string `json:"action"`

	// Inputs are the module input values
	Inputs map[string]interface{} `json:"inputs"`

	// State is the current module state (for updates/destroys)
	State map[string]interface{} `json:"state,omitempty"`

	// Environment variables to set
	Environment map[string]string `json:"environment,omitempty"`

	// StackName for Pulumi or workspace name for OpenTofu
	StackName string `json:"stack_name,omitempty"`

	// Backend configuration for state storage
	Backend *BackendConfig `json:"backend,omitempty"`
}

ModuleRequest represents the input contract for a containerized module. This is passed to the container via a mounted JSON file.

type ModuleResponse

type ModuleResponse struct {
	// Success indicates whether the operation succeeded
	Success bool `json:"success"`

	// Action that was performed
	Action string `json:"action"`

	// Outputs from the module (after apply)
	Outputs map[string]OutputValue `json:"outputs,omitempty"`

	// State to persist (opaque to cldctl)
	State map[string]interface{} `json:"state,omitempty"`

	// Changes describes what changed (for preview)
	Changes []ResourceChange `json:"changes,omitempty"`

	// Error message if Success is false
	Error string `json:"error,omitempty"`

	// Logs from the operation
	Logs string `json:"logs,omitempty"`
}

ModuleResponse represents the output contract from a containerized module. The container writes this as JSON to a mounted output file.

type ModuleType

type ModuleType string

ModuleType identifies the IaC framework.

const (
	ModuleTypePulumi   ModuleType = "pulumi"
	ModuleTypeOpenTofu ModuleType = "opentofu"
)

func DetectModuleType

func DetectModuleType(dir string) (ModuleType, error)

DetectModuleType detects the IaC framework from a module directory.

type OutputValue

type OutputValue struct {
	Value     interface{} `json:"value"`
	Sensitive bool        `json:"sensitive,omitempty"`
}

OutputValue represents a module output.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin implements the IaC plugin interface using containerized modules.

func NewPlugin

func NewPlugin() (*Plugin, error)

NewPlugin creates a new container-based IaC plugin.

func NewPluginWithType

func NewPluginWithType(moduleType ModuleType) (*Plugin, error)

NewPluginWithType creates a plugin for a specific module type.

func (*Plugin) Apply

func (p *Plugin) Apply(ctx context.Context, opts iac.RunOptions) (*iac.ApplyResult, error)

Apply executes a module to create/update resources.

func (*Plugin) Destroy

func (p *Plugin) Destroy(ctx context.Context, opts iac.RunOptions) error

Destroy removes resources.

func (*Plugin) Import

func (p *Plugin) Import(ctx context.Context, opts iac.ImportOptions) (*iac.ImportResult, error)

Import delegates to the containerized module's import entrypoint.

func (*Plugin) Name

func (p *Plugin) Name() string

Name returns the plugin name.

func (*Plugin) Preview

func (p *Plugin) Preview(ctx context.Context, opts iac.RunOptions) (*iac.PreviewResult, error)

Preview shows what changes would be made.

func (*Plugin) Refresh

func (p *Plugin) Refresh(ctx context.Context, opts iac.RunOptions) (*iac.RefreshResult, error)

Refresh reads the current state.

type ResourceChange

type ResourceChange struct {
	// Resource identifier
	Resource string `json:"resource"`

	// Action: create, update, delete, replace, no-op
	Action string `json:"action"`

	// Before state (for updates/deletes)
	Before map[string]interface{} `json:"before,omitempty"`

	// After state (for creates/updates)
	After map[string]interface{} `json:"after,omitempty"`
}

ResourceChange describes a planned or executed change.

Directories

Path Synopsis
Package main implements the cldctl module entrypoint.
Package main implements the cldctl module entrypoint.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL