diff --git a/commands/src/apply/command.rs b/commands/src/apply/command.rs index 81b66338..c2714d4c 100644 --- a/commands/src/apply/command.rs +++ b/commands/src/apply/command.rs @@ -16,18 +16,18 @@ pub fn run( opts: &ApplyOpts, providers: TelemetryProviders, ) -> ExitCode { - let t_start = Timestamp::now(); // For the report - let start_time = Instant::now(); // For metrics + let start_timestamp = Timestamp::now(); // goes in the the report + let run_timer = Instant::now(); // calculate run duration for metrics let exit_code = match config::compile(host_file, opts) { Ok(cfg) => { let lock = lockfile::acquire(opts); - if lockfile::is_on(&start_time, &lock) { + if lockfile::is_on(&run_timer, &lock) { ExitCode::FAILURE } else { let run_result = Applicator::from(cfg).run(opts); - let elapsed_time = start_time.elapsed(); + let elapsed_time = run_timer.elapsed(); tracing::info!("Run time: {:.3?}", elapsed_time); let run_exit_code = match run_result { @@ -44,7 +44,7 @@ pub fn run( fail_phase: None, summary: Some(&apply_summary), duration: &elapsed_time, - t_start, + t_start: start_timestamp, t_end: Timestamp::now(), host_file: host_file.map(|f| f.to_owned()), opts, @@ -69,7 +69,7 @@ pub fn run( fail_phase: Some(FailPhase::Apply), summary: None, duration: &elapsed_time, - t_start, + t_start: start_timestamp, t_end: Timestamp::now(), host_file: host_file.map(|f| f.to_owned()), opts, @@ -100,7 +100,7 @@ pub fn run( } }; - metrics::send(ApplyStatus::Fail(e.into()), &start_time.elapsed()); + metrics::send(ApplyStatus::Fail(e.into()), &run_timer.elapsed()); ExitCode::FAILURE } }; diff --git a/doc/commands/apply.md b/doc/commands/apply.md index 8b9a7330..4c194f6d 100644 --- a/doc/commands/apply.md +++ b/doc/commands/apply.md @@ -75,12 +75,12 @@ There are options: Useful for cleaning up during development, but should be used with extreme caution. It has no short value, so you can't type it by accident. - `--no-lock` makes Gurp not check for, create, or remove its runtime lock file. -- `--no-report` Gurp normally writes a JSON report in `/var/log/` at the end - of a run. This option turns of that report. -- `--pre-run-noop` Runs a no-op before the normal apply: this is very - fast and minimises the chance of a partially applied config. Can be - problematic in some circumstances. For instance, if your config adds a `pkg` - repo then installs packages from it. +- `--no-report` Gurp normally writes a JSON report in `/var/log/` at the end of + a run. This option turns of that report. +- `--pre-run-noop` Runs a no-op before the normal apply: this is very fast and + minimises the chance of a partially applied config. Can be problematic in some + circumstances. For instance, if your config adds a `pkg` repo then installs + packages from it. - `--post-run-noop` If a Gurp `apply` makes a change to the system state, this option makes it immediately re-run the config as a no-op. If the no-op finds changes must be made, Gurp assumes it failed to correctly assert state, and @@ -92,10 +92,11 @@ There are options: - `-O, --only ` Makes Gurp only apply resources whose IDs match the given Rust regex. - `-D, --define ` Can be used multiple time, with the values used to - build a Janet struct, `gurp-user-defs` with global scope, visible during the - compile phase. If `DEFINE` is of the form `key=value`, the struct gets `key` - as a symbol and `value` as a string. If `DEFINE` is `key` only, the struct - gets `key` as a symbol, with a value of boolean `true`. + build a Janet struct, `gurp-user-defs` set as a dynamic binding with global + scope, visible during the compile phase. If `DEFINE` is of the form + `key=value`, the struct gets `key` as a symbol and `value` as a string. If + `DEFINE` is `key` only, the struct gets `key` as a symbol, with a value of + boolean `true`. - `-S, --splay ` pauses for a random time up to a maximum of `SPLAY` seconds prior to applying. A splay time can also be set via `control-data`, but the command-line value takes precedence. diff --git a/doc/doers/zone.md b/doc/doers/zone.md index fb900361..82bddbb0 100644 --- a/doc/doers/zone.md +++ b/doc/doers/zone.md @@ -249,6 +249,7 @@ None ## Notes - You must supply exactly one of `:file` and `:server`. +- On a bootstrap run, `gurp-user-defs` contains `:is-bootstrap true`, so you can change behaviour on an initial run. # zone/cloudinit diff --git a/doers/src/zone/container.rs b/doers/src/zone/container.rs index 02534085..4f2d3401 100644 --- a/doers/src/zone/container.rs +++ b/doers/src/zone/container.rs @@ -186,6 +186,8 @@ fn bootstrap(zone: &str, conf: &ZoneConfig, opts: &ApplyOpts) -> anyhow::Result< bootstrap_args.push(format!("--metrics-to={metrics_host}")); } + bootstrap_args.push("-Dis-bootstrap".to_owned()); + ensure!( exactly_one_some!(bootstrap_conf.server, bootstrap_conf.file), "bootstrap requires exactly one of :file and :server" diff --git a/embed/src/client.rs b/embed/src/client.rs index 9a1b87a6..84b1f67d 100644 --- a/embed/src/client.rs +++ b/embed/src/client.rs @@ -40,7 +40,7 @@ pub fn gurp(vmopts: &ApplyVmOpts, destroy: bool) -> anyhow::Result } if vmopts.define.is_empty() { - janet_instructions.push_str(r#"(defglobal "gurp-user-defs" {})"#); + janet_instructions.push_str(r#"(setdyn :gurp-user-defs {})"#); } else { janet_instructions.push_str(&define_string(vmopts)); } @@ -60,7 +60,7 @@ fn destroyer_string() -> String { /// contain an '=', are split on that char, with the first part becoming a struct key (keyword) /// and the second becoming the corresponding value (string). If there is no '=', the whole value /// becomes a key (keyword) and the value is set to true (boolean). -fn define_string(vmopts: &ApplyVmOpts) -> String { +pub fn define_string(vmopts: &ApplyVmOpts) -> String { tracing::debug!("setting gurp-user-defs"); let bindings = vmopts @@ -87,7 +87,7 @@ fn define_string(vmopts: &ApplyVmOpts) -> String { if bindings.is_empty() { String::new() } else { - format!(r#"(defglobal "gurp-user-defs" (struct {bindings}))"#) + format!(r#"(setdyn :gurp-user-defs (struct {bindings}))"#) } } @@ -125,7 +125,7 @@ mod tests { }; assert_eq!( - r#"(defglobal "gurp-user-defs" (struct (keyword "boolean") true (keyword "key") "value"))"#, + r#"(setdyn :gurp-user-defs (struct (keyword "boolean") true (keyword "key") "value"))"#, define_string(&opts) ); } diff --git a/embed/src/compiler.rs b/embed/src/compiler.rs index 2e244e38..c326e49f 100644 --- a/embed/src/compiler.rs +++ b/embed/src/compiler.rs @@ -1,4 +1,4 @@ -use crate::client; +use super::client; use anyhow::Context; use camino::Utf8Path; use common::constants::SERVER_PORT; diff --git a/janet/src/doers/zone/bootstrap.janet b/janet/src/doers/zone/bootstrap.janet index be66b49e..07b2cc48 100644 --- a/janet/src/doers/zone/bootstrap.janet +++ b/janet/src/doers/zone/bootstrap.janet @@ -13,7 +13,9 @@ bootstrap"}} :notes - ["You must supply exactly one of `:file` and `:server`."]) + ["You must supply exactly one of `:file` and `:server`." + "On a bootstrap run, `gurp-user-defs` contains `:is-bootstrap true`, so + you can change behaviour on an initial run."]) (defn bootstrap "Given a spec, return config to bootstrap a zone" diff --git a/janet/src/dsl.janet b/janet/src/dsl.janet index bd38bd67..f0e289ce 100644 --- a/janet/src/dsl.janet +++ b/janet/src/dsl.janet @@ -306,19 +306,21 @@ (default key-column 0) (tabular-rows->struct (lines tabular-output) key-column)) +(defn gurp-user-defs + "A shim around the :gurp-user-defs dyn for backward compatibility" + [key] + (let [user-defs (dyn :gurp-user-defs {})] + (user-defs (keyword key)))) + (defn recreate? "Returns 1 (true) or 0 (false) depending on whether the user has defined :recreate-zone- or :recreate-all-zones. To be used in conjunction with the zone :recreate property" [zone-name] - (let [user-defs (get (curenv) 'gurp-user-defs)] - (if (not user-defs) - 0 - (if (or - ((user-defs :value) (keyword "recreate-all-zones")) - ((user-defs :value) (keyword "recreate-zone-" zone-name))) - 1 - 0)))) + (if (or (gurp-user-defs :recreate-all-zones) + (gurp-user-defs (keyword "recreate-zone-" zone-name))) + 1 + 0)) (defn num-field-sort "Sort a file when each line's first field is numeric"