Module: Pangea::Backend
- Defined in:
- lib/pangea/backend.rb
Overview
Backend selection + auto-discovery layer.
Per theory/MAGMA.md §II.11, Pangea supports two execution backends —tofu (historical default) and magma (pleme-io’s Rust-native executor). The selected backend consumes Pangea-rendered Terraform JSON and drives plan/apply/destroy.
Resolution priority:
1. --backend CLI flag
2. PANGEA_BACKEND env var
3. pangea.yml `backend:` field
4. default: tofu
Auto-discovery: each backend’s binary is probed via ‘<bin> capabilities` (magma) / `<bin> version -json` (tofu) on first use; the resulting capabilities manifest gates feature use with typed BackendIncompatible errors.
Defined Under Namespace
Classes: BackendIncompatible, BackendUnavailable, Capabilities
Class Method Summary collapse
-
.capabilities(name) ⇒ Capabilities
Probe a backend’s capabilities by spawning ‘<bin> capabilities` (magma) or `<bin> version -json` (tofu).
- .probe(name) ⇒ Object
- .probe_magma ⇒ Object
- .probe_tofu ⇒ Object
-
.resolve(explicit: nil, yml_config: nil) ⇒ String
Resolve backend choice from explicit > env > config > default.
-
.verify_compatible!(backend, requires) ⇒ Object
Verify a workspace’s requirements against the selected backend.
Class Method Details
.capabilities(name) ⇒ Capabilities
Probe a backend’s capabilities by spawning ‘<bin> capabilities` (magma) or `<bin> version -json` (tofu). Cached per-process.
87 88 89 90 |
# File 'lib/pangea/backend.rb', line 87 def self.capabilities(name) @capabilities ||= {} @capabilities[name] ||= probe(name) end |
.probe(name) ⇒ Object
92 93 94 95 96 97 98 99 |
# File 'lib/pangea/backend.rb', line 92 def self.probe(name) case name when 'magma' then probe_magma when 'tofu' then probe_tofu else raise ArgumentError, "unknown backend: #{name}" end end |
.probe_magma ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/pangea/backend.rb', line 101 def self.probe_magma out, status = Open3.capture2('magma', 'capabilities') raise BackendUnavailable, "magma binary not on PATH" unless status.success? m = JSON.parse(out) Capabilities.new( name: m['tool'], version: m['version'], supported_protocols: Array(m['supported_protocols']), input_formats: Array(m['input_formats']), subcommands: Array(m['subcommands']), supports_in_memory_pipeline: m['in_memory_pipeline_supported'] == true, supports_workspace_chains: m['workspace_chain_supported'] == true, raw: m, ) rescue Errno::ENOENT raise BackendUnavailable, "magma binary not on PATH" end |
.probe_tofu ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/pangea/backend.rb', line 120 def self.probe_tofu out, status = Open3.capture2('tofu', 'version', '-json') raise BackendUnavailable, "tofu binary not on PATH" unless status.success? m = JSON.parse(out) # tofu's version -json is minimal; we synthesize a Capabilities by # composing version + a compile-time table of "tofu has X # subcommand" plus capability-flags fixed to tofu's known shape. Capabilities.new( name: 'tofu', version: m['terraform_version'] || m['version'] || 'unknown', supported_protocols: %w[tfplugin5 tfplugin6], input_formats: %w[hcl2 terraform-json], subcommands: %w[init plan apply destroy state import workspace output show refresh taint force-unlock get fmt validate console], supports_in_memory_pipeline: false, supports_workspace_chains: false, raw: m, ) rescue Errno::ENOENT raise BackendUnavailable, "tofu binary not on PATH" end |
.resolve(explicit: nil, yml_config: nil) ⇒ String
Resolve backend choice from explicit > env > config > default.
67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/pangea/backend.rb', line 67 def self.resolve(explicit: nil, yml_config: nil) value = explicit || ENV['PANGEA_BACKEND'] || yml_config&.dig('backend') || yml_config&.dig(:backend) || 'tofu' value = value.to_s unless %w[tofu magma].include?(value) raise ArgumentError, "unknown backend #{value.inspect}; expected 'tofu' or 'magma'" end value end |
.verify_compatible!(backend, requires) ⇒ Object
Verify a workspace’s requirements against the selected backend. Raises BackendIncompatible with hints when something doesn’t match.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/pangea/backend.rb', line 150 def self.verify_compatible!(backend, requires) caps = capabilities(backend) if (fmt = requires[:input_format]) unless caps.input_formats.include?(fmt.to_s) raise BackendIncompatible.new( backend: backend, feature: "input format '#{fmt}'", alternatives: %w[tofu magma].reject { |b| b == backend }, hint: "Set `backend: magma` in pangea.yml if you need #{fmt}.", ) end end if requires[:feature] == :in_memory_pipeline && !caps.supports_in_memory_pipeline raise BackendIncompatible.new( backend: backend, feature: 'in-memory workspace chains', alternatives: ['magma'], hint: "Switch to backend=magma to use §II.9 in-memory chains.", ) end end |