Gate Idunn actuation with typed deployment brake
This commit is contained in:
parent
1e51864109
commit
2b1f1ee64e
5 changed files with 1369 additions and 784 deletions
|
|
@ -54,6 +54,15 @@ Agents do not deploy daemons directly. They configure Idunn's target catalog,
|
|||
release targets, migration commands, and command boundaries so Idunn can run
|
||||
the shared rollout primitive and leave typed witnesses behind.
|
||||
|
||||
Deployment consequence has one final owner: immediately before every migration,
|
||||
deploy, or restart process spawn, Idunn re-opens the root-owned deployment-brake
|
||||
CultCache and the dedicated operator public anchor. A released record must bind
|
||||
the canonical runtime (`yggdrasil` for `yggdrasil-local`), exact source revision
|
||||
(or `restart:<daemon>`), exact request id, operator, signature, and short expiry.
|
||||
Missing, corrupt, engaged, expired, foreign, wrong-scope, substituted, or
|
||||
badly-signed state fails closed. Planning and lifecycle-command publication do
|
||||
not bypass or cache this decision.
|
||||
|
||||
Local package surfaces:
|
||||
|
||||
- Organ contract: `docs/idunn.md`
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,8 +3,12 @@ use cultcache_rs::{
|
|||
CultCacheEnvelope, CultCacheExpectedEnvelope, DatabaseEntry, SingleFileMessagePackBackingStore,
|
||||
};
|
||||
use cultnet_rs::{
|
||||
GameCultProviderHealthIdentity, IdunnServiceIdentity, ServiceIdentityProfile,
|
||||
ServiceIdentityTrustAnchor, derive_service_identity_id, enroll_service_identity_at,
|
||||
GameCultProviderHealthIdentity, IDUNN_DEPLOYMENT_BRAKE_AUTHORITY, IDUNN_DEPLOYMENT_BRAKE_ID,
|
||||
IDUNN_DEPLOYMENT_BRAKE_SCHEMA, IDUNN_DEPLOYMENT_BRAKE_SCOPE, IDUNN_DEPLOYMENT_RELEASE_PURPOSE,
|
||||
IdunnDeploymentBrakeObservation, IdunnDeploymentBrakeOperatorIdentity,
|
||||
IdunnDeploymentBrakeRecord, IdunnDeploymentBrakeReleasePurpose, IdunnServiceIdentity,
|
||||
ServiceIdentityProfile, ServiceIdentityTrustAnchor, derive_service_identity_id,
|
||||
enroll_service_identity_at, evaluate_idunn_deployment_brake,
|
||||
export_service_identity_trust_anchor, open_service_identity_at,
|
||||
};
|
||||
use odin_core::{
|
||||
|
|
@ -50,6 +54,26 @@ pub fn run(args: impl IntoIterator<Item = String>) -> Result<()> {
|
|||
let signer = open_service_identity_at::<IdunnServiceIdentity>(&private)?;
|
||||
export_service_identity_trust_anchor(&signer, &public)?;
|
||||
}
|
||||
"enroll-deployment-brake-operator" => {
|
||||
require_only(&options, &["private-store"])?;
|
||||
enroll_service_identity_at::<IdunnDeploymentBrakeOperatorIdentity>(&path(
|
||||
&options,
|
||||
"private-store",
|
||||
)?)?;
|
||||
}
|
||||
"export-deployment-brake-operator-anchor" => {
|
||||
require_only(&options, &["private-store", "public-anchor"])?;
|
||||
let private = path(&options, "private-store")?;
|
||||
let public = path(&options, "public-anchor")?;
|
||||
reject_alias(&private, &public)?;
|
||||
refuse_existing(&public, "deployment brake operator anchor")?;
|
||||
let signer =
|
||||
open_service_identity_at::<IdunnDeploymentBrakeOperatorIdentity>(&private)?;
|
||||
export_service_identity_trust_anchor(&signer, &public)?;
|
||||
}
|
||||
"deployment-brake-engage" => deployment_brake_engage(&options)?,
|
||||
"deployment-brake-release" => deployment_brake_release(&options)?,
|
||||
"deployment-brake-status" => deployment_brake_status(&options)?,
|
||||
"create-daemon-health-trust-binding" => create_health_binding(&options)?,
|
||||
"add-daemon-health-trust-binding" => add_health_binding(&options)?,
|
||||
"validate-daemon-health-trust-binding" => {
|
||||
|
|
@ -63,6 +87,204 @@ pub fn run(args: impl IntoIterator<Item = String>) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn deployment_brake_engage(options: &BTreeMap<String, String>) -> Result<()> {
|
||||
require_only(
|
||||
options,
|
||||
&[
|
||||
"store",
|
||||
"runtime-id",
|
||||
"owner",
|
||||
"reason",
|
||||
"observed-at-unix-millis",
|
||||
],
|
||||
)?;
|
||||
let observed = parse_u64(options, "observed-at-unix-millis")?;
|
||||
let record = IdunnDeploymentBrakeRecord {
|
||||
schema_version: IDUNN_DEPLOYMENT_BRAKE_SCHEMA.into(),
|
||||
brake_id: IDUNN_DEPLOYMENT_BRAKE_ID.into(),
|
||||
authority: IDUNN_DEPLOYMENT_BRAKE_AUTHORITY.into(),
|
||||
runtime_id: required(options, "runtime-id")?.into(),
|
||||
status: "engaged".into(),
|
||||
scope: IDUNN_DEPLOYMENT_BRAKE_SCOPE.into(),
|
||||
reason: required(options, "reason")?.into(),
|
||||
observed_at_unix_millis: observed,
|
||||
expires_at_unix_millis: None,
|
||||
authorization_id: None,
|
||||
authorization_purpose: None,
|
||||
authorized_release_id: None,
|
||||
authorized_deployment_id: None,
|
||||
authorized_by: None,
|
||||
authorization_issued_at_unix_millis: None,
|
||||
authorization_expires_at_unix_millis: None,
|
||||
signature_algorithm: None,
|
||||
signature: None,
|
||||
private_state_exposed: false,
|
||||
updated_by: required(options, "owner")?.into(),
|
||||
};
|
||||
record.validate()?;
|
||||
replace_brake(&path(options, "store")?, record, observed)
|
||||
}
|
||||
|
||||
fn deployment_brake_release(options: &BTreeMap<String, String>) -> Result<()> {
|
||||
require_only(
|
||||
options,
|
||||
&[
|
||||
"store",
|
||||
"private-store",
|
||||
"runtime-id",
|
||||
"owner",
|
||||
"reason",
|
||||
"authorization-id",
|
||||
"release-id",
|
||||
"deployment-id",
|
||||
"issued-at-unix-millis",
|
||||
"expires-at-unix-millis",
|
||||
],
|
||||
)?;
|
||||
let issued = parse_u64(options, "issued-at-unix-millis")?;
|
||||
let expires = parse_u64(options, "expires-at-unix-millis")?;
|
||||
let signer = open_service_identity_at::<IdunnDeploymentBrakeOperatorIdentity>(&path(
|
||||
options,
|
||||
"private-store",
|
||||
)?)?;
|
||||
let mut record = IdunnDeploymentBrakeRecord {
|
||||
schema_version: IDUNN_DEPLOYMENT_BRAKE_SCHEMA.into(),
|
||||
brake_id: IDUNN_DEPLOYMENT_BRAKE_ID.into(),
|
||||
authority: IDUNN_DEPLOYMENT_BRAKE_AUTHORITY.into(),
|
||||
runtime_id: required(options, "runtime-id")?.into(),
|
||||
status: "released".into(),
|
||||
scope: IDUNN_DEPLOYMENT_BRAKE_SCOPE.into(),
|
||||
reason: required(options, "reason")?.into(),
|
||||
observed_at_unix_millis: issued,
|
||||
expires_at_unix_millis: Some(expires),
|
||||
authorization_id: Some(required(options, "authorization-id")?.into()),
|
||||
authorization_purpose: Some(IDUNN_DEPLOYMENT_RELEASE_PURPOSE.into()),
|
||||
authorized_release_id: Some(required(options, "release-id")?.into()),
|
||||
authorized_deployment_id: Some(required(options, "deployment-id")?.into()),
|
||||
authorized_by: Some(signer.trust_anchor()?.identity_id),
|
||||
authorization_issued_at_unix_millis: Some(issued),
|
||||
authorization_expires_at_unix_millis: Some(expires),
|
||||
signature_algorithm: Some("ed25519".into()),
|
||||
signature: None,
|
||||
private_state_exposed: false,
|
||||
updated_by: required(options, "owner")?.into(),
|
||||
};
|
||||
record.signature = Some(
|
||||
signer
|
||||
.sign::<IdunnDeploymentBrakeReleasePurpose>(&rmp_serde::to_vec(&record)?)
|
||||
.signature,
|
||||
);
|
||||
record.validate()?;
|
||||
replace_brake(&path(options, "store")?, record, issued)
|
||||
}
|
||||
|
||||
fn deployment_brake_status(options: &BTreeMap<String, String>) -> Result<()> {
|
||||
allow_only(
|
||||
options,
|
||||
&[
|
||||
"store",
|
||||
"operator-anchor",
|
||||
"runtime-id",
|
||||
"release-id",
|
||||
"deployment-id",
|
||||
"now-unix-millis",
|
||||
],
|
||||
)?;
|
||||
let record = read_brake(&path(options, "store")?)?;
|
||||
if record.runtime_id != required(options, "runtime-id")? {
|
||||
bail!("deployment brake belongs to another runtime")
|
||||
}
|
||||
if record.status == "engaged" {
|
||||
println!(
|
||||
"engaged runtime={} scope={} owner={} reason={}",
|
||||
record.runtime_id, record.scope, record.updated_by, record.reason
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
let anchor = read_operator_anchor(&path(options, "operator-anchor")?)?;
|
||||
evaluate_idunn_deployment_brake(
|
||||
IdunnDeploymentBrakeObservation::Present(&record),
|
||||
&anchor,
|
||||
required(options, "runtime-id")?,
|
||||
required(options, "release-id")?,
|
||||
required(options, "deployment-id")?,
|
||||
parse_u64(options, "now-unix-millis")?,
|
||||
)
|
||||
.map_err(|d| anyhow!("deployment brake denied actuation: {d:?}"))?;
|
||||
println!(
|
||||
"released runtime={} release={} deployment={} owner={}",
|
||||
record.runtime_id,
|
||||
record.authorized_release_id.unwrap(),
|
||||
record.authorized_deployment_id.unwrap(),
|
||||
record.updated_by
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn replace_brake(path: &Path, record: IdunnDeploymentBrakeRecord, millis: u64) -> Result<()> {
|
||||
let store = SingleFileMessagePackBackingStore::new(path);
|
||||
let entries = store.pull_all_read_only_snapshot()?;
|
||||
let current = match entries.as_slice() {
|
||||
[] => None,
|
||||
[entry]
|
||||
if entry.r#type == IdunnDeploymentBrakeRecord::TYPE
|
||||
&& entry.key == IDUNN_DEPLOYMENT_BRAKE_ID =>
|
||||
{
|
||||
Some(entry.clone())
|
||||
}
|
||||
_ => bail!("deployment brake store is corrupt or ambiguous"),
|
||||
};
|
||||
let next = typed_envelope(
|
||||
IDUNN_DEPLOYMENT_BRAKE_ID,
|
||||
&record,
|
||||
IDUNN_DEPLOYMENT_BRAKE_SCHEMA,
|
||||
millis,
|
||||
)?;
|
||||
if !store.compare_exchange(
|
||||
&[CultCacheExpectedEnvelope {
|
||||
r#type: IdunnDeploymentBrakeRecord::TYPE.into(),
|
||||
key: IDUNN_DEPLOYMENT_BRAKE_ID.into(),
|
||||
current,
|
||||
}],
|
||||
&[next],
|
||||
)? {
|
||||
bail!("deployment brake changed during transition");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_brake(path: &Path) -> Result<IdunnDeploymentBrakeRecord> {
|
||||
let entries = SingleFileMessagePackBackingStore::new(path).pull_all_read_only_snapshot()?;
|
||||
let [entry] = entries.as_slice() else {
|
||||
bail!("deployment brake is missing or ambiguous")
|
||||
};
|
||||
if entry.r#type != IdunnDeploymentBrakeRecord::TYPE
|
||||
|| entry.key != IDUNN_DEPLOYMENT_BRAKE_ID
|
||||
|| entry.schema_id.as_deref() != Some(IDUNN_DEPLOYMENT_BRAKE_SCHEMA)
|
||||
{
|
||||
bail!("deployment brake is foreign")
|
||||
}
|
||||
let record: IdunnDeploymentBrakeRecord = rmp_serde::from_slice(&entry.payload)?;
|
||||
if rmp_serde::to_vec(&record)? != entry.payload {
|
||||
bail!("deployment brake is noncanonical")
|
||||
}
|
||||
record.validate()?;
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
fn read_operator_anchor(path: &Path) -> Result<ServiceIdentityTrustAnchor> {
|
||||
let entries = SingleFileMessagePackBackingStore::new(path).pull_all_read_only_snapshot()?;
|
||||
let [entry] = entries.as_slice() else {
|
||||
bail!("operator anchor is missing or ambiguous")
|
||||
};
|
||||
if entry.r#type != IdunnDeploymentBrakeOperatorIdentity::TRUST_ANCHOR_TYPE
|
||||
|| entry.key != IdunnDeploymentBrakeOperatorIdentity::TRUST_ANCHOR_KEY
|
||||
{
|
||||
bail!("operator anchor is foreign")
|
||||
}
|
||||
Ok(rmp_serde::from_slice(&entry.payload)?)
|
||||
}
|
||||
|
||||
fn validate_health_binding_store(path: &Path) -> Result<()> {
|
||||
let entries = SingleFileMessagePackBackingStore::new(path).pull_all_read_only_snapshot()?;
|
||||
if entries.is_empty() {
|
||||
|
|
@ -395,6 +617,13 @@ fn require_only(options: &BTreeMap<String, String>, names: &[&str]) -> Result<()
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn allow_only(options: &BTreeMap<String, String>, names: &[&str]) -> Result<()> {
|
||||
if let Some(name) = options.keys().find(|name| !names.contains(&name.as_str())) {
|
||||
bail!("unsupported option --{name}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn required<'a>(options: &'a BTreeMap<String, String>, name: &str) -> Result<&'a str> {
|
||||
options
|
||||
.get(name)
|
||||
|
|
@ -474,7 +703,7 @@ fn normalized(path: &Path) -> Result<PathBuf> {
|
|||
}
|
||||
|
||||
fn usage() -> &'static str {
|
||||
"Usage: idunn-provision enroll-idunn-identity --private-store <path>\n idunn-provision export-idunn-public-anchor --private-store <path> --public-anchor <path>\n idunn-provision create-daemon-health-trust-binding --output <path> --binding-id <id> --daemon <id> --health-contract <id> --source-runtime <id> --signer-public-key-hex <hex> --bound-at-unix-millis <u64> --release-binding-required <true|false>\n idunn-provision add-daemon-health-trust-binding --output <path> --binding-id <id> --daemon <id> --health-contract <id> --source-runtime <id> --signer-public-key-hex <hex> --bound-at-unix-millis <u64> --release-binding-required <true|false>\n idunn-provision validate-daemon-health-trust-binding --input <path>\n idunn-provision create-provider-projection-trust-anchor --output <path> --trust-anchor-id <id> --runtime-id <id> --idunn-public-anchor <path> --bound-at-unix-millis <u64> --expires-at-unix-millis <u64>\n idunn-provision validate-provider-projection-trust-anchor --input <path> --idunn-public-anchor <path>"
|
||||
"Usage: idunn-provision enroll-idunn-identity --private-store <path>\n idunn-provision export-idunn-public-anchor --private-store <path> --public-anchor <path>\n idunn-provision enroll-deployment-brake-operator --private-store <path>\n idunn-provision export-deployment-brake-operator-anchor --private-store <path> --public-anchor <path>\n idunn-provision deployment-brake-engage --store <path> --runtime-id <id> --owner <principal> --reason <text> --observed-at-unix-millis <u64>\n idunn-provision deployment-brake-release --store <path> --private-store <path> --runtime-id <id> --owner <principal> --reason <text> --authorization-id <id> --release-id <id> --deployment-id <id> --issued-at-unix-millis <u64> --expires-at-unix-millis <u64>\n idunn-provision deployment-brake-status --store <path> --operator-anchor <path> --runtime-id <id> [--release-id <id> --deployment-id <id> --now-unix-millis <u64>]\n idunn-provision create-daemon-health-trust-binding --output <path> --binding-id <id> --daemon <id> --health-contract <id> --source-runtime <id> --signer-public-key-hex <hex> --bound-at-unix-millis <u64> --release-binding-required <true|false>\n idunn-provision add-daemon-health-trust-binding --output <path> --binding-id <id> --daemon <id> --health-contract <id> --source-runtime <id> --signer-public-key-hex <hex> --bound-at-unix-millis <u64> --release-binding-required <true|false>\n idunn-provision validate-daemon-health-trust-binding --input <path>\n idunn-provision create-provider-projection-trust-anchor --output <path> --trust-anchor-id <id> --runtime-id <id> --idunn-public-anchor <path> --bound-at-unix-millis <u64> --expires-at-unix-millis <u64>\n idunn-provision validate-provider-projection-trust-anchor --input <path> --idunn-public-anchor <path>"
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -610,16 +839,45 @@ mod tests {
|
|||
let temp = TempDir::new()?;
|
||||
let store = temp.path().join("trust.cc");
|
||||
let path = store.to_str().unwrap().to_string();
|
||||
invoke(&binding_args("create-daemon-health-trust-binding", &path, "one", "daemon-one", "runtime-one"))?;
|
||||
invoke(&binding_args(
|
||||
"create-daemon-health-trust-binding",
|
||||
&path,
|
||||
"one",
|
||||
"daemon-one",
|
||||
"runtime-one",
|
||||
))?;
|
||||
let barrier = std::sync::Arc::new(std::sync::Barrier::new(3));
|
||||
let workers = [("two", "daemon-two", "runtime-two"), ("three", "daemon-three", "runtime-three")].map(|(id, daemon, runtime)| {
|
||||
let path = path.clone(); let barrier = barrier.clone();
|
||||
std::thread::spawn(move || { barrier.wait(); invoke(&binding_args("add-daemon-health-trust-binding", &path, id, daemon, runtime)) })
|
||||
let workers = [
|
||||
("two", "daemon-two", "runtime-two"),
|
||||
("three", "daemon-three", "runtime-three"),
|
||||
]
|
||||
.map(|(id, daemon, runtime)| {
|
||||
let path = path.clone();
|
||||
let barrier = barrier.clone();
|
||||
std::thread::spawn(move || {
|
||||
barrier.wait();
|
||||
invoke(&binding_args(
|
||||
"add-daemon-health-trust-binding",
|
||||
&path,
|
||||
id,
|
||||
daemon,
|
||||
runtime,
|
||||
))
|
||||
})
|
||||
});
|
||||
barrier.wait();
|
||||
let wins = workers.into_iter().map(|worker| worker.join().unwrap()).filter(Result::is_ok).count();
|
||||
let wins = workers
|
||||
.into_iter()
|
||||
.map(|worker| worker.join().unwrap())
|
||||
.filter(Result::is_ok)
|
||||
.count();
|
||||
assert!((1..=2).contains(&wins));
|
||||
assert_eq!(SingleFileMessagePackBackingStore::new(&store).pull_all_read_only_snapshot()?.len(), 1 + wins);
|
||||
assert_eq!(
|
||||
SingleFileMessagePackBackingStore::new(&store)
|
||||
.pull_all_read_only_snapshot()?
|
||||
.len(),
|
||||
1 + wins
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -877,4 +1135,148 @@ mod tests {
|
|||
partial.extend(["--release-binding-required", "sometimes"]);
|
||||
assert!(invoke(&partial).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brake_operator_can_engage_then_issue_one_exact_bounded_release() -> Result<()> {
|
||||
let temp = TempDir::new()?;
|
||||
let private = temp.path().join("operator-private.cc");
|
||||
let anchor = temp.path().join("operator-public.cc");
|
||||
let store = temp.path().join("brake.cc");
|
||||
invoke(&[
|
||||
"enroll-deployment-brake-operator",
|
||||
"--private-store",
|
||||
private.to_str().unwrap(),
|
||||
])?;
|
||||
invoke(&[
|
||||
"export-deployment-brake-operator-anchor",
|
||||
"--private-store",
|
||||
private.to_str().unwrap(),
|
||||
"--public-anchor",
|
||||
anchor.to_str().unwrap(),
|
||||
])?;
|
||||
invoke(&[
|
||||
"deployment-brake-engage",
|
||||
"--store",
|
||||
store.to_str().unwrap(),
|
||||
"--runtime-id",
|
||||
"yggdrasil",
|
||||
"--owner",
|
||||
"operator/metacrat",
|
||||
"--reason",
|
||||
"sleep mode",
|
||||
"--observed-at-unix-millis",
|
||||
"100",
|
||||
])?;
|
||||
assert_eq!(read_brake(&store)?.status, "engaged");
|
||||
invoke(&[
|
||||
"deployment-brake-release",
|
||||
"--store",
|
||||
store.to_str().unwrap(),
|
||||
"--private-store",
|
||||
private.to_str().unwrap(),
|
||||
"--runtime-id",
|
||||
"yggdrasil",
|
||||
"--owner",
|
||||
"operator/metacrat",
|
||||
"--reason",
|
||||
"one deployment",
|
||||
"--authorization-id",
|
||||
"auth/r4",
|
||||
"--release-id",
|
||||
"commit-4",
|
||||
"--deployment-id",
|
||||
"request-4",
|
||||
"--issued-at-unix-millis",
|
||||
"200",
|
||||
"--expires-at-unix-millis",
|
||||
"800",
|
||||
])?;
|
||||
invoke(&[
|
||||
"deployment-brake-status",
|
||||
"--store",
|
||||
store.to_str().unwrap(),
|
||||
"--operator-anchor",
|
||||
anchor.to_str().unwrap(),
|
||||
"--runtime-id",
|
||||
"yggdrasil",
|
||||
"--release-id",
|
||||
"commit-4",
|
||||
"--deployment-id",
|
||||
"request-4",
|
||||
"--now-unix-millis",
|
||||
"500",
|
||||
])?;
|
||||
assert!(
|
||||
invoke(&[
|
||||
"deployment-brake-status",
|
||||
"--store",
|
||||
store.to_str().unwrap(),
|
||||
"--operator-anchor",
|
||||
anchor.to_str().unwrap(),
|
||||
"--runtime-id",
|
||||
"yggdrasil",
|
||||
"--release-id",
|
||||
"substituted",
|
||||
"--deployment-id",
|
||||
"request-4",
|
||||
"--now-unix-millis",
|
||||
"500"
|
||||
])
|
||||
.is_err()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brake_compare_exchange_rejects_stale_transition_snapshot() -> Result<()> {
|
||||
let temp = TempDir::new()?;
|
||||
let store_path = temp.path().join("brake.cc");
|
||||
let base = IdunnDeploymentBrakeRecord {
|
||||
schema_version: IDUNN_DEPLOYMENT_BRAKE_SCHEMA.into(),
|
||||
brake_id: IDUNN_DEPLOYMENT_BRAKE_ID.into(),
|
||||
authority: IDUNN_DEPLOYMENT_BRAKE_AUTHORITY.into(),
|
||||
runtime_id: "yggdrasil".into(),
|
||||
status: "engaged".into(),
|
||||
scope: IDUNN_DEPLOYMENT_BRAKE_SCOPE.into(),
|
||||
reason: "first".into(),
|
||||
observed_at_unix_millis: 100,
|
||||
expires_at_unix_millis: None,
|
||||
authorization_id: None,
|
||||
authorization_purpose: None,
|
||||
authorized_release_id: None,
|
||||
authorized_deployment_id: None,
|
||||
authorized_by: None,
|
||||
authorization_issued_at_unix_millis: None,
|
||||
authorization_expires_at_unix_millis: None,
|
||||
signature_algorithm: None,
|
||||
signature: None,
|
||||
private_state_exposed: false,
|
||||
updated_by: "operator/a".into(),
|
||||
};
|
||||
replace_brake(&store_path, base.clone(), 100)?;
|
||||
let stale = SingleFileMessagePackBackingStore::new(&store_path)
|
||||
.pull_all_read_only_snapshot()?
|
||||
.remove(0);
|
||||
let mut changed = base.clone();
|
||||
changed.reason = "racing writer".into();
|
||||
changed.observed_at_unix_millis = 101;
|
||||
replace_brake(&store_path, changed, 101)?;
|
||||
let candidate = typed_envelope(
|
||||
IDUNN_DEPLOYMENT_BRAKE_ID,
|
||||
&base,
|
||||
IDUNN_DEPLOYMENT_BRAKE_SCHEMA,
|
||||
102,
|
||||
)?;
|
||||
assert!(
|
||||
!SingleFileMessagePackBackingStore::new(&store_path).compare_exchange(
|
||||
&[CultCacheExpectedEnvelope {
|
||||
r#type: IdunnDeploymentBrakeRecord::TYPE.into(),
|
||||
key: IDUNN_DEPLOYMENT_BRAKE_ID.into(),
|
||||
current: Some(stale)
|
||||
}],
|
||||
&[candidate]
|
||||
)?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ User=idunn
|
|||
Group=idunn
|
||||
# Operational cancellation ceiling only. Target daemons own readiness through
|
||||
# typed health; this duration must never be interpreted as deployment success.
|
||||
ExecStart=/usr/local/bin/idunn --swarm-profile yggdrasil-local --repo-root /srv/odin/source --store /var/lib/gamecult/idunn/yggdrasil.keepalive.cc --release-authority-store /srv/bifrost/state/repository-release-authority.cc --rudp-health-bind 10.77.0.1:17870 --trusted-epiphany-health-identity-store /etc/gamecult/idunn/epiphany-health-identity.ccmp --command-timeout-seconds 21600 --execute
|
||||
ExecStart=/usr/local/bin/idunn --swarm-profile yggdrasil-local --repo-root /srv/odin/source --store /var/lib/gamecult/idunn/yggdrasil.keepalive.cc --release-authority-store /srv/bifrost/state/repository-release-authority.cc --deployment-brake-store /var/lib/gamecult/idunn/deployment-brake.cc --deployment-brake-operator-anchor /etc/gamecult/idunn/deployment-brake-operator-anchor.cc --rudp-health-bind 10.77.0.1:17870 --trusted-epiphany-health-identity-store /etc/gamecult/idunn/epiphany-health-identity.ccmp --command-timeout-seconds 21600 --execute
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
TimeoutStopSec=20
|
||||
|
|
|
|||
2
vendor/cultnet-rs
vendored
2
vendor/cultnet-rs
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit a1001dc855aa4de04859d01d8be4c966f510f557
|
||||
Subproject commit 402f6e7f0b938a251fc9443d734a6c86d5c1b40e
|
||||
Loading…
Reference in a new issue