Repository navigation
Deployment risk conformance: one environment model for the repo #13639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,14 @@ import gunbc.fabric_storage_address { fabric_storage_route_prefix, fabric_storag | |
| import std.types { String, NonEmptyStr, FilePath, Int, Bool, Port, List } | ||
| import product.host_identity { HostIdentity } | ||
| import gunbc.fleet_intent_network { fleet_intent_network } | ||
| import gunbc.fleet_host_identity { operator_host_srv1 } | ||
| import gunbc.roadmap_dashboard_instance { HostDashboardInstance, dashboard_instance_fabric_storage_root, srv1_live_dashboard_instance, dashboard_instance_child } | ||
| import gunbc.roadmap_dashboard_instance { | ||
| HostDashboardInstance, | ||
| dashboard_instance_fabric_storage_root, | ||
| dashboard_instance_child, | ||
| prod_role_holder_dashboard_instance, | ||
| srv1_live_dashboard_instance, | ||
| } | ||
| import gunbc.deployment_risk { ProdRoleSelection, prod_role_selection } | ||
| import std.effect_grant { Read, Write, Execute } | ||
| import extdeps.tailscale.serve { tailscale_serve_unix_proxy_peer } | ||
| import gunbc.ownership { Ensured } | ||
|
|
@@ -67,19 +73,37 @@ data fabric_storage_door_proxy_peer: NonEmptyStr = tailscale_serve_unix_proxy_pe | |
|
|
||
|
|
||
| // The store root is the placed instance's fabric DB root -- one authority with the deployment that | ||
| // creates it (gunbc.roadmap_dashboard_instance dashboard_instance_fabric_storage_root). | ||
| // creates it (gunbc.roadmap_dashboard_instance dashboard_instance_fabric_storage_root). Placement | ||
| // follows the prod-role holder. Door and store-root PATH helpers still need a HostDashboardInstance | ||
| // when the live row is NoProdRole; that arm names srv1_live only as a path constructor residual | ||
| // (DESIGN 3b stated divergence). The placement decision itself is Unplaced there and never treats | ||
| // that residual as ownership. | ||
| fn fabric_storage_placed_instance_under(selection: ProdRoleSelection) -> HostDashboardInstance? { | ||
| prod_role_holder_dashboard_instance(selection: selection) | ||
| } | ||
|
|
||
| fn fabric_storage_placed_instance() -> HostDashboardInstance { | ||
| srv1_live_dashboard_instance() | ||
| match fabric_storage_placed_instance_under(selection: prod_role_selection) { | ||
| Present { value: i } => i | ||
| Absent => srv1_live_dashboard_instance() | ||
| } | ||
| } | ||
|
|
||
| fn fabric_storage_store_root() -> NonEmptyStr { | ||
| dashboard_instance_fabric_storage_root(instance: fabric_storage_placed_instance()) as String as NonEmptyStr | ||
| } | ||
|
|
||
| fn fabric_storage_placed_on_under(selection: ProdRoleSelection, instance: HostDashboardInstance) -> Bool { | ||
| match fabric_storage_placed_instance_under(selection: selection) { | ||
| Absent => false | ||
| Present { value: holder } => (instance.instance_id as String) == (holder.instance_id as String) | ||
| } | ||
| } | ||
|
|
||
| // WHETHER AN INSTANCE HOLDS THE PLACEMENT: the deployment owns the store root and the endpoint only | ||
| // there. Identity is the instance's own id, not a path comparison. | ||
| fn fabric_storage_placed_on(instance: HostDashboardInstance) -> Bool { | ||
| (instance.instance_id as String) == (fabric_storage_placed_instance().instance_id as String) | ||
| fabric_storage_placed_on_under(selection: prod_role_selection, instance: instance) | ||
| } | ||
|
|
||
| fn fabric_storage_endpoint_for(host: HostIdentity) -> NonEmptyStr? { | ||
|
|
@@ -89,13 +113,25 @@ fn fabric_storage_endpoint_for(host: HostIdentity) -> NonEmptyStr? { | |
| } | ||
| } | ||
|
|
||
| fn fabric_storage_placement() -> FabricStoragePlacement { | ||
| match fabric_storage_endpoint_for(host: operator_host_srv1) { | ||
| fn fabric_storage_placement_under(selection: ProdRoleSelection) -> FabricStoragePlacement { | ||
| match fabric_storage_placed_instance_under(selection: selection) { | ||
| Absent => FabricStorageUnplaced | ||
| Present { value: e } => FabricStoragePlaced { host: operator_host_srv1, store_root: fabric_storage_store_root(), endpoint: e } | ||
| Present { value: instance } => | ||
| match fabric_storage_endpoint_for(host: instance.host_identity as HostIdentity) { | ||
| Absent => FabricStorageUnplaced | ||
| Present { value: e } => FabricStoragePlaced { | ||
| host: instance.host_identity as HostIdentity, | ||
| store_root: dashboard_instance_fabric_storage_root(instance: instance) as String as NonEmptyStr, | ||
|
Comment on lines
+123
to
+124
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
With Useful? React with 👍 / 👎. |
||
| endpoint: e, | ||
|
Comment on lines
+122
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn fabric_storage_placement() -> FabricStoragePlacement { | ||
| fabric_storage_placement_under(selection: prod_role_selection) | ||
| } | ||
|
|
||
| // ── WHO WRITES THE STORE, AND SO WHAT ITS DIRECTORIES ARE ──────────────────────────────────────── | ||
| // | ||
| // ONE PRINCIPAL WRITES THIS STORE'S FILES: the served endpoint (gunbc.live_deploy.emit | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,7 +51,9 @@ import gunbc.roadmap_dashboard_instance { | |
| dashboard_instance_compute_root, | ||
| dashboard_instance_provider_state_root, | ||
| dashboard_instance_tailnet_door_socket, | ||
| prod_role_holder_dashboard_instance, | ||
| } | ||
| import gunbc.deployment_risk { ProdRoleSelection, prod_role_selection } | ||
| import extdeps.http.server { HttpServerListenConfig } | ||
| import gunbc.auth.approval_broker_endpoint { approval_broker_listen_port, approval_broker_listen_host, approval_broker_confirm_front_door } | ||
| import gunbc.auth.approval_broker_cutover { approval_front_door_mounts_installed, approval_broker_front_door_endpoints } | ||
|
|
@@ -414,20 +416,21 @@ data approval_broker_serve_entry_file: NonEmptyStr = "dag/gunbc/auth/approval_br | |
| // | ||
| // SO OWNERSHIP IS DECLARED AND THE EMITTER READS IT. One instance per host owns the broker unit; | ||
| // every other spec emits no broker step at all, writes nothing and restarts nothing. | ||
| // THE INSTANCE THAT OWNS THE HOST'S BROKER. It is srv1-live because that is where the approval | ||
| // store is: gunbc.auth.approval_decision_store approval_decision_store_root is one path on one | ||
| // host, and the whole point of a single-writer store is that exactly one process opens it. | ||
| // | ||
| // IT IS A DECLARED OWNER RATHER THAN A DERIVATION FROM THE INSTANCE because "which deployment runs | ||
| // the approval loop" is not recoverable from any field an instance carries -- srv1-live and | ||
| // srv1-lab are the same shape on the same host, and the only thing that distinguishes them for this | ||
| // purpose is that one of them was chosen. Deriving it would mean inventing a rule that happens to | ||
| // select production today. | ||
| // | ||
| // A SECOND HOST SERVING APPROVALS IS WHAT RETIRES THIS ROW, at which point the owner becomes a | ||
| // per-host lookup and the store root becomes host-derived with it. Until then a single row is the | ||
| // honest shape: there is one approval loop, and it has one home. | ||
| data gunbc_approval_broker_owning_instance: NonEmptyStr = "srv1-live" | ||
| // THE INSTANCE THAT OWNS THE HOST'S BROKER follows the prod role. The store is still one path on | ||
| // one host (gunbc.auth.approval_decision_store approval_decision_store_root); which instance may | ||
| // install or restart the unit is the deployment that holds ProdRole, not a constructor name. | ||
| // Moving prod_role_selection moves the owner. NoProdRole means nobody owns the unit -- a twin | ||
| // must not install it there either. | ||
| fn gunbc_approval_broker_owning_instance_under(selection: ProdRoleSelection) -> NonEmptyStr? { | ||
| match prod_role_holder_dashboard_instance(selection: selection) { | ||
| Absent => none | ||
| Present { value: holder } => Present { value: holder.instance_id } | ||
|
Comment on lines
+425
to
+427
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎.
Comment on lines
+424
to
+427
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the role is moved to srv2, this makes the srv2 instance own and start the broker, but Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
|
|
||
| fn gunbc_approval_broker_owning_instance() -> NonEmptyStr? { | ||
| gunbc_approval_broker_owning_instance_under(selection: prod_role_selection) | ||
| } | ||
|
|
||
| data gunbc_approval_broker_unit_name: NonEmptyStr = "gunbc-approval-broker.service" | ||
|
|
||
|
|
@@ -456,8 +459,15 @@ fn deployment_owns_the_host_approval_broker(spec: DeploymentSpec) -> Bool { | |
|
|
||
| // THE ONE OWNERSHIP COMPARISON, read by the spec-level predicate above and by the front-door member, | ||
| // which is built before a spec exists and so holds only the instance. | ||
| fn instance_owns_the_host_approval_broker_under(selection: ProdRoleSelection, instance_id: NonEmptyStr) -> Bool { | ||
| match gunbc_approval_broker_owning_instance_under(selection: selection) { | ||
| Absent => false | ||
| Present { value: owner } => (instance_id as String) == (owner as String) | ||
| } | ||
| } | ||
|
|
||
| fn instance_owns_the_host_approval_broker(instance_id: NonEmptyStr) -> Bool { | ||
| (instance_id as String) == (gunbc_approval_broker_owning_instance as String) | ||
| instance_owns_the_host_approval_broker_under(selection: prod_role_selection, instance_id: instance_id) | ||
| } | ||
|
|
||
| fn gunbc_approval_broker_unit_path() -> NonEmptyStr { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ module gunbc.roadmap_dashboard_instance_apply | |
| import std.algebra { trim } | ||
|
|
||
| import std.types { String, NonEmptyStr, Bool, List, FilePath, GitRef, CommitSha } | ||
| import gunbc.deployment_risk { ProdRoleSelection, prod_role_selection } | ||
| import std.process { ProcessExit, ExitSuccess, ExitFailure, exit_failure } | ||
| import extdeps.shell | ||
| import extdeps.filesystem.filesystem_io | ||
|
|
@@ -93,6 +94,7 @@ import gunbc.roadmap_dashboard_instance { | |
| DashboardInstanceRefused, | ||
| srv1_live_dashboard_instance, | ||
| srv1_lab_dashboard_instance, | ||
| prod_role_holder_dashboard_instance, | ||
| srv2_lab_dashboard_instance, | ||
| dashboard_instance_provider_state_root, | ||
| dashboard_instance_provider_executable, | ||
|
|
@@ -789,6 +791,25 @@ type DashboardProductionPeer | |
| = ProductionPeer { instance: HostDashboardInstance } | ||
| | NoProductionPeer { host: NonEmptyStr } | ||
|
|
||
| // Isolation and liveness compare against the prod-role holder on the SAME HOST, never against a | ||
| // constructor named "live". A holder on another host is not a peer of this apply; NoProdRole is | ||
| // unpeered. Same-host TestRisk applies still refuse overlapping owned paths with whoever holds prod. | ||
| fn dashboard_production_peer_on_host_under(selection: ProdRoleSelection, host: NonEmptyStr) -> DashboardProductionPeer { | ||
| match prod_role_holder_dashboard_instance(selection: selection) { | ||
| Absent => NoProductionPeer { host: host } | ||
| Present { value: holder } => | ||
| if (holder.host_identity as String) == (host as String) { | ||
| ProductionPeer { instance: holder } | ||
|
Comment on lines
+800
to
+802
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If Useful? React with 👍 / 👎. |
||
| } else { | ||
| NoProductionPeer { host: host } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn dashboard_production_peer_on_host(host: NonEmptyStr) -> DashboardProductionPeer { | ||
| dashboard_production_peer_on_host_under(selection: prod_role_selection, host: host) | ||
| } | ||
|
|
||
| type DashboardProductionReading | ||
| = ProductionRead { snapshot: DashboardProductionSnapshot } | ||
| | ProductionUnpeered { host: NonEmptyStr } | ||
|
|
@@ -1968,14 +1989,14 @@ fn srv2_deploy_ensure_roots_cli() -> ProcessExit { | |
| fn srv1_lab_dashboard_apply() -> DashboardInstanceApplyResult { | ||
| dashboard_instance_apply( | ||
| instance: srv1_lab_dashboard_instance(), | ||
| production: ProductionPeer { instance: srv1_live_dashboard_instance() }, | ||
| production: dashboard_production_peer_on_host(host: srv1_lab_dashboard_instance().host_identity), | ||
|
Comment on lines
1989
to
+1992
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This update wires the new role-derived peer lookup only into the srv1 lab entries. With the newly tested role assignment to Useful? React with 👍 / 👎. |
||
| ) | ||
| } | ||
|
|
||
| fn srv1_lab_dashboard_preflight() -> DashboardApplyPreflightResult { | ||
| dashboard_instance_apply_preflight( | ||
| instance: srv1_lab_dashboard_instance(), | ||
| production: ProductionPeer { instance: srv1_live_dashboard_instance() }, | ||
| production: dashboard_production_peer_on_host(host: srv1_lab_dashboard_instance().host_identity), | ||
| ) | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the role is moved to the newly tested
srv2_deploy_deployment, this selects its dashboard bind of0.0.0.0(roadmap_dashboard_instance.dag:1210), soapproval_identity_trust_forreturnsIdentityHeaderTrustNotEstablishedand every redemption is rejected atapproval_decision_store.dag:593-596. The broker itself still bindsapproval_broker_listen_host, independently of the dashboard instance (live_deploy/spec.dag:547-560), so tying trust to the holder's dashboard bind makes the moved broker unable to approve or deny anything even when its actual loopback/front-door topology is safe.Useful? React with 👍 / 👎.