Skip to content

consider CUE manifests #99

@ripienaar

Description

@ripienaar

The manifests as they stand function but its a bit janky to support loops and arrays and all that.

CUE might be better than the mix of yaml+jet

Claude came up with some examples, untested but I like some of the abilities here. Could support both.

// Example CCM Manifest using CUE
// This demonstrates how to define and validate manifest configurations

package manifest

// Import and use the schema definition
import "choria.io/ccm/internal/fs/schemas:manifest"

// =============================================================================
// Comprehensions: Generate multiple resources from arrays
// =============================================================================

// Define lists of items to manage
_packages: ["vim", "git", "curl", "wget", "jq"]
_services: ["nginx", "php-fpm", "redis"]

// Generate package resources from the list - all with ensure: present
generatedPackages: #Manifest & {
	ccm: resources: [
		{package: [
			for pkg in _packages {
				(pkg): {ensure: "present"}
			},
		]},
	]
}

// Generate services - all running and enabled
generatedServices: #Manifest & {
	ccm: resources: [
		{service: [
			for svc in _services {
				(svc): {
					ensure: "running"
					enable: true
				}
			},
		]},
	]
}

// More complex: Different ensure states per package
_packageStates: {
	vim:    "present"
	git:    "latest"
	curl:   "present"
	telnet: "absent" // remove this one
}

mixedPackages: #Manifest & {
	ccm: resources: [
		{package: [
			for pkg, state in _packageStates {
				(pkg): {ensure: state}
			},
		]},
	]
}

// Generate files from a map of paths to content
_configFiles: {
	"/etc/myapp/app.conf": {
		content: "debug=false"
		mode:    "0644"
	}
	"/etc/myapp/secrets.conf": {
		content: "api_key=xxx"
		mode:    "0600"
	}
}

generatedFiles: #Manifest & {
	ccm: resources: [
		for path, props in _configFiles {
			file: {
				name:    path
				ensure:  "present"
				content: props.content
				mode:    props.mode
				owner:   "root"
				group:   "root"
			}
		},
	]
}

// Combine packages and services in one manifest using spread
_devPackages: ["vim", "git", "tmux"]
_buildPackages: ["gcc", "make", "cmake"]

combinedManifest: #Manifest & {
	ccm: {
		fail_on_error: true
		resources: [
			{package: [
				for pkg in _devPackages + _buildPackages {
					(pkg): {ensure: "present"}
				},
			]},
		]
	}
}

// Conditional resources using if
_enableRedis: true
_enablePostgres: false

conditionalServices: #Manifest & {
	ccm: resources: [
		{service: [
			{nginx: {ensure: "running", enable: true}},
			if _enableRedis {
				{redis: {ensure: "running", enable: true}}
			},
			if _enablePostgres {
				{postgresql: {ensure: "running", enable: true}}
			},
		]},
	]
}

// =============================================================================
// Traditional Examples
// =============================================================================

// Example 1: A basic manifest with packages and services
basicManifest: #Manifest & {
	data: {
		app_name: "myapp"
		app_user: "appuser"
		config_dir: "/etc/myapp"
	}

	hierarchy: {
		order: [
			"os:{{ lookup('facts.host.info.platformFamily') }}",
			"common",
		]
		merge: "deep"
	}

	overrides: {
		"os:debian": {
			package_manager: "apt"
		}
		"os:redhat": {
			package_manager: "yum"
		}
	}

	ccm: {
		fail_on_error: true
		resources: [
			// Named format: array of packages with name as key
			{package: [
				{vim: {ensure: "present"}},
				{git: {ensure: "latest"}},
			]},
			// Direct format: single package with name property
			{package: {
				name: "curl"
				ensure: "present"
			}},
			// Service with subscription
			{service: {
				name: "nginx"
				ensure: "running"
				enable: true
				subscribe: ["file#/etc/nginx/nginx.conf"]
			}},
			// File with content
			{file: {
				name: "/etc/myapp/config.yaml"
				ensure: "present"
				content: """
					app:
					  name: myapp
					  debug: false
					"""
				owner: "root"
				group: "root"
				mode: "0644"
			}},
			// Exec with health check
			{exec: {
				name: "/usr/local/bin/setup.sh"
				cwd: "/opt/myapp"
				creates: "/opt/myapp/.setup_complete"
				timeout: "5m"
				require: ["package#curl"]
				health_checks: [{
					command: "/usr/local/bin/check_setup.sh"
					timeout: "30s"
					tries: 3
					try_sleep: "5s"
					format: "nagios"
				}]
			}},
		]
	}
}

// Example 2: Minimal manifest with just packages (named format)
minimalManifest: #Manifest & {
	ccm: resources: [
		{package: [
			{vim: {ensure: "present"}},
		]},
	]
}

// Example 3: Using variables and templates in a realistic scenario
webServerManifest: #Manifest & {
	data: {
		http_port: 8080
		document_root: "/var/www/html"
	}

	ccm: {
		fail_on_error: true
		resources: [
			{package: [
				{nginx: {ensure: "present"}},
				{"php-fpm": {ensure: "present"}},
			]},
			{file: [
				{"/var/www/html": {
					ensure: "directory"
					owner: "www-data"
					group: "www-data"
					mode: "0755"
				}},
			]},
			{service: [
				{nginx: {
					ensure: "running"
					enable: true
					subscribe: ["file#/etc/nginx/nginx.conf"]
				}},
				{"php-fpm": {
					ensure: "running"
					enable: true
				}},
			]},
		]
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions