empeira 0.1.0.pre.alpha.3 → 0.1.0.pre.alpha.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c856b6d473f798e1882b261f6dec67a20ebaa89142a073ba9b977265cd24cd6f
4
- data.tar.gz: ac23a47ef8ecf4abfdd8d48557cee762f5954e69c40c335b12e7d6428e376709
3
+ metadata.gz: 4e4e7094658b9e3d8f4ee0edbafa3e4e45be28921900cce8eb4af7d66096cee6
4
+ data.tar.gz: 7f062dc35a9cef14ad52f55362beba6c4a5a0ab1b57225ce27a191123dc01fc9
5
5
  SHA512:
6
- metadata.gz: 30b50d1c33a73512fdd2067064e0f17b63dda84204045b90994137ec79ee8374c466501b232119ea2fff3b81f9dda39c62edd8fed47b5ee50a528f75f063dbf6
7
- data.tar.gz: 8daa8af9f4f9bb5ffcc1751cf3c8714e6853142dd59878ba6791150b79511549b626cc3edd9eafc95e36adfed3b00f793e8f97d78a607ac6d29dd70865e91dc4
6
+ metadata.gz: 8410139aeffa1ccaab90f3ddac57a809a86420e385f1354f801f8181c936ecf5653762cc4dbade0178d6df99b2dad1cc8b68cb8d537013132ec09e2ed65aefa1
7
+ data.tar.gz: 52ba2b37546bc90263d9834193b5080440e2a66425c9f9403b19800e791bf5f26b895d4c4e1af4bc218951b3a0e433437c07c550f493f4577e4e8dfd156a06bb
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![OpenVox E2E](https://img.shields.io/github/actions/workflow/status/c8m6/empeira/openvox.yml?branch=main&label=OpenVox%20E2E)](https://github.com/c8m6/empeira/actions/workflows/openvox.yml)
7
7
  [![Release](https://img.shields.io/github/v/release/c8m6/empeira?include_prereleases&sort=date&label=Release)](https://github.com/c8m6/empeira/releases)
8
8
  [![Ruby: 3.1–4.0](https://img.shields.io/badge/Ruby-3.1%20%7C%203.2%20%7C%203.3%20%7C%203.4%20%7C%204.0-CC342D)](docs/installation.md)
9
- [![License: AGPL-3.0-only](https://img.shields.io/badge/License-AGPL--3.0--only-blue)](LICENSE)
9
+ [![License: AGPL-3.0-or-later](https://img.shields.io/badge/License-AGPL--3.0--or--later-blue)](LICENSE)
10
10
 
11
11
  **ἐμπειρία (empeiría)** — experience gained through practice. The name reflects
12
12
  learning about infrastructure by experimenting, testing and seeing what happens.
@@ -149,6 +149,6 @@ and [pull requests](https://github.com/c8m6/empeira/pulls) are welcome. Follow t
149
149
  [security policy](SECURITY.md).
150
150
 
151
151
  Project-owned code and documentation are licensed under
152
- [AGPL-3.0-only](LICENSE). Third-party packages and images retain their own licenses
152
+ [AGPL-3.0-or-later](LICENSE). Third-party packages and images retain their own licenses
153
153
  and notices and are obtained from upstream; empeira does not bundle them or
154
154
  redistribute proprietary Puppet Enterprise software.
data/docs/architecture.md CHANGED
@@ -184,9 +184,12 @@ there are no implicit database-major migrations or automatic node replacements.
184
184
 
185
185
  Managed servers disable environment caching through native Puppet configuration
186
186
  (`environment_timeout = 0` in `[server]`), applied by the startup wrapper before
187
- the configured image entrypoint. `up` checks the actual native setting and restarts
188
- only a server whose setting changed; it preserves every other configuration key
189
- and retained storage. The configured Puppet confdir must agree with Puppetserver
187
+ the configured image entrypoint. An independent `environment.conf` copy in the
188
+ managed environment enforces zero even when the control repository specifies
189
+ `unlimited`; the original remains unchanged and read-only. `up` refreshes the copy,
190
+ checks both native and selected-environment settings, and restarts only a server
191
+ whose native setting changed. It preserves every other configuration key and
192
+ retained storage. The configured Puppet confdir must agree with Puppetserver
190
193
  HOCON `master-conf-dir`. Catalog
191
194
  requests reload live code without repository hashes or environment-cache checkpoints.
192
195
  See [nodes](nodes.md#environment-cache-and-live-code).
@@ -404,20 +404,25 @@ runtime values without inferring them from the image name. Missing or unsafe
404
404
  runtime fields fail configuration validation. Managed servers use
405
405
  `environment_timeout = 0` in the native `[server]` section. The startup wrapper
406
406
  uses `puppet config set` before the configured entrypoint, independently of image
407
- environment-variable mappings. Startup and `up` read Puppet's native server
408
- settings without selecting an environment override, against the
409
- confdir containing `server.runtime.paths.puppetdb_config`; custom Puppetserver
407
+ environment-variable mappings. Empeira also prepares an independent copy of the
408
+ project's `environment.conf` in its managed environment directory, replacing or
409
+ adding only `environment_timeout = 0`. Other settings, including `modulepath` and
410
+ `config_version`, are preserved. The original file and the control repository
411
+ remain read-only in the server. The copy is refreshed during `up`, including when
412
+ the project has no `environment.conf` or Hiera mounts.
413
+
414
+ `up` verifies both the native server setting and the effective timeout for
415
+ `server.environment`, against the confdir containing
416
+ `server.runtime.paths.puppetdb_config`; custom Puppetserver
410
417
  `jruby-puppet.master-conf-dir` must refer to that same directory. Puppetserver
411
- HOCON master directories take precedence over Puppet CLI defaults. A changed
412
- effective timeout updates only that key and restarts the same owned server; an
418
+ HOCON master directories take precedence over Puppet CLI defaults. Native setting
419
+ drift updates only that key and restarts the same owned server; an
413
420
  entrypoint that overwrites it fails verification. CA, SSL and database storage
414
421
  are preserved. The next catalog request reloads live code without
415
- an Empeira content scan or admin API invalidation. An explicit `environment.conf`
416
- can override this server setting; keep its timeout at zero for immediate edits.
417
- Such overrides are preserved and do not prevent server startup or cause repeated
418
- restarts. `puppet config print environment_timeout --section server` includes the
419
- selected environment's override, so it can report `unlimited` while the native
420
- server setting is zero. Inspect both configuration layers when diagnosing caching.
422
+ an Empeira content scan or admin API invalidation. A project timeout of `unlimited`
423
+ therefore stays unchanged in the checkout while the managed server uses zero.
424
+ To verify it, run `puppet config print environment_timeout --section server
425
+ --environment ENVIRONMENT` inside the server with the configured environment name.
421
426
 
422
427
  `puppetdb.runtime` holds the independent OpenVoxDB process contract. Custom
423
428
  images may override its executable, numeric user, Java and main arguments,
data/docs/development.md CHANGED
@@ -253,6 +253,7 @@ The APT authentication regression uses a local HTTPS repository with synthetic B
253
253
  credentials and an ephemeral GPG signing key. It checks the helper's effective
254
254
  `auth.conf.d` configuration, private file mode and exact credential bytes, then
255
255
  verifies metadata returns 401 before one interactive login and 200 after the retry.
256
+ The fixture omits the 401 reason phrase to exercise APT's bare status/IP diagnostic.
256
257
  The selected package download must reuse that login and pass its SHA-256 check.
257
258
  It requires host `gpg`/`gpgconf` and a locally reachable container engine; helper and
258
259
  host TLS verification remain enabled. Run it explicitly for either engine:
@@ -511,9 +512,10 @@ Run `EMPEIRA_INTEGRATION=server-environment bundle exec rspec spec/integration/s
511
512
  to check native timeout 0, live edits, CA retention, PuppetDB and idempotent up
512
513
  under Docker/rootless Podman with the default server and a custom entrypoint
513
514
  that ignores timeout environment mapping.
514
- The same gate verifies startup and idempotent `up` with an explicit
515
- `environment.conf` timeout override, preserving the file and distinguishing its
516
- effective value from the native server setting.
515
+ The same gate verifies that an explicit `environment.conf` timeout of `unlimited`
516
+ is zero in the managed copy while the original stays unchanged. It edits a Ruby
517
+ Puppet function between catalogs and checks the changed result without `up` or a
518
+ restart, followed by an idempotent `up`.
517
519
 
518
520
  Run the isolated diagnostics/recovery gate with each native runtime:
519
521
 
@@ -105,7 +105,8 @@ An optional mount is not permission to ignore unsafe configuration.
105
105
  Puppet's [default modulepath](https://help.puppet.com/core/current/Content/PuppetCore/config_file_environment.htm)
106
106
  includes the environment's `modules` directory. Empeira checks explicit
107
107
  `environment.conf` modulepaths and reports a known exclusion before startup. It
108
- never rewrites that file. Expressions containing unresolved Puppet variables cannot
108
+ never rewrites the original file. Its managed copy changes only
109
+ `environment_timeout` to zero. Expressions containing unresolved Puppet variables cannot
109
110
  be verified completely from the CLI host; ensure the effective server modulepath
110
111
  includes the mount destination. Environment mounts can be referenced by ordinary
111
112
  relative paths in `hiera.yaml`, such as `data/external` or `external` when the datadir
@@ -117,7 +118,8 @@ an image rebuild, `up`, or a service restart.
117
118
  Empeira neither copies their data nor writes probe files into them. It uses a managed
118
119
  environment directory containing links to the read-only control repository so nested
119
120
  mount targets can exist without modifying control code. Existing control files and
120
- linked directories remain live. `up` refreshes links for new control entries under
121
+ linked directories remain live; `environment.conf` is an independent copy refreshed
122
+ by `up`. `up` also refreshes links for new top-level control entries and entries under
121
123
  projected ancestor directories. EYAML keys remain configured separately in `eyaml`,
122
124
  and decryption runs on the server.
123
125
 
data/docs/nodes.md CHANGED
@@ -87,18 +87,22 @@ Managed servers enforce native `[server] environment_timeout = 0` before startup
87
87
  and verify the effective setting during `up`, independently of image environment
88
88
  variable support. Existing workspaces adopt it on
89
89
  their next `up`; run that once after updating from a version that enabled caching.
90
+ Adopting the managed environment copy changes the server mount and recreates its
91
+ container once, retaining CA, SSL and database storage.
90
92
  Each catalog request reloads live control code, modules and mounted Hiera data.
91
93
  Empeira does not hash these trees,
92
94
  persist content checkpoints or call the environment-cache admin API. Code edits
93
95
  need neither `up` nor a server restart, including for agents invoked manually.
94
96
  Empeira does not enable `use_cached_catalog`.
95
97
 
96
- A repository's own `environment.conf` can override the server timeout; use zero
97
- for immediate edits. Avoid concurrent edits during a catalog run. Existing files
98
- and directories in projected environments stay live through read-only links.
99
- New siblings under ancestors projected for nested Hiera mounts require `up` to
100
- create their links. `node start` resumes the node without invoking Puppet; Puppet
101
- services enabled by a catalog may run independently on startup.
98
+ Empeira copies `environment.conf` into its managed environment and sets its timeout
99
+ to zero there, preserving all other settings and the original file. Run `up` to
100
+ refresh this copy after editing other environment settings. Avoid concurrent edits
101
+ during a catalog run. Existing files and directories stay live through read-only
102
+ links. New top-level entries and new siblings under ancestors projected for nested
103
+ Hiera mounts require `up` to create their links. `node start` resumes the node
104
+ without invoking Puppet; Puppet services enabled by a catalog may run independently
105
+ on startup.
102
106
 
103
107
  ## Distribution package sources
104
108
 
@@ -33,6 +33,14 @@ HTTP 403 does not trigger login, and HTTP 407 identifies proxy authentication. R
33
33
  authentication continues to use native `docker login` / `podman login` and the reported
34
34
  registry host; Empeira does not log into registries automatically.
35
35
 
36
+ APT can report an HTTP status without a reason phrase, for example
37
+ `401 [IP: 192.0.2.1 443]`. This still triggers the single repository login attempt
38
+ when no ENV credentials are set and both standard input and standard error are
39
+ connected to a TTY. The corresponding `403` and `407` forms retain their access-denied
40
+ and proxy-authentication diagnoses without prompting. A node retained as `incomplete`
41
+ after an acquisition failure must be destroyed with `empeira node destroy HOSTNAME`
42
+ and recreated with the original `empeira node run` arguments after resolving the cause.
43
+
36
44
  APT helper credentials use literal `login` and `password` tokens in a temporary
37
45
  0600 file under `/etc/apt/auth.conf.d`. APT treats surrounding quotes and added
38
46
  backslashes as credential bytes, so Empeira does not add them. A successful login
@@ -140,11 +148,14 @@ problem. A successful probe with different directory listings indicates a
140
148
  different engine view of the source. Empeira does not weaken mount checks or
141
149
  change host permissions automatically.
142
150
 
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.
151
+ Empeira enforces `environment_timeout = 0` in both the native server setting and
152
+ its independent copy of `environment.conf`. The project's original file remains
153
+ unchanged, even when it specifies `unlimited`. Run `up` after updating Empeira or
154
+ editing other settings in `environment.conf` to refresh the managed copy. Inside
155
+ the server, `puppet config print environment_timeout --section server --environment
156
+ ENVIRONMENT` must report `0` for the configured environment. A different value
157
+ fails startup verification; check the custom server's confdir and environmentpath.
158
+ No explicit cache invalidation is performed.
148
159
 
149
160
  `config show` includes `effective_hiera_mounts`. A skipped optional source is
150
161
  reported during `up`; mount it locally or correct the source path, then run `up`
@@ -39,11 +39,11 @@ module Empeira
39
39
  diagnostic = Execution::Diagnostics.native(result.with(stderr: text, stdout: ''),
40
40
  operation: "Agent #{operation}", tool: arguments.first)
41
41
  case text
42
- when /407\s+Proxy Authentication Required|(?:HTTP|Status code|HTTP code)\s*:?\s*407\b/i
42
+ when /407\s+Proxy Authentication Required|(?:HTTP|Status code|HTTP code)\s*:?\s*407\b|\b407[ \t]+\[IP:/i
43
43
  raise Error, "Host helper proxy authentication failed\n#{diagnostic}", cause: nil
44
- when /401\s+Unauthorized|(?:HTTP|Status code|HTTP code)\s*:?\s*401\b/i
44
+ when /401\s+Unauthorized|(?:HTTP|Status code|HTTP code)\s*:?\s*401\b|\b401[ \t]+\[IP:/i
45
45
  raise Agent::AuthenticationRequired, diagnostic, cause: nil
46
- when /403\s+Forbidden|(?:HTTP|Status code|HTTP code)\s*:?\s*403\b/i
46
+ when /403\s+Forbidden|(?:HTTP|Status code|HTTP code)\s*:?\s*403\b|\b403[ \t]+\[IP:/i
47
47
  raise Error, "Agent repository access denied\n#{diagnostic}", cause: nil
48
48
  end
49
49
  raise Error, "Agent #{operation} failed\n#{diagnostic}", cause: nil
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0-alpha.3",
3
- "revision": "bfada39f711fd74dae3f061f80e1c5293bd13eec",
4
- "build_time": "2026-10-15T13:10:56Z"
2
+ "version": "0.1.0-alpha.4",
3
+ "revision": "b073c6a15db14c4dace4a732703240f4289cbb48",
4
+ "build_time": "2026-10-21T11:41:02Z"
5
5
  }
@@ -350,10 +350,11 @@ module Empeira
350
350
 
351
351
  def reconcile_server_environment
352
352
  environment = Server::EnvironmentConfiguration.new(runtime: @runtime, plan: @plan)
353
- return unless environment.reconcile(@observed.fetch('server'))
353
+ if environment.reconcile(@observed.fetch('server'))
354
+ @changed = true
355
+ @health.wait('server', @observed)
356
+ end
354
357
 
355
- @changed = true
356
- @health.wait('server', @observed)
357
358
  environment.verify!(@observed.fetch('server'))
358
359
  end
359
360
 
@@ -9,7 +9,7 @@ module Empeira
9
9
  def initialize(context:, hiera:)
10
10
  @project = context.project.path
11
11
  @root = root(context)
12
- @targets = mount_targets(hiera)
12
+ @targets = ['environment.conf', *mount_targets(hiera)]
13
13
  @modules = Modules::Storage.new(context: context).root if @project.join('Puppetfile').file?
14
14
  @targets |= ['modules'] if @modules
15
15
  @base = context.locations.workspace(context.workspace).join('environments')
@@ -17,14 +17,11 @@ module Empeira
17
17
  end
18
18
 
19
19
  def mounts
20
- return [bind(@project, @root)] if @targets.empty?
21
-
22
20
  [bind(@project, CONTROL), bind(@directory, @root),
23
21
  *(@modules ? [bind(@modules, "#{@root}/modules")] : [])]
24
22
  end
25
23
 
26
24
  def prepare
27
- return if @targets.empty?
28
25
  raise Error, 'Managed environment directory must not be a symlink' if @directory.symlink?
29
26
 
30
27
  FileUtils.mkdir_p(@directory, mode: 0o755)
@@ -81,13 +78,36 @@ module Empeira
81
78
  end
82
79
 
83
80
  def create_entry(relative, destination)
84
- if @targets.any? { |target| target == relative || target.start_with?("#{relative}/") }
81
+ if relative == 'environment.conf'
82
+ prepare_configuration(destination)
83
+ elsif @targets.any? { |target| target == relative || target.start_with?("#{relative}/") }
85
84
  create_target(relative, destination)
86
85
  else
87
86
  create_link(relative, destination)
88
87
  end
89
88
  end
90
89
 
90
+ def prepare_configuration(destination)
91
+ content = environment_configuration
92
+ return if !destination.symlink? && destination.file? && destination.binread == content
93
+
94
+ Tempfile.create('environment-', @directory) do |file|
95
+ file.chmod(0o644)
96
+ file.write(content)
97
+ file.flush
98
+ File.rename(file.path, destination)
99
+ end
100
+ end
101
+
102
+ def environment_configuration
103
+ source = @project.join('environment.conf')
104
+ content = source.exist? ? source.binread : ''
105
+ content = content.gsub(/^[ \t]*environment_timeout[ \t]*=[^\r\n]*/, 'environment_timeout = 0')
106
+ content += "\n" unless content.empty? || content.end_with?("\n")
107
+ content += "environment_timeout = 0\n" unless content.match?(/^environment_timeout = 0\r?$/)
108
+ content
109
+ end
110
+
91
111
  def create_target(relative, destination)
92
112
  raise Error, 'Managed environment destination is unsafe' if destination.symlink?
93
113
 
@@ -24,10 +24,10 @@ module Empeira
24
24
  end
25
25
 
26
26
  def verify!(resource)
27
- return if current(resource) == '0'
27
+ return if current(resource) == '0' && effective(resource) == '0'
28
28
 
29
29
  raise Error, 'Puppetserver environment_timeout is not 0 after startup; the custom entrypoint or ' \
30
- 'server confdir overrides the managed setting; inspect the server runtime contract'
30
+ 'server confdir/environment overrides the managed setting; inspect the server runtime contract'
31
31
  end
32
32
 
33
33
  private
@@ -36,6 +36,12 @@ module Empeira
36
36
  execute(resource, [ControlPlane::Health::RUBY, READER, '--confdir', confdir]).stdout.strip
37
37
  end
38
38
 
39
+ def effective(resource)
40
+ arguments = [PUPPET, 'config', 'print', 'environment_timeout', '--section', 'server',
41
+ '--confdir', confdir, '--environment', @plan.config.dig('server', 'environment')]
42
+ execute(resource, arguments).stdout.strip
43
+ end
44
+
39
45
  def execute(resource, arguments)
40
46
  result = @runtime.service_exec(resource, arguments)
41
47
  return result if result.success?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: empeira
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre.alpha.3
4
+ version: 0.1.0.pre.alpha.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Meißner (c8m6)
@@ -304,7 +304,7 @@ files:
304
304
  - resources/relay/server_start.sh
305
305
  homepage: https://github.com/c8m6/empeira
306
306
  licenses:
307
- - AGPL-3.0-only
307
+ - AGPL-3.0-or-later
308
308
  metadata:
309
309
  rubygems_mfa_required: 'true'
310
310
  rdoc_options: []