empeira 0.1.0.pre.alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +661 -0
  3. data/README.md +150 -0
  4. data/bin/empeira +18 -0
  5. data/config/defaults.yaml +229 -0
  6. data/docs/addons.md +331 -0
  7. data/docs/architecture.md +277 -0
  8. data/docs/configuration.md +1204 -0
  9. data/docs/development.md +472 -0
  10. data/docs/hiera-and-eyaml.md +361 -0
  11. data/docs/installation.md +280 -0
  12. data/docs/networking.md +362 -0
  13. data/docs/nodes.md +642 -0
  14. data/docs/proxy.md +136 -0
  15. data/docs/troubleshooting.md +361 -0
  16. data/lib/empeira/addons/environment.rb +82 -0
  17. data/lib/empeira/addons/error.rb +14 -0
  18. data/lib/empeira/addons/installation.rb +119 -0
  19. data/lib/empeira/addons/manifest.rb +112 -0
  20. data/lib/empeira/addons/service.rb +50 -0
  21. data/lib/empeira/addons/storage.rb +94 -0
  22. data/lib/empeira/agent/acquisition.rb +120 -0
  23. data/lib/empeira/agent/artifact.rb +41 -0
  24. data/lib/empeira/agent/authentication.rb +122 -0
  25. data/lib/empeira/agent/cache.rb +122 -0
  26. data/lib/empeira/agent/cache_storage.rb +82 -0
  27. data/lib/empeira/agent/download.rb +84 -0
  28. data/lib/empeira/agent/native_commands.rb +84 -0
  29. data/lib/empeira/agent/release_sources.rb +64 -0
  30. data/lib/empeira/agent/target.rb +66 -0
  31. data/lib/empeira/agent/versions.rb +29 -0
  32. data/lib/empeira/application.rb +86 -0
  33. data/lib/empeira/build.json +5 -0
  34. data/lib/empeira/build_info.rb +46 -0
  35. data/lib/empeira/cli/addons.rb +45 -0
  36. data/lib/empeira/cli/base.rb +73 -0
  37. data/lib/empeira/cli/completion.rb +42 -0
  38. data/lib/empeira/cli/config.rb +22 -0
  39. data/lib/empeira/cli/main.rb +114 -0
  40. data/lib/empeira/cli/node.rb +87 -0
  41. data/lib/empeira/cli/progress.rb +169 -0
  42. data/lib/empeira/cli/proxy.rb +36 -0
  43. data/lib/empeira/cli/update.rb +23 -0
  44. data/lib/empeira/completion/addons.rb +93 -0
  45. data/lib/empeira/completion/bash.rb +62 -0
  46. data/lib/empeira/completion/catalog.rb +170 -0
  47. data/lib/empeira/configuration/additional_services.rb +74 -0
  48. data/lib/empeira/configuration/addons.rb +85 -0
  49. data/lib/empeira/configuration/agent_schema.rb +163 -0
  50. data/lib/empeira/configuration/agent_values.rb +54 -0
  51. data/lib/empeira/configuration/bootstrap_guests.rb +42 -0
  52. data/lib/empeira/configuration/command_mocks.rb +65 -0
  53. data/lib/empeira/configuration/display.rb +20 -0
  54. data/lib/empeira/configuration/dns_rewrites.rb +58 -0
  55. data/lib/empeira/configuration/document.rb +67 -0
  56. data/lib/empeira/configuration/eyaml.rb +46 -0
  57. data/lib/empeira/configuration/hiera_modulepath.rb +40 -0
  58. data/lib/empeira/configuration/hiera_mount_schema.rb +62 -0
  59. data/lib/empeira/configuration/hiera_mounts.rb +83 -0
  60. data/lib/empeira/configuration/image_schema.rb +68 -0
  61. data/lib/empeira/configuration/loader.rb +70 -0
  62. data/lib/empeira/configuration/merge.rb +48 -0
  63. data/lib/empeira/configuration/network_redirects.rb +73 -0
  64. data/lib/empeira/configuration/node_images.rb +64 -0
  65. data/lib/empeira/configuration/schema.rb +215 -0
  66. data/lib/empeira/configuration/server_mounts.rb +106 -0
  67. data/lib/empeira/configuration/service_configuration.rb +40 -0
  68. data/lib/empeira/configuration/ssh_preferences.rb +83 -0
  69. data/lib/empeira/configuration/user.rb +46 -0
  70. data/lib/empeira/configuration/vm_interfaces.rb +147 -0
  71. data/lib/empeira/control_plane/additional_configurations.rb +106 -0
  72. data/lib/empeira/control_plane/browser.rb +73 -0
  73. data/lib/empeira/control_plane/controller.rb +763 -0
  74. data/lib/empeira/control_plane/discovery.rb +69 -0
  75. data/lib/empeira/control_plane/environment.rb +101 -0
  76. data/lib/empeira/control_plane/files.rb +263 -0
  77. data/lib/empeira/control_plane/health.rb +104 -0
  78. data/lib/empeira/control_plane/plan.rb +356 -0
  79. data/lib/empeira/control_plane/storage.rb +48 -0
  80. data/lib/empeira/errors.rb +9 -0
  81. data/lib/empeira/execution/console.rb +45 -0
  82. data/lib/empeira/execution/diagnostics.rb +210 -0
  83. data/lib/empeira/execution/launch_error.rb +14 -0
  84. data/lib/empeira/execution/result.rb +11 -0
  85. data/lib/empeira/execution/runner.rb +235 -0
  86. data/lib/empeira/execution/terminal.rb +35 -0
  87. data/lib/empeira/hostname_pattern.rb +14 -0
  88. data/lib/empeira/images/configuration.rb +44 -0
  89. data/lib/empeira/images/identity.rb +30 -0
  90. data/lib/empeira/images/recipe.rb +22 -0
  91. data/lib/empeira/images/reference.rb +43 -0
  92. data/lib/empeira/images/source.rb +19 -0
  93. data/lib/empeira/immutable.rb +19 -0
  94. data/lib/empeira/infrastructure/cleanup.rb +328 -0
  95. data/lib/empeira/infrastructure/definition.rb +59 -0
  96. data/lib/empeira/infrastructure/owned_files.rb +63 -0
  97. data/lib/empeira/infrastructure/recovery_inventory.rb +53 -0
  98. data/lib/empeira/infrastructure/service.rb +237 -0
  99. data/lib/empeira/infrastructure/status.rb +73 -0
  100. data/lib/empeira/infrastructure/store.rb +274 -0
  101. data/lib/empeira/modules/events.rb +67 -0
  102. data/lib/empeira/modules/git_safety.rb +60 -0
  103. data/lib/empeira/modules/host_git.rb +91 -0
  104. data/lib/empeira/modules/image.rb +27 -0
  105. data/lib/empeira/modules/installer.rb +67 -0
  106. data/lib/empeira/modules/request.rb +68 -0
  107. data/lib/empeira/modules/state.rb +87 -0
  108. data/lib/empeira/modules/storage.rb +35 -0
  109. data/lib/empeira/network/bootstrap_proxy.rb +157 -0
  110. data/lib/empeira/network/definition.rb +61 -0
  111. data/lib/empeira/network/direct_egress.rb +133 -0
  112. data/lib/empeira/network/egress.rb +27 -0
  113. data/lib/empeira/network/gateway.rb +79 -0
  114. data/lib/empeira/network/host_policy.rb +53 -0
  115. data/lib/empeira/network/naming.rb +28 -0
  116. data/lib/empeira/network/peer/adapter_image.rb +32 -0
  117. data/lib/empeira/network/peer/allocation.rb +45 -0
  118. data/lib/empeira/network/peer/attachment.rb +27 -0
  119. data/lib/empeira/network/peer/backend.rb +108 -0
  120. data/lib/empeira/network/peer/channel.rb +133 -0
  121. data/lib/empeira/network/peer/channel_server.rb +71 -0
  122. data/lib/empeira/network/peer/docker_adapter.rb +165 -0
  123. data/lib/empeira/network/peer/helper_security.rb +40 -0
  124. data/lib/empeira/network/peer/layout.rb +55 -0
  125. data/lib/empeira/network/peer/linux_podman.rb +114 -0
  126. data/lib/empeira/network/peer/podman_machine.rb +112 -0
  127. data/lib/empeira/network/policy.rb +39 -0
  128. data/lib/empeira/network/proxy_bindings.rb +52 -0
  129. data/lib/empeira/network/proxy_policy.rb +64 -0
  130. data/lib/empeira/network/redirects.rb +89 -0
  131. data/lib/empeira/network/resource.rb +50 -0
  132. data/lib/empeira/node/agent_installer.rb +31 -0
  133. data/lib/empeira/node/agent_package.rb +125 -0
  134. data/lib/empeira/node/agent_repository.rb +160 -0
  135. data/lib/empeira/node/apt_configuration.rb +85 -0
  136. data/lib/empeira/node/bootstrap.rb +17 -0
  137. data/lib/empeira/node/certificates.rb +98 -0
  138. data/lib/empeira/node/command_mock.rb +38 -0
  139. data/lib/empeira/node/command_mocks.rb +114 -0
  140. data/lib/empeira/node/container.rb +134 -0
  141. data/lib/empeira/node/container_agent_bootstrap.rb +43 -0
  142. data/lib/empeira/node/container_bootstrap.rb +52 -0
  143. data/lib/empeira/node/container_guest.rb +42 -0
  144. data/lib/empeira/node/container_lifecycle.rb +145 -0
  145. data/lib/empeira/node/container_preparation.rb +144 -0
  146. data/lib/empeira/node/container_state.rb +129 -0
  147. data/lib/empeira/node/definition.rb +20 -0
  148. data/lib/empeira/node/diagnostic_command.rb +19 -0
  149. data/lib/empeira/node/dnf_agent_repository.rb +202 -0
  150. data/lib/empeira/node/external_fact.rb +21 -0
  151. data/lib/empeira/node/image.rb +38 -0
  152. data/lib/empeira/node/interactive_tools.rb +33 -0
  153. data/lib/empeira/node/interface.rb +18 -0
  154. data/lib/empeira/node/inventory.rb +143 -0
  155. data/lib/empeira/node/package_bootstrap.rb +129 -0
  156. data/lib/empeira/node/package_proxy.rb +102 -0
  157. data/lib/empeira/node/package_sources.rb +19 -0
  158. data/lib/empeira/node/puppet_command.rb +12 -0
  159. data/lib/empeira/node/rpm_configuration.rb +62 -0
  160. data/lib/empeira/node/run_request.rb +52 -0
  161. data/lib/empeira/node/runtime_proxy.rb +86 -0
  162. data/lib/empeira/node/service.rb +72 -0
  163. data/lib/empeira/node/ssh_client.rb +25 -0
  164. data/lib/empeira/node/ssh_credentials.rb +78 -0
  165. data/lib/empeira/node/ssh_endpoint.rb +58 -0
  166. data/lib/empeira/node/ssh_identity.rb +37 -0
  167. data/lib/empeira/node/user_ssh.rb +60 -0
  168. data/lib/empeira/node/vm.rb +89 -0
  169. data/lib/empeira/node/vm_bootstrap.rb +53 -0
  170. data/lib/empeira/node/vm_lifecycle.rb +212 -0
  171. data/lib/empeira/node/vm_preflight.rb +37 -0
  172. data/lib/empeira/node/vm_preparation.rb +161 -0
  173. data/lib/empeira/node/vm_state.rb +161 -0
  174. data/lib/empeira/platform/facts.rb +66 -0
  175. data/lib/empeira/platform/kvm.rb +91 -0
  176. data/lib/empeira/platform/locations.rb +51 -0
  177. data/lib/empeira/platform/resolvers.rb +85 -0
  178. data/lib/empeira/platform/routes.rb +50 -0
  179. data/lib/empeira/progress.rb +70 -0
  180. data/lib/empeira/project_root.rb +47 -0
  181. data/lib/empeira/providers/lifecycle.rb +96 -0
  182. data/lib/empeira/providers/registry.rb +21 -0
  183. data/lib/empeira/providers/resource.rb +32 -0
  184. data/lib/empeira/providers.rb +55 -0
  185. data/lib/empeira/runtime/agent_helper.rb +34 -0
  186. data/lib/empeira/runtime/container.rb +184 -0
  187. data/lib/empeira/runtime/diagnostic_execution.rb +52 -0
  188. data/lib/empeira/runtime/docker.rb +87 -0
  189. data/lib/empeira/runtime/image_metadata.rb +83 -0
  190. data/lib/empeira/runtime/interface.rb +8 -0
  191. data/lib/empeira/runtime/mount_probe.rb +57 -0
  192. data/lib/empeira/runtime/podman.rb +122 -0
  193. data/lib/empeira/runtime/recipe_build.rb +103 -0
  194. data/lib/empeira/runtime/registry_diagnostics.rb +23 -0
  195. data/lib/empeira/runtime/service_arguments.rb +55 -0
  196. data/lib/empeira/runtime/service_images.rb +118 -0
  197. data/lib/empeira/runtime/service_inspection.rb +159 -0
  198. data/lib/empeira/runtime/services.rb +120 -0
  199. data/lib/empeira/runtime/update_helper.rb +97 -0
  200. data/lib/empeira/runtime/workspace_network.rb +69 -0
  201. data/lib/empeira/server/environment_configuration.rb +53 -0
  202. data/lib/empeira/server/http.rb +38 -0
  203. data/lib/empeira/server/puppetdb_runtime.rb +74 -0
  204. data/lib/empeira/server/relay_certificate.rb +76 -0
  205. data/lib/empeira/server/runtime.rb +90 -0
  206. data/lib/empeira/services/definition.rb +42 -0
  207. data/lib/empeira/updates/images.rb +104 -0
  208. data/lib/empeira/updates/modules.rb +45 -0
  209. data/lib/empeira/updates/service.rb +45 -0
  210. data/lib/empeira/version_requirement.rb +23 -0
  211. data/lib/empeira/vm/agent.rb +66 -0
  212. data/lib/empeira/vm/bootstrap_requirements.rb +69 -0
  213. data/lib/empeira/vm/certificate_runtime.rb +30 -0
  214. data/lib/empeira/vm/cloud_init.rb +171 -0
  215. data/lib/empeira/vm/disk.rb +136 -0
  216. data/lib/empeira/vm/guest.rb +123 -0
  217. data/lib/empeira/vm/guest_channel.rb +110 -0
  218. data/lib/empeira/vm/guest_network.rb +94 -0
  219. data/lib/empeira/vm/guest_output.rb +52 -0
  220. data/lib/empeira/vm/guest_shell.rb +79 -0
  221. data/lib/empeira/vm/guest_upload.rb +43 -0
  222. data/lib/empeira/vm/image_cache.rb +77 -0
  223. data/lib/empeira/vm/image_source.rb +91 -0
  224. data/lib/empeira/vm/interface.rb +8 -0
  225. data/lib/empeira/vm/interface_ownership.rb +124 -0
  226. data/lib/empeira/vm/interface_routes.rb +101 -0
  227. data/lib/empeira/vm/interfaces.rb +240 -0
  228. data/lib/empeira/vm/management.rb +73 -0
  229. data/lib/empeira/vm/prerequisites.rb +42 -0
  230. data/lib/empeira/vm/qemu.rb +83 -0
  231. data/lib/empeira/vm/qemu_runtime.rb +404 -0
  232. data/lib/empeira/vm/removal.rb +30 -0
  233. data/lib/empeira/vm/root_disk.rb +92 -0
  234. data/lib/empeira/vm/session_lock.rb +56 -0
  235. data/lib/empeira/vm/system_ssh.rb +29 -0
  236. data/lib/empeira/workspace.rb +42 -0
  237. data/lib/empeira.rb +209 -0
  238. data/resources/browser/relay.rb +22 -0
  239. data/resources/gateway/Containerfile +8 -0
  240. data/resources/gateway/bridge.rb +66 -0
  241. data/resources/gateway/configure.rb +323 -0
  242. data/resources/modules/Containerfile +15 -0
  243. data/resources/modules/Gemfile +5 -0
  244. data/resources/modules/Gemfile.lock +105 -0
  245. data/resources/modules/worker.rb +161 -0
  246. data/resources/network/adapter/Containerfile +13 -0
  247. data/resources/network/adapter/bridge_linux.go +51 -0
  248. data/resources/network/adapter/framing.go +45 -0
  249. data/resources/network/adapter/framing_test.go +53 -0
  250. data/resources/network/adapter/go.mod +3 -0
  251. data/resources/network/adapter/main_linux.go +273 -0
  252. data/resources/network/channel.rb +18 -0
  253. data/resources/network/ssh_connect.rb +32 -0
  254. data/resources/nodes/apt_sources.rb +66 -0
  255. data/resources/nodes/debian/Containerfile +10 -0
  256. data/resources/nodes/diagnostic_exec.rb +109 -0
  257. data/resources/nodes/interactive_tools.rb +139 -0
  258. data/resources/nodes/managed_file.rb +91 -0
  259. data/resources/nodes/management/guest_agent.py +415 -0
  260. data/resources/nodes/management/setup.sh +20 -0
  261. data/resources/nodes/management/shell_rc.sh +12 -0
  262. data/resources/nodes/management/system_account.sh +24 -0
  263. data/resources/nodes/management/system_home.sh +15 -0
  264. data/resources/nodes/rpm/Containerfile +10 -0
  265. data/resources/nodes/runtime_proxy.rb +244 -0
  266. data/resources/nodes/ssh/proxy.rb +16 -0
  267. data/resources/nodes/ssh/sshd_config +15 -0
  268. data/resources/nodes/ssh/start +6 -0
  269. data/resources/proxy/Containerfile +10 -0
  270. data/resources/relay/Containerfile +5 -0
  271. data/resources/relay/puppetdb.rb +81 -0
  272. data/resources/relay/server_environment.rb +10 -0
  273. data/resources/relay/server_start.sh +41 -0
  274. metadata +326 -0
data/docs/proxy.md ADDED
@@ -0,0 +1,136 @@
1
+ # Proxy policy
2
+
3
+ Normal external HTTP/HTTPS access stays behind Squid. Enabling the workspace proxy
4
+ makes it available to every node; global and hostname rules remain the destination
5
+ allowlist:
6
+
7
+ ```yaml
8
+ proxy:
9
+ enabled: true
10
+ global:
11
+ - forgeapi.puppet.com
12
+ rules:
13
+ - hosts: ["lab-*"]
14
+ allow: ["*.github.com"]
15
+ - hosts: ["*-web-*"]
16
+ allow: [github.com, nginx.org]
17
+ ```
18
+
19
+ ```console
20
+ empeira up
21
+ empeira node run lab-web-1-01.example.net --provider container
22
+ empeira proxy show lab-web-1-01.example.net
23
+ ```
24
+
25
+ The diagnostic prints:
26
+
27
+ ```text
28
+ Effective proxy policy for lab-web-1-01.example.net
29
+ All workspace nodes may use this policy after 'empeira up' reconciles the proxy.
30
+ Global:
31
+ forgeapi.puppet.com
32
+ Matched rules:
33
+ lab-*
34
+ *-web-*
35
+ Allowed:
36
+ forgeapi.puppet.com
37
+ *.github.com
38
+ github.com
39
+ nginx.org
40
+ Managed bootstrap destinations are separate from this normal policy.
41
+ ```
42
+
43
+ The diagnostic resolves desired policy even for a hostname that does not exist.
44
+ It reports when the normal proxy is disabled and does not claim that live
45
+ infrastructure has already been reconciled.
46
+
47
+ ## Matching and destinations
48
+
49
+ Defaults are `proxy.global: []` and `proxy.rules: []`. Every node receives
50
+ all global destinations plus `allow` from **every** matching rule. Duplicate entries
51
+ are removed while preserving configuration order. A rule matches when any of its
52
+ `hosts` patterns matches the complete lowercase hostname. Patterns must be lowercase.
53
+ `*` matches any sequence, including dots, and `?` matches one character. Examples
54
+ include `*-web-*`, `lab-*` and `*-db-??.*`. There is no regex, first-match,
55
+ last-match or deny-rule syntax. Container and VM providers use the same resolver.
56
+
57
+ Destination rules are separate from hostname globs:
58
+
59
+ | Entry | Meaning |
60
+ | --- | --- |
61
+ | `github.com` | Exact DNS destination |
62
+ | `*.github.com` | Squid domain family, including the apex and its subdomains |
63
+ | `https://github.com`, `github.com/path`, `github.com:443` | Invalid |
64
+ | IP literals, whitespace, internal Empeira names | Invalid |
65
+
66
+ Only HTTP port 80 and HTTPS CONNECT port 443 are allowed. Allowed DNS destinations
67
+ are reachable regardless of their target IP range, including private, loopback,
68
+ link-local and reserved addresses. Domains without a matching allow rule remain
69
+ denied. There is no TLS interception. Server-side outbound traffic gets
70
+ `proxy.global` only; node hostname rules do not expand server access.
71
+
72
+ ## Node policy and reconciliation
73
+
74
+ When `proxy.enabled` is true, all nodes are authorized as proxy clients. Nodes get
75
+ upper/lowercase proxy variables and internal `NO_PROXY` entries, including explicitly
76
+ configured `network.egress` hosts. Those named hosts use the common workspace
77
+ gateway for their configured ports. VM access uses the same destination policy bound
78
+ directly to the VM instance's reserved peer address. The proxy is reached at
79
+ `proxy.empeira.internal:3128` on the isolated network. A request without a matching
80
+ global or hostname rule remains denied.
81
+
82
+ After changing global destinations or rules, or updating Empeira's generated Squid
83
+ policy, run `empeira up`. It regenerates current node bindings and reloads Squid
84
+ after readiness without replacing the proxy or unrelated services. Unchanged
85
+ policy and bindings do not trigger a reload. A checkpoint in the existing
86
+ inventory records the proxy ID and digest of the small configuration files only
87
+ after the native reload succeeds; a failed or interrupted reload is retried by
88
+ the next `up` even when files already contain the requested policy.
89
+ Container and VM nodes consume the new policy without a restart; open proxy
90
+ connections can end during reconciliation. Enabling/disabling proxy mode is a
91
+ separate topology change and may require explicit node cleanup as reported by `up`.
92
+
93
+ Changing direct-egress destinations also changes the server's `NO_PROXY`
94
+ environment. `up` replaces a server whose environment definition changes while
95
+ retaining CA storage and node identities. Changing only destination ports or
96
+ transparent redirect rules preserves the server when its definition is unchanged.
97
+
98
+ ## Managed bootstrap
99
+
100
+ Managed bootstrap destinations are separate from normal user policy. Reviewed
101
+ agent and distribution endpoints in `agent.install` and `bootstrap.guests` allow
102
+ Puppet/OpenVox and required guest packages to be installed without duplicating them
103
+ in `proxy.global`. The temporary authenticated bootstrap proxy uses workspace DNS
104
+ and retains its private, loopback, link-local and reserved destination-address
105
+ restrictions independently of normal proxy policy. Its credential is not stored in
106
+ the guest seed, JSON inventory or logs. A /32 source ACL admits only the provisioning
107
+ node. The proxy and its private configuration are removed after use. When the normal
108
+ workspace proxy is disabled, a node has no proxy access once bootstrap ends.
109
+
110
+ `bootstrap.guests.*.destinations` grants network access only; it never writes an APT
111
+ source. Ubuntu and Debian retain the base image's sources. The temporary agent
112
+ repository and every other `/etc/apt` change made during bootstrap are restored and
113
+ verified before the proxy is removed and before Puppet starts. The reviewed Ubuntu
114
+ defaults permit archive/security only for amd64 and ports only
115
+ for arm64.
116
+
117
+ Default OpenVox sources include `apt.voxpupuli.org` and
118
+ `yum.voxpupuli.org`; distribution mirrors come from reviewed base images.
119
+ Custom agent sources are configured under `agent.install` and are not embedded
120
+ in Empeira. Their versions, checksums and destination lists are user-managed.
121
+ Package managers verify repository signatures. See the
122
+ [official OpenVox installation sources](https://voxpupuli.org/openvox/install/),
123
+ [Ubuntu archives](https://archive.ubuntu.com/ubuntu/) and
124
+ [Rocky repositories](https://dl.rockylinux.org/pub/rocky/).
125
+
126
+ For later package updates performed by Puppet, enable the normal proxy and add the required
127
+ destinations to global or matching normal rules. They can remain permitted
128
+ permanently. Requests to all other destinations remain denied.
129
+
130
+ Puppetfile synchronization and image refreshes belong to the independent update
131
+ plane. They do not use this workspace proxy or its allowlists. Forge runs in a
132
+ disposable helper with normal container-runtime networking and supplied user proxy
133
+ variables. Git uses the invoking user's host Git/SSH configuration and network
134
+ access, without copying private keys. No update starts Squid, CoreDNS or other
135
+ workspace services. See [update-plane architecture](architecture.md#runtime-and-update-planes).
136
+ Self-update and the aggregate `update all` remain unavailable.
@@ -0,0 +1,361 @@
1
+ # Troubleshooting
2
+
3
+ Start with `empeira config validate`, `empeira status` and `empeira node list`.
4
+ Status is read-only and reports unavailable engines or invalid inventory as
5
+ unknown/diagnostic states. Its successful exit means a report was produced, not
6
+ that infrastructure is healthy. `up` performs readiness checks.
7
+
8
+ ## HTTP and transport errors
9
+
10
+ HTTP failures identify the operation, requested URL, actual status and server reason
11
+ phrase, followed by the available response. For example:
12
+
13
+ ```text
14
+ Operation: Download agent package
15
+ URL: https://packages.example.org/apt/pool/agent.deb
16
+ HTTP status: 404 Not Found
17
+ Response:
18
+ Package is unavailable
19
+ ```
20
+
21
+ URLs retain their host and path; user information, sensitive query values, known
22
+ credentials, authorization headers and cookies are redacted. Multiline text and JSON
23
+ remain readable. HTML, truncation and unavailable/empty bodies are labelled; binary
24
+ bodies are omitted. Diagnostic output is limited to 8 KiB after redaction.
25
+
26
+ DNS, connection, timeout, TLS and proxy failures are reported as transport failures.
27
+ If no HTTP response was received, no status is invented. APT, DNF, Docker/Podman and
28
+ Git/r10k retain their native details; unavailable URL/status/body fields are identified
29
+ as unavailable separately. Empeira sends no extra requests for diagnosis. A 401 can
30
+ offer one agent-repository login in a TTY; CI uses both `EMPEIRA_AGENT_REPO_USERNAME`
31
+ and `EMPEIRA_AGENT_REPO_PASSWORD`. Rejected interactive and ENV logins are distinguished.
32
+ HTTP 403 does not trigger login, and HTTP 407 identifies proxy authentication. Registry
33
+ authentication continues to use native `docker login` / `podman login` and the reported
34
+ registry host; Empeira does not log into registries automatically.
35
+
36
+ APT helper credentials use literal `login` and `password` tokens in a temporary
37
+ 0600 file under `/etc/apt/auth.conf.d`. APT treats surrounding quotes and added
38
+ backslashes as credential bytes, so Empeira does not add them. A successful login
39
+ rewrites this file before retrying the same metadata command. The host-side package
40
+ download reuses the credentials for the same HTTPS origin. APT's token format cannot
41
+ represent whitespace inside a credential value.
42
+
43
+ ## KVM and QEMU
44
+
45
+ A present `/dev/kvm` is insufficient: the invoking user needs read/write access
46
+ and a working KVM API. Empeira reports device ownership, groups and capability
47
+ failures. If access is through the `kvm` group, add your user and start a fresh session:
48
+
49
+ ```console
50
+ sudo usermod -aG kvm "$USER"
51
+ ```
52
+
53
+ On WSL2, run `wsl.exe --shutdown` from Windows and reopen WSL after changing groups.
54
+ Do not change device permissions globally. Missing `/dev/kvm` or absent accelerator
55
+ support is not repaired by a group change. The QEMU build must support KVM/HVF for
56
+ the detected platform. Empeira refuses software emulation and does not provision
57
+ Windows-host QEMU automatically. See [installation](installation.md).
58
+
59
+ Missing `xorriso` prevents initial cloud-init seed creation. Install it with the
60
+ other VM prerequisites. Restarting an existing initialized VM does not require a
61
+ new seed build. ARM64 also needs the firmware reported by preflight.
62
+
63
+ ## SSH login failures
64
+
65
+ For a VM, `node ssh` defaults to `empeira` and the existing managed private key,
66
+ including when bootstrap is incomplete. For containers, it uses the local user's
67
+ name and normal OpenSSH keys/agent/config. Neither path injects a replacement key
68
+ or provisions a login user. Verify your Puppet-created account,
69
+ authorized keys, home permissions, daemon, PAM and login profile. Use `--user`,
70
+ `--identity` and `--port` independently when appropriate:
71
+
72
+ ```console
73
+ empeira node ssh host1 --user deploy --identity ~/.ssh/test_ed25519
74
+ empeira node ssh host1 --port 2222
75
+ empeira node shell host1
76
+ ```
77
+
78
+ If SSH reports `Too many authentication failures`, your agent may offer too many
79
+ keys before the intended one. For containers, select the identity in your normal
80
+ OpenSSH `Host` configuration and set `IdentitiesOnly yes` there when needed. VM
81
+ access selects its managed or explicit identity and disables password authentication.
82
+
83
+ VM management uses a private VirtIO-Serial execution service, separate from
84
+ system SSH. `node ssh` defaults to guest port 22 and never checks or forwards
85
+ through management. A failed system login reports the native SSH error and does
86
+ not restart the daemon or replace authentication. Root shadow `!`, sudoers, PAM,
87
+ regular accounts and authorized keys do not govern management.
88
+
89
+ For an actual management failure, inspect `node logs` and the private serial
90
+ console. From the guest console run:
91
+
92
+ ```bash
93
+ cloud-init status --long
94
+ journalctl -u empeira-management.service
95
+ ls -l /dev/virtio-ports/org.empeira.management.0
96
+ ```
97
+
98
+ The cloud image requires Python 3 before package bootstrap (`/usr/bin/python3`
99
+ or `/usr/libexec/platform-python` on Rocky Linux). Preserve SELinux enforcement
100
+ and inspect AVC messages if it denies a guest operation. Removing the interpreter,
101
+ service, VirtIO port or filesystem can interrupt management. Empeira preserves
102
+ storage, reports unknown completion and uses no authentication fallback. Console
103
+ credentials stay under Puppet's control; there are no later password resets.
104
+ Older VM layouts require explicit `node destroy` and recreation. Current destroy
105
+ uses stored ownership without requiring a compatible guest layout. Existing VMs are never migrated or deleted automatically.
106
+
107
+ VM creation failures retain the original diagnostic and add the retained node's
108
+ cleanup command. Inspect the underlying failure first; an incomplete VM requires
109
+ explicit node destruction and recreation after the cause is corrected.
110
+
111
+ For a container, shell is runtime exec. For a VM, shell is a root PTY over private
112
+ VirtIO and works with root locked or SSH stopped. `node console` is the separate
113
+ serial recovery path. Press Enter there, then use the current console credentials.
114
+ The initial default is `root` / `empeira`, unless disabled/overridden or changed by
115
+ Puppet. **Ctrl-] detaches**. Empeira does not reset credentials to repair a failed login.
116
+ Stopped nodes need `node start`. Investigate changed SSH host keys instead of
117
+ turning off checking. Destroy/recreate removes a disposable node's private known-hosts entry.
118
+
119
+ VM sessions release the workspace mutation lock. Status and independent workspace
120
+ operations remain available, and multiple VM SSH sessions may coexist. Stop, destroy
121
+ or restart of that same VM conflicts with the shared instance guard: detach with
122
+ **Ctrl-]** for console sessions, or exit SSH, then retry. Do not delete lock files.
123
+
124
+ ## Proxy destination denied
125
+
126
+ Run `empeira proxy show HOSTNAME` and check every matching rule. Verify the node
127
+ exists in the workspace, `proxy.enabled` is true, and `up` has reconciled the
128
+ configuration and current node addresses. Allowed DNS destinations may resolve to
129
+ private or reserved IP addresses; only HTTP port 80 and HTTPS CONNECT port 443 are
130
+ permitted. After updating Empeira, run `empeira up` to replace a proxy still using
131
+ the previous IP restrictions. Managed bootstrap access retains its own destination
132
+ restrictions and does not grant normal access afterward. See [proxy](proxy.md).
133
+
134
+ ## Hiera and EYAML
135
+
136
+ Mount visibility failures identify the local source, container destination and
137
+ sanitized native exit status/output. A stopped container cannot run a mount probe;
138
+ inspect its existing server logs rather than assuming a sharing or permission
139
+ problem. A successful probe with different directory listings indicates a
140
+ different engine view of the source. Empeira does not weaken mount checks or
141
+ change host permissions automatically.
142
+
143
+ An explicit `environment_timeout` in the project's `environment.conf` overrides
144
+ the native server setting. Empeira preserves it. Bare `puppet config print`
145
+ includes that override; server startup and reconciliation verify only the native
146
+ server value. Set the environment override to zero or remove it when immediate
147
+ code reloads are required. No explicit cache invalidation is performed.
148
+
149
+ `config show` includes `effective_hiera_mounts`. A skipped optional source is
150
+ reported during `up`; mount it locally or correct the source path, then run `up`
151
+ again. Set `required: true` when absence must stop startup. Invalid targets,
152
+ collisions, symlink escapes and known modulepath exclusions are errors even for
153
+ optional mounts. Existing mounted data changes are live without a restart.
154
+
155
+ When EYAML is enabled, both key paths must resolve to readable regular PEM files.
156
+ Relative paths are resolved against the selected canonical control repository.
157
+ Check engine visibility/UID mapping and the paths in `hiera.yaml`, without printing
158
+ private material. See [Hiera and EYAML](hiera-and-eyaml.md).
159
+
160
+ ## Retained, missing or stale resources
161
+
162
+ If agent installation reports an unavailable package version, compare
163
+ `agent.version` and the configured repository `suffix` with the versions
164
+ published by that source. The retained node can be inspected with `node shell`.
165
+ For `method: package`, use the complete installed package version and verify
166
+ its URL and SHA-256. A checksum mismatch stops before package installation.
167
+ Valid agent-cache hits need no network access or credentials. On a cache miss,
168
+ a damaged artifact or with caching disabled, `401 Unauthorized` calls for both
169
+ `EMPEIRA_AGENT_REPO_USERNAME` and `EMPEIRA_AGENT_REPO_PASSWORD`; `403 Forbidden`
170
+ means access is denied and `404 Not Found` means a source or package is missing.
171
+ A signature error calls
172
+ for checking the configured signing key or RPM signature. Credentials do not
173
+ belong in YAML. Failed APT/DNF restoration retains the node and prevents the
174
+ first Puppet run and subsequent `node start` or `node puppet` operations. Existing
175
+ release packages are rejected before changes; use a clean base image or a signed
176
+ custom source. Protected DNF release-RPM repositories are unsupported; configure
177
+ `agent.install.dnf` instead.
178
+
179
+ If node creation fails during key preparation, verify that the selected agent
180
+ package was installed and provides `/opt/puppetlabs/bin/puppet` and
181
+ `/opt/puppetlabs/puppet/bin/ruby`. Keep replacement agents out of
182
+ `bootstrap.packages`: configure `agent.package`, `agent.version` and
183
+ `agent.install` instead, then destroy and recreate the incomplete node.
184
+ The agent choice does not change server or database images.
185
+
186
+ A failed bootstrap retains the container or VM and hostname for diagnosis:
187
+
188
+ ```console
189
+ empeira node logs vm1
190
+ empeira node ssh vm1
191
+ empeira node console vm1
192
+ empeira node destroy vm1
193
+ empeira node run vm1 --provider vm
194
+ ```
195
+
196
+ APT cleanup errors now identify the failed archive or removal operation, exit code,
197
+ timeout and sanitized native output. A guest failure is distinct from an SSH
198
+ transport failure with unknown completion. A readable backup that can be removed
199
+ manually does not establish the earlier SSH outcome. The already verified backup
200
+ removal is retried once only for recognizable transient SSH connection errors.
201
+ Permission, host-key and authentication errors, command timeouts and persistent failures
202
+ remain fatal. Successful manual cleanup alone does not mark an incomplete node as
203
+ provisioned or permit Puppet; correct the reported cause and recreate the node.
204
+
205
+ Incomplete nodes remain available for diagnostics but cannot start or run Puppet.
206
+ Destroy and recreate them after correcting the source of the failure. Missing or corrupt
207
+ pinned base images require restoring the exact cached image before an existing
208
+ overlay can resume. Image changes never replace node state automatically.
209
+ Ownership conflicts or changed runtime IDs require inspection; deterministic names
210
+ are insufficient to adopt/delete resources. Never delete inventory or the stable
211
+ mutation lock file to bypass a failure. Unknown state schemas fail closed.
212
+
213
+ `down` removes services/networks but retains CA and database volumes. Existing nodes,
214
+ even stopped ones, must be destroyed first. `destroy` permanently removes all owned
215
+ workspace nodes, infrastructure and retained CA/database data without a prompt.
216
+ If `up` reports that a managed service exited after start, inspect its container logs
217
+ using the runtime command printed in the error. A route-helper failure can otherwise
218
+ hide an earlier service exit; `status` shows which service stopped.
219
+ `destroy` can also run when `.empeira.yaml` or user configuration is invalid. It uses
220
+ the validated inventory to select the owning runtime and removes only owned resources.
221
+ Keep the project marker file and valid inventory in place for this recovery path.
222
+ It preserves the control repository, module directory and native caches so a subsequent `up` can reuse
223
+ unchanged Puppetfile modules. Missing retained volumes or database credentials
224
+ are not permission to initialize empty replacements. Preserve state and volumes
225
+ together during recovery. See [architecture](architecture.md#persistent-state-and-ownership).
226
+
227
+ ## Project marker and browser
228
+
229
+ “An Empeira Git project is required” means the current directory is outside a Git
230
+ worktree, or Git cannot run. Enter your control repository and check
231
+ `git rev-parse --show-toplevel`. A missing-marker error prints the canonical root,
232
+ exact `.empeira.yaml` path and shell-safe `touch` command. An empty marker is valid;
233
+ placing it in a subdirectory does not opt in the root. `--project` is not supported.
234
+
235
+ `empeira browser` prints a dynamic loopback HTTPS URL. The default UI has a self-signed
236
+ certificate and may take a few seconds to initialize. Open internal URLs in that
237
+ Chromium desktop, not directly in the host browser. External websites failing is
238
+ expected: the browser has no Internet grant. `status` shows Browser and Browser-ui;
239
+ `up` reconciles an existing browser, and `down` removes it and its disposable profile.
240
+ Run `empeira up` before the first browser request. The browser command does not start
241
+ or reconcile stopped workspace infrastructure. If it reports a missing workspace,
242
+ network, or CoreDNS service, restore the control plane with `empeira up` and retry.
243
+ `browser.start_url` defaults to `about:blank` and is not checked for reachability.
244
+ For an internal application that starts later, simply reload the page in Chromium.
245
+ If ownership or loopback/network checks fail, investigate the runtime resource rather
246
+ than attaching an unrestricted network or publishing services publicly. Browser-to-VM
247
+ URLs use the VM hostname and application port directly. Check VM network health,
248
+ CoreDNS records and whether the guest application listens on its peer interface.
249
+
250
+ OpenVox View should use `PUPPETDB_HOST=puppetdb.empeira.internal`,
251
+ `PUPPETDB_PORT=8080`, and `PUPPETDB_TLS=false`. Check internal DNS and PuppetDB's
252
+ HTTP query readiness if it cannot load data. No client TLS credentials are needed.
253
+ Do not publish PuppetDB to the host: its HTTP API is intentionally unauthenticated
254
+ within the disposable isolated network.
255
+ The configuration server uses the HTTPS relay on port 8081 with a workspace
256
+ CA-issued server certificate. This is required by the PuppetDB terminus; check
257
+ the relay and backend separately if catalogs or reports fail. The backend itself
258
+ listens only on HTTP port 8080.
259
+
260
+ The browser relay builds `resources/relay/Containerfile` locally. On build errors,
261
+ check the selected runtime's registry access and the configured relay recipe/image;
262
+ changing the Puppet/OpenVox server image is not a relay dependency fix.
263
+
264
+ ## Shared peer network
265
+
266
+ Unsupported inventory/definition revisions require explicit recovery or recreation.
267
+ Preserve state while investigating ownership errors; never delete live channel
268
+ directories, force-remove attached networks, reset Podman Machine or kill processes
269
+ by name. No automatic migration is provided.
270
+
271
+ Native rootless Podman requires writable TUN and KVM inside
272
+ `podman unshare --rootless-netns`, iproute2, Netavark and disabled forwarding on the
273
+ owned bridge. Docker requires its supported isolated bridge mode. A packet helper
274
+ checksum, network, capability or recorded-ID mismatch fails closed. A live QEMU or
275
+ channel with an unavailable control socket must be investigated before retrying.
276
+
277
+ On macOS, select exactly one running rootless Podman Machine and its matching default
278
+ connection. Share the canonical Empeira state path with that machine: the exported
279
+ helper must have the same SHA-256 from both hosts. No rootful fallback is available.
280
+ Subnet exhaustion means known host/VPN or runtime routes cover all candidates;
281
+ inspect those routes instead of disabling overlap checks. IPv6 peer networking is
282
+ not supported.
283
+
284
+ ## Artifact updates
285
+
286
+ `update modules` and `update images` do not start or reconcile infrastructure. Image
287
+ changes take effect during the next `empeira up`. Module updates write directly into
288
+ `modules.path` and can be visible to a running server immediately. `up` never synchronizes modules; missing modules fail before runtime mutation with
289
+ `Run: empeira update modules`. Missing images are acquired automatically; existing
290
+ images are reused without remote checks.
291
+ Failed explicit updates may leave partially changed module files; fix the cause and
292
+ rerun the update. Existing plausible modules are usable without a freshness check. Diagnose Forge, registry, version/ref, authentication and normal
293
+ host/container connectivity errors; changing the workspace proxy allowlist does
294
+ not repair update-plane connectivity. User proxy settings and runtime registry
295
+ credentials apply. Git uses the invoking user's normal SSH identity and configuration.
296
+
297
+ Image updates preserve the native Docker/Podman buildcache (`build --pull`, without
298
+ `--no-cache`) and select the configured default plus workspace-used container node
299
+ variants. Native remote metadata checks have a 30-second budget (Podman may need
300
+ up to five more seconds for local temporary-manifest cleanup). Authentication,
301
+ unsupported/malformed metadata or timeout errors identify the image and filtered
302
+ diagnostic; no full pull is attempted as a fallback. Docker/Podman must support their
303
+ native manifest commands. An old index digest may need a native immutable-reference
304
+ lookup for platform comparison; a registry that has deleted that manifest can prevent
305
+ the comparison. Fix registry access or explicitly remove only the affected unused
306
+ local artifact before retrying. Builds and pulls have a 600-second timeout. A timed-out CLI process group
307
+ is killed and reaped, its temporary build context is removed, and filtered buffered
308
+ diagnostics identify the affected image. Successful tool logs remain hidden.
309
+
310
+ Update helpers are removed after completion where the engine is reachable. If an
311
+ engine stops responding during cleanup, restore access and inspect the reported
312
+ update-helper failure before removing anything; names alone do not establish
313
+ ownership. `self-update` and `update all` remain unavailable until release package
314
+ replacement is implemented. Image updates are not atomic across the full catalog;
315
+ earlier successful pulls/builds remain after a later failure.
316
+
317
+ ## User preferences and registry authentication
318
+
319
+ `config show` includes effective `runtime.container_engine` and `images.registry`
320
+ after optional `~/.empeira.yaml` preferences. Only those two keys are allowed there;
321
+ move project behavior into the control repository rather than adding user-level
322
+ server, network or service overrides. Invalid user YAML fails with its file and
323
+ configuration path. The file is optional; absence is not an error.
324
+
325
+ For likely registry authentication/authorization failures (401/403, unauthorized,
326
+ authentication required, access denied or insufficient scope), Empeira prints the
327
+ native login command for the resolved registry, for example
328
+ `docker login registry.example.net` or `podman login ghcr.io`. Authenticate
329
+ and verify repository permissions, then retry the original Empeira command. A login
330
+ cannot grant permissions your account does not have. Runtime credential helpers and
331
+ auth files are used directly; Empeira never manages registry credentials.
332
+
333
+ `manifest unknown`, `name unknown` and missing-image errors point to the repository
334
+ or tag. Timeouts, refused connections and DNS failures require network diagnosis;
335
+ certificate/x509 failures require runtime CA trust diagnosis. These errors do not
336
+ produce login advice. Sanitized native diagnostics are retained for all categories;
337
+ metadata failures never fall back to a full pull.
338
+
339
+ ## Workspace gateway and direct egress
340
+
341
+ `status` reports Gateway/DNS/Proxy readiness and overall Workspace network health.
342
+ After an egress edit or DNS change, run `empeira up`; nodes keep their identities.
343
+ A failed hostname lookup leaves egress blocked. Check the host/corporate resolver
344
+ and every A record first. An IPv6-only destination is unsupported, not silently allowed.
345
+
346
+ For DIRECT traffic, inspect `network.egress`: exactly one host/IP and the actual
347
+ TCP port must be allowed. `NO_PROXY` is application selection, not authorization.
348
+ No proxy fallback occurs. A denied SYN normally times out; Empeira cannot infer
349
+ from that timeout alone whether a remote service, route or firewall caused it.
350
+ `Network is unreachable` instead suggests a missing node default route; rerun `up`
351
+ to repair managed container routes and inspect the VM workspace NIC gateway.
352
+
353
+ Gateway firewall drift or disabled forwarding is reported separately by the
354
+ readiness probe. Failed apply stops/blocks the gateway; never attach a node to an
355
+ external runtime network as a workaround. Docker workspace bridge attachment
356
+ errors require an accessible Linux-engine host namespace and an identifiable
357
+ iptables backend. The helper changes only owned bridge rules. Do not disable the
358
+ host firewall globally. Check Docker Desktop host-network restrictions separately.
359
+ Proxy errors require checking Squid source bindings and `proxy.global`/`proxy.rules`,
360
+ not adding unrelated direct grants. Corporate DNS still follows host discovery;
361
+ IPv6-only upstreams must be replaced with reachable IPv4 resolvers for this backend.
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'etc'
4
+
5
+ module Empeira
6
+ module Addons
7
+ class Environment
8
+ API_VERSION = '1'
9
+
10
+ def initialize(context:, name:, ref:, path:, inherited: ENV.to_h)
11
+ @context = context
12
+ @name = name
13
+ @ref = ref
14
+ @path = path
15
+ @inherited = inherited
16
+ end
17
+
18
+ def export(arguments: [])
19
+ environment = @inherited.keys.grep(/^EMPEIRA_/).to_h { |key| [key, nil] }
20
+ flatten(@context.configuration, 'EMPEIRA_CONFIG', [], environment)
21
+ flatten(@context.configuration.dig('addons', @name, 'config') || {}, 'EMPEIRA_ADDON_CONFIG', [], environment)
22
+ environment.merge!(runtime)
23
+ validate_size!(@inherited.merge(environment).compact, arguments)
24
+ environment
25
+ end
26
+
27
+ private
28
+
29
+ def runtime
30
+ values = { 'EMPEIRA_VERSION' => BuildInfo.load.version, 'EMPEIRA_ADDON_API_VERSION' => API_VERSION,
31
+ 'EMPEIRA_ADDON_NAME' => @name, 'EMPEIRA_ADDON_VERSION' => @ref,
32
+ 'EMPEIRA_ADDON_PATH' => @path.expand_path.to_s }
33
+ values['EMPEIRA_CONTROL_REPO'] = @context.project.path.expand_path.to_s if @context.project
34
+ values['EMPEIRA_WORKSPACE'] = @context.workspace.path.expand_path.to_s if @context.workspace
35
+ values
36
+ end
37
+
38
+ def private_path?(path)
39
+ Configuration::Schema::ADDON_PRIVATE_PATHS.any? do |pattern|
40
+ pattern.size == path.size && pattern.zip(path).all? { |expected, key| expected == '*' || expected == key }
41
+ end
42
+ end
43
+
44
+ def flatten(value, prefix, path, output)
45
+ return if value.nil? || private_path?(path)
46
+
47
+ case value
48
+ when Hash then flatten_mapping(value, prefix, path, output)
49
+ when Array
50
+ output["#{prefix}_COUNT"] = value.size.to_s
51
+ value.each_with_index { |child, index| flatten(child, "#{prefix}_#{index}", path + [index.to_s], output) }
52
+ else output[prefix] = value.to_s
53
+ end
54
+ end
55
+
56
+ def flatten_mapping(value, prefix, path, output)
57
+ value.each do |key, child|
58
+ flatten(child, "#{prefix}_#{key.upcase.gsub(/[^A-Z0-9_]/, '_')}", path + [key], output)
59
+ end
60
+ end
61
+
62
+ def validate_size!(environment, arguments)
63
+ strings = environment.map { |key, value| "#{key}=#{value}" } + arguments + [@path.to_s]
64
+ if strings.any? { |value| value.include?("\0") }
65
+ raise Error.new(@name, 'configuration environment or arguments contain a null byte')
66
+ end
67
+
68
+ return if fits?(strings)
69
+
70
+ raise Error.new(@name,
71
+ 'environment and arguments exceed the operating system size limit; ' \
72
+ 'reduce configuration or inherited environment')
73
+ end
74
+
75
+ def fits?(strings)
76
+ size = strings.sum { |value| value.bytesize + 1 + 8 }
77
+ limit = Etc.sysconf(Etc::SC_ARG_MAX) || 131_072
78
+ size < limit - 16_384 && strings.all? { |value| value.bytesize < 131_072 }
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Empeira
4
+ module Addons
5
+ class Error < Empeira::Error
6
+ attr_reader :exit_status
7
+
8
+ def initialize(name, message, exit_status: 1)
9
+ @exit_status = exit_status
10
+ super("Addon #{name}: #{message}")
11
+ end
12
+ end
13
+ end
14
+ end