mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-02 07:34:45 +08:00
feat(server): separate image preparation and admission deadlines
Give sandbox image preparation its own deadline and start the admission deadline after preparation finishes. Persist both phases across gateway restarts and show recovery guidance for the phase that expired. Preserve the current service authorization schema and regenerate the Go bindings with the preparation timestamps. Fixes #3952 Related to #3955 Signed-off-by: Shiju <shiju@nvidia.com>
This commit is contained in:
@@ -1230,14 +1230,15 @@ pub async fn sandbox_create(
|
||||
SandboxPhase::Error => {
|
||||
drop(stream);
|
||||
drop(client);
|
||||
let provisioning_timed_out = last_sandbox
|
||||
let timed_out_provisioning = last_sandbox
|
||||
.status
|
||||
.as_ref()
|
||||
.and_then(|status| status.provisioning.as_ref())
|
||||
.is_some_and(|record| record.timeout_time.is_some());
|
||||
let create_result = if provisioning_timed_out {
|
||||
.filter(|record| record.timeout_time.is_some());
|
||||
let create_result = if let Some(record) = timed_out_provisioning {
|
||||
Err(miette::miette!(
|
||||
"{last_error_reason}\nSandbox '{sandbox_name}' was retained. Inspect it with `openshell sandbox get {sandbox_name}`; repair its configuration, then run `openshell sandbox start {sandbox_name}` after cleanup completes."
|
||||
"{}",
|
||||
retained_sandbox_timeout_message(&sandbox_name, &last_error_reason, record)
|
||||
))
|
||||
} else if last_error_reason.is_empty() {
|
||||
Err(miette::miette!(
|
||||
@@ -1271,6 +1272,24 @@ pub async fn sandbox_create(
|
||||
}
|
||||
}
|
||||
|
||||
/// Use the persisted phase to select recovery guidance. Preparation may expire
|
||||
/// before any policy is evaluated, so it must not tell the user to repair policy.
|
||||
fn retained_sandbox_timeout_message(
|
||||
sandbox_name: &str,
|
||||
error_reason: &str,
|
||||
record: &openshell_core::proto::SandboxProvisioning,
|
||||
) -> String {
|
||||
let recovery = if record.preparation_deadline.is_some() && record.admission_start_time.is_none()
|
||||
{
|
||||
"check image preparation and supervisor startup diagnostics and the gateway's `image_preparation_timeout_seconds` budget"
|
||||
} else {
|
||||
"repair its configuration"
|
||||
};
|
||||
format!(
|
||||
"{error_reason}\nSandbox '{sandbox_name}' was retained. Inspect it with `openshell sandbox get {sandbox_name}`; {recovery}, then run `openshell sandbox start {sandbox_name}` after cleanup completes."
|
||||
)
|
||||
}
|
||||
|
||||
/// Resolved source for the `--from` flag on `sandbox create`.
|
||||
#[derive(Debug)]
|
||||
enum ResolvedSource {
|
||||
@@ -2783,6 +2802,15 @@ fn sandbox_to_json(sandbox: &Sandbox) -> serde_json::Value {
|
||||
"configuration_change_id": record.configuration_change_id,
|
||||
"configuration_change_time": record.configuration_change_time.as_ref().map(ToString::to_string),
|
||||
"first_rejection_time": record.first_rejection_time.as_ref().map(ToString::to_string),
|
||||
"phase": if record.deadline.is_none() && record.timeout_time.is_none() {
|
||||
"ready"
|
||||
} else if record.preparation_deadline.is_some() && record.admission_start_time.is_none() {
|
||||
"preparation"
|
||||
} else {
|
||||
"admission"
|
||||
},
|
||||
"preparation_deadline": record.preparation_deadline.as_ref().map(ToString::to_string),
|
||||
"admission_start_time": record.admission_start_time.as_ref().map(ToString::to_string),
|
||||
"deadline": record.deadline.as_ref().map(ToString::to_string),
|
||||
"timeout_time": record.timeout_time.as_ref().map(ToString::to_string),
|
||||
"cleanup_completed_time": record.cleanup_completed_time.as_ref().map(ToString::to_string),
|
||||
@@ -7792,6 +7820,79 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retained_sandbox_timeout_message_matches_expired_phase() {
|
||||
let mut record = openshell_core::proto::SandboxProvisioning {
|
||||
preparation_deadline: openshell_core::time::timestamp_from_millis(1_800_000).ok(),
|
||||
timeout_time: openshell_core::time::timestamp_from_millis(1_800_000).ok(),
|
||||
..Default::default()
|
||||
};
|
||||
let message = super::retained_sandbox_timeout_message(
|
||||
"cold-image",
|
||||
"ImagePreparationTimedOut: preparation expired",
|
||||
&record,
|
||||
);
|
||||
assert!(message.starts_with("ImagePreparationTimedOut: preparation expired\n"));
|
||||
assert!(message.contains("Sandbox 'cold-image' was retained"));
|
||||
assert!(message.contains("image preparation and supervisor startup diagnostics"));
|
||||
assert!(message.contains("image_preparation_timeout_seconds"));
|
||||
assert!(!message.contains("repair its configuration"));
|
||||
assert!(message.contains("openshell sandbox get cold-image"));
|
||||
assert!(message.contains("openshell sandbox start cold-image` after cleanup completes"));
|
||||
|
||||
// An admission timeout retains preparation timestamps. Its completed
|
||||
// transition must select configuration repair rather than a larger budget.
|
||||
record.admission_start_time = openshell_core::time::timestamp_from_millis(600_000).ok();
|
||||
let admission_without_preparation = openshell_core::proto::SandboxProvisioning {
|
||||
timeout_time: record.timeout_time,
|
||||
..Default::default()
|
||||
};
|
||||
for admission_record in [&record, &admission_without_preparation] {
|
||||
let message = super::retained_sandbox_timeout_message(
|
||||
"invalid-policy",
|
||||
"ProvisioningTimedOut: repair window expired",
|
||||
admission_record,
|
||||
);
|
||||
assert!(message.contains("repair its configuration"));
|
||||
assert!(!message.contains("image_preparation_timeout_seconds"));
|
||||
assert!(
|
||||
message.contains("openshell sandbox start invalid-policy` after cleanup completes")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provisioning_json_distinguishes_preparation_and_admission() {
|
||||
let mut sandbox = Sandbox::default();
|
||||
sandbox.set_phase(SandboxPhase::Provisioning.into());
|
||||
let ceiling = openshell_core::time::timestamp_from_millis(1_800_000).ok();
|
||||
sandbox.status.as_mut().unwrap().provisioning =
|
||||
Some(openshell_core::proto::SandboxProvisioning {
|
||||
preparation_deadline: ceiling,
|
||||
deadline: ceiling,
|
||||
..Default::default()
|
||||
});
|
||||
let json = super::sandbox_to_json(&sandbox);
|
||||
assert_eq!(json["provisioning"]["phase"], "preparation");
|
||||
assert_eq!(
|
||||
json["provisioning"]["preparation_deadline"],
|
||||
"1970-01-01T00:30:00Z"
|
||||
);
|
||||
assert!(json["provisioning"]["admission_start_time"].is_null());
|
||||
sandbox
|
||||
.status
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.provisioning
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.admission_start_time = openshell_core::time::timestamp_from_millis(600_000).ok();
|
||||
assert_eq!(
|
||||
super::sandbox_to_json(&sandbox)["provisioning"]["phase"],
|
||||
"admission"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sandbox_json_exposes_repair_diagnostic_and_accepted_generation() {
|
||||
use openshell_core::proto::{ConfigurationAdmissionState, SandboxConfigurationAdmission};
|
||||
|
||||
@@ -240,6 +240,10 @@ pub struct Config {
|
||||
/// TTL for SSH session tokens, in seconds. 0 disables expiry.
|
||||
pub ssh_session_ttl_secs: u64,
|
||||
|
||||
/// Absolute image preparation and initial supervisor startup budget for new
|
||||
/// sandbox attempts, in seconds. Must be between 1 and 86400, inclusive.
|
||||
pub image_preparation_timeout_seconds: u32,
|
||||
|
||||
/// Maximum gRPC requests allowed per rate-limit window.
|
||||
///
|
||||
/// When paired with [`Self::grpc_rate_limit_window_secs`], positive values
|
||||
@@ -865,6 +869,7 @@ impl Config {
|
||||
credential_drivers: Vec::new(),
|
||||
default_credential_driver: None,
|
||||
ssh_session_ttl_secs: default_ssh_session_ttl_secs(),
|
||||
image_preparation_timeout_seconds: 1800,
|
||||
grpc_rate_limit_requests: None,
|
||||
grpc_rate_limit_window_secs: None,
|
||||
service_routing: ServiceRoutingConfig::default(),
|
||||
|
||||
@@ -560,6 +560,18 @@ fn prepare_server_config_with_drivers(
|
||||
config.policy_validation_failure_mode = mode;
|
||||
}
|
||||
|
||||
if let Some(seconds) = file
|
||||
.as_ref()
|
||||
.and_then(|f| f.openshell.gateway.image_preparation_timeout_seconds)
|
||||
{
|
||||
if !(1..=86_400).contains(&seconds) {
|
||||
return Err(miette::miette!(
|
||||
"image_preparation_timeout_seconds must be between 1 and 86400"
|
||||
));
|
||||
}
|
||||
config.image_preparation_timeout_seconds = seconds;
|
||||
}
|
||||
|
||||
if let Some(issuer) = args.oidc_issuer.clone() {
|
||||
config = config.with_oidc(openshell_core::OidcConfig {
|
||||
issuer,
|
||||
@@ -3292,6 +3304,7 @@ version = 2
|
||||
|
||||
[openshell.gateway]
|
||||
policy_validation_failure_mode = "retain_last_valid"
|
||||
image_preparation_timeout_seconds = 2400
|
||||
|
||||
[openshell.drivers.docker]
|
||||
unknown_docker_key = true
|
||||
@@ -3317,6 +3330,7 @@ mem_mib = "not-a-number"
|
||||
super::prepare_server_config(&mut args, &matches).expect("server config is prepared");
|
||||
|
||||
assert_eq!(prepared.config.compute_driver.as_deref(), Some("podman"));
|
||||
assert_eq!(prepared.config.image_preparation_timeout_seconds, 2400);
|
||||
assert_eq!(
|
||||
prepared.config.policy_validation_failure_mode,
|
||||
openshell_core::PolicyValidationFailureMode::RetainLastValid
|
||||
@@ -3325,4 +3339,39 @@ mem_mib = "not-a-number"
|
||||
assert!(file.openshell.drivers.contains_key("docker"));
|
||||
assert!(file.openshell.drivers.contains_key("vm"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_config_rejects_unbounded_image_preparation() {
|
||||
let _lock = ENV_LOCK
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let state = tempfile::tempdir().unwrap();
|
||||
let tls = tempfile::tempdir().unwrap();
|
||||
let _state = EnvVarGuard::set("XDG_STATE_HOME", state.path().to_str().unwrap());
|
||||
let _tls = EnvVarGuard::set("OPENSHELL_LOCAL_TLS_DIR", tls.path().to_str().unwrap());
|
||||
let config_path = state.path().join("gateway.toml");
|
||||
for seconds in [0, 86_401] {
|
||||
std::fs::write(&config_path, format!(
|
||||
"[openshell]\nversion = 2\n[openshell.gateway]\nimage_preparation_timeout_seconds = {seconds}\n"
|
||||
)).unwrap();
|
||||
let (mut args, matches) = parse_with_args(&[
|
||||
"openshell-gateway",
|
||||
"--config",
|
||||
config_path.to_str().unwrap(),
|
||||
"--db-url",
|
||||
"sqlite::memory:",
|
||||
"--compute-driver",
|
||||
"podman",
|
||||
"--disable-tls",
|
||||
]);
|
||||
let Err(error) = super::prepare_server_config(&mut args, &matches) else {
|
||||
panic!("unbounded preparation must be rejected");
|
||||
};
|
||||
assert!(
|
||||
error
|
||||
.to_string()
|
||||
.contains("image_preparation_timeout_seconds must be between 1 and 86400")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,6 +644,7 @@ pub struct ComputeRuntime {
|
||||
telemetry_compute_driver: TelemetryComputeDriver,
|
||||
driver_process: Option<Arc<ManagedDriverProcess>>,
|
||||
default_image: String,
|
||||
image_preparation_timeout_seconds: u32,
|
||||
store: Arc<Store>,
|
||||
sandbox_index: SandboxIndex,
|
||||
sandbox_watch_bus: SandboxWatchBus,
|
||||
@@ -745,6 +746,7 @@ impl ComputeRuntime {
|
||||
telemetry_compute_driver: TelemetryComputeDriver::custom(),
|
||||
driver_process,
|
||||
default_image,
|
||||
image_preparation_timeout_seconds: 1800,
|
||||
store,
|
||||
sandbox_index,
|
||||
sandbox_watch_bus,
|
||||
@@ -826,6 +828,16 @@ impl ComputeRuntime {
|
||||
&self.default_image
|
||||
}
|
||||
|
||||
/// Validate the budget once at startup. Persisted attempts keep their
|
||||
/// original deadline even if the operator changes this value on restart.
|
||||
pub(crate) fn with_image_preparation_timeout(mut self, seconds: u32) -> Result<Self, String> {
|
||||
if !(1..=86_400).contains(&seconds) {
|
||||
return Err("image_preparation_timeout_seconds must be between 1 and 86400".into());
|
||||
}
|
||||
self.image_preparation_timeout_seconds = seconds;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn driver_info_snapshots(&self) -> &[ComputeDriverInfoSnapshot] {
|
||||
std::slice::from_ref(&self.driver_info)
|
||||
@@ -1618,6 +1630,7 @@ impl ComputeRuntime {
|
||||
SandboxPhase::Starting,
|
||||
"Starting",
|
||||
"Sandbox start requested",
|
||||
self.image_preparation_timeout_seconds,
|
||||
);
|
||||
},
|
||||
)
|
||||
@@ -1699,7 +1712,7 @@ impl ComputeRuntime {
|
||||
.await.map_err(|error| Status::internal(error.to_string()))?
|
||||
.ok_or_else(|| Status::not_found("sandbox removed during startup"))?;
|
||||
if provisioning_deadline::timed_out(¤t) {
|
||||
return Err(Status::deadline_exceeded("provisioning repair window expired"));
|
||||
return Err(Status::deadline_exceeded("provisioning deadline expired"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2104,7 +2117,13 @@ impl ComputeRuntime {
|
||||
&sandbox_id,
|
||||
expected_resource_version,
|
||||
move |sandbox| {
|
||||
apply_lifecycle_phase(sandbox, phase, &reason, &message);
|
||||
apply_lifecycle_phase(
|
||||
sandbox,
|
||||
phase,
|
||||
&reason,
|
||||
&message,
|
||||
self.image_preparation_timeout_seconds,
|
||||
);
|
||||
},
|
||||
)
|
||||
.await
|
||||
@@ -3311,22 +3330,27 @@ impl ComputeRuntime {
|
||||
}
|
||||
};
|
||||
let expected_runtime_identity = sandbox_compute_runtime_identity(&sandbox);
|
||||
// Recovery retains the original attempt and deadline. A
|
||||
// stalled driver must release this lifecycle gate after
|
||||
// expiry so the deadline worker can reclaim its compute.
|
||||
if let Err(err) = self
|
||||
.driver
|
||||
.call(
|
||||
openshell_otel::rpc::START_SANDBOX,
|
||||
Some(&sandbox_id),
|
||||
|driver| async move {
|
||||
driver
|
||||
.start_sandbox(Request::new(StartSandboxRequest {
|
||||
sandbox_id: driver_sandbox_id,
|
||||
name: sandbox_name,
|
||||
launch_authentication: Vec::new(),
|
||||
generation_id,
|
||||
expected_runtime_identity,
|
||||
}))
|
||||
.await
|
||||
},
|
||||
.await_provisioning_operation(
|
||||
&sandbox,
|
||||
self.driver.call(
|
||||
openshell_otel::rpc::START_SANDBOX,
|
||||
Some(&sandbox_id),
|
||||
|driver| async move {
|
||||
driver
|
||||
.start_sandbox(Request::new(StartSandboxRequest {
|
||||
sandbox_id: driver_sandbox_id,
|
||||
name: sandbox_name,
|
||||
launch_authentication: Vec::new(),
|
||||
generation_id,
|
||||
expected_runtime_identity,
|
||||
}))
|
||||
.await
|
||||
},
|
||||
),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -6503,7 +6527,13 @@ fn is_recoverable_error_reason(sandbox: &Sandbox) -> bool {
|
||||
.is_some_and(|c| c.reason == CONDITION_RUNTIME_RESTART || c.reason == CONDITION_STOPPED)
|
||||
}
|
||||
|
||||
fn apply_lifecycle_phase(sandbox: &mut Sandbox, phase: SandboxPhase, reason: &str, message: &str) {
|
||||
fn apply_lifecycle_phase(
|
||||
sandbox: &mut Sandbox,
|
||||
phase: SandboxPhase,
|
||||
reason: &str,
|
||||
message: &str,
|
||||
image_preparation_timeout_seconds: u32,
|
||||
) {
|
||||
sandbox.set_phase(phase as i32);
|
||||
if matches!(phase, SandboxPhase::Stopping | SandboxPhase::Starting) {
|
||||
let status = sandbox.status.get_or_insert_with(Default::default);
|
||||
@@ -6514,8 +6544,9 @@ fn apply_lifecycle_phase(sandbox: &mut Sandbox, phase: SandboxPhase, reason: &st
|
||||
set_next_restart_at_ms(status, 0);
|
||||
set_main_process_started_at_ms(status, 0);
|
||||
if phase == SandboxPhase::Starting {
|
||||
status.provisioning = Some(provisioning_deadline::new_record(
|
||||
status.provisioning = Some(provisioning_deadline::new_preparation_record(
|
||||
openshell_core::time::now_ms(),
|
||||
image_preparation_timeout_seconds,
|
||||
));
|
||||
status.configuration_admission =
|
||||
Some(openshell_core::proto::SandboxConfigurationAdmission {
|
||||
@@ -6890,6 +6921,7 @@ pub fn new_test_runtime_with_driver(
|
||||
telemetry_compute_driver: TelemetryComputeDriver::custom(),
|
||||
driver_process: None,
|
||||
default_image: "openshell/sandbox:test".to_string(),
|
||||
image_preparation_timeout_seconds: 1800,
|
||||
store,
|
||||
sandbox_index: SandboxIndex::new(),
|
||||
sandbox_watch_bus: SandboxWatchBus::new(),
|
||||
@@ -7939,6 +7971,7 @@ mod tests {
|
||||
telemetry_compute_driver: TelemetryComputeDriver::custom(),
|
||||
driver_process: None,
|
||||
default_image: "openshell/sandbox:test".to_string(),
|
||||
image_preparation_timeout_seconds: 1800,
|
||||
store,
|
||||
sandbox_index: SandboxIndex::new(),
|
||||
sandbox_watch_bus: SandboxWatchBus::new(),
|
||||
@@ -10453,18 +10486,19 @@ mod tests {
|
||||
assert!(!crate::policy_store::permits_initial_static_policy_repair(
|
||||
&blocked
|
||||
));
|
||||
// Simulate the supervisor's successful exact-generation admission report.
|
||||
// The supervisor report records the preparation-to-admission transition
|
||||
// together with acceptance of the exact configuration generation.
|
||||
runtime
|
||||
.store
|
||||
.update_message_cas::<Sandbox, _>(sandbox.object_id(), 0, |sandbox| {
|
||||
sandbox
|
||||
.status
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.configuration_admission
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.state = openshell_core::proto::ConfigurationAdmissionState::Accepted.into();
|
||||
let status = sandbox.status.as_mut().unwrap();
|
||||
provisioning_deadline::record_admission_start(
|
||||
status.provisioning.as_mut().unwrap(),
|
||||
openshell_core::time::now_ms(),
|
||||
)
|
||||
.unwrap();
|
||||
status.configuration_admission.as_mut().unwrap().state =
|
||||
openshell_core::proto::ConfigurationAdmissionState::Accepted.into();
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -15210,6 +15244,162 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn preparation_expiry_releases_stalled_start_recovery_for_cleanup() {
|
||||
let driver = ControlledDriver::new();
|
||||
driver.block_start();
|
||||
let runtime = test_runtime(driver.clone()).await;
|
||||
let now = openshell_core::time::now_ms();
|
||||
let preparation = provisioning_deadline::new_preparation_record(now, 1800);
|
||||
let mut sandbox = sandbox_record("sb-recovery-ttl", "recovery-ttl", SandboxPhase::Starting);
|
||||
sandbox.status.as_mut().unwrap().provisioning = Some(preparation.clone());
|
||||
runtime.store.put_message(&sandbox).await.unwrap();
|
||||
let recovered_runtime = runtime.clone();
|
||||
let mut recovery = tokio::spawn(async move {
|
||||
recovered_runtime
|
||||
.recover_persisted_lifecycle_transitions()
|
||||
.await
|
||||
});
|
||||
tokio::time::timeout(Duration::from_secs(1), driver.start_started.notified())
|
||||
.await
|
||||
.unwrap();
|
||||
runtime
|
||||
.reconcile_provisioning_deadlines(now + 1_800_000)
|
||||
.await
|
||||
.unwrap();
|
||||
let expired = runtime
|
||||
.store
|
||||
.get_message::<Sandbox>("sb-recovery-ttl")
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(expired.phase(), i32::from(SandboxPhase::Error));
|
||||
let status = expired.status.as_ref().unwrap();
|
||||
let record = status.provisioning.as_ref().unwrap();
|
||||
assert_eq!(record.attempt_id, preparation.attempt_id);
|
||||
assert_eq!(
|
||||
record.preparation_deadline,
|
||||
preparation.preparation_deadline
|
||||
);
|
||||
assert!(
|
||||
status
|
||||
.conditions
|
||||
.iter()
|
||||
.any(|condition| condition.reason == "ImagePreparationTimedOut")
|
||||
);
|
||||
// The recovery RPC never receives its semaphore permit. The persisted
|
||||
// expiry must cancel its waiter and release the lifecycle gate itself.
|
||||
if let Ok(result) = tokio::time::timeout(Duration::from_secs(3), &mut recovery).await {
|
||||
result.unwrap().unwrap();
|
||||
} else {
|
||||
recovery.abort();
|
||||
let _ = recovery.await;
|
||||
panic!("expired recovery kept the lifecycle gate while the driver was blocked");
|
||||
}
|
||||
runtime
|
||||
.reconcile_provisioning_deadlines(now + 1_800_001)
|
||||
.await
|
||||
.unwrap();
|
||||
tokio::time::timeout(Duration::from_secs(1), async {
|
||||
loop {
|
||||
let sandbox = runtime
|
||||
.store
|
||||
.get_message::<Sandbox>("sb-recovery-ttl")
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let record = sandbox
|
||||
.status
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.provisioning
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
if record.cleanup_completed_time.is_some() {
|
||||
assert_eq!(record.attempt_id, preparation.attempt_id);
|
||||
break;
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(driver.stop_calls(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn preparation_timeout_retains_reason_and_uses_existing_cleanup() {
|
||||
let driver = ControlledDriver::new();
|
||||
let runtime = test_runtime(driver.clone()).await;
|
||||
let mut sandbox = sandbox_record("sb-prepare", "prepare", SandboxPhase::Provisioning);
|
||||
sandbox.status.as_mut().unwrap().provisioning =
|
||||
Some(provisioning_deadline::new_preparation_record(0, 1800));
|
||||
runtime.store.put_message(&sandbox).await.unwrap();
|
||||
let sandbox = runtime
|
||||
.store
|
||||
.get_message::<Sandbox>("sb-prepare")
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let gate = runtime.lifecycle_gates.lock_for("sb-prepare").await;
|
||||
let global = runtime.lock_global_for_lifecycle(&gate).await;
|
||||
assert!(
|
||||
runtime
|
||||
.claim_provisioning_timeout(&sandbox, 600_000)
|
||||
.await
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
let expired = runtime
|
||||
.claim_provisioning_timeout(&sandbox, 1_800_000)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(expired.phase(), i32::from(SandboxPhase::Error));
|
||||
assert!(
|
||||
expired
|
||||
.status
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.conditions
|
||||
.iter()
|
||||
.any(|condition| { condition.reason == "ImagePreparationTimedOut" })
|
||||
);
|
||||
let record = expired
|
||||
.status
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.provisioning
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
assert!(record.admission_start_time.is_none());
|
||||
assert!(record.preparation_deadline.is_some());
|
||||
assert!(record.cleanup_completed_time.is_none());
|
||||
drop(global);
|
||||
runtime
|
||||
.reclaim_provisioning_timeout(&expired, &gate)
|
||||
.await
|
||||
.unwrap();
|
||||
let reclaimed = runtime
|
||||
.store
|
||||
.get_message::<Sandbox>("sb-prepare")
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(
|
||||
reclaimed
|
||||
.status
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.provisioning
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.cleanup_completed_time
|
||||
.is_some()
|
||||
);
|
||||
assert_eq!(driver.stop_calls(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn provisioning_timeout_persists_error_before_cleanup_and_preserves_record() {
|
||||
let driver = ControlledDriver::new();
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Gateway-owned provisioning repair-window transitions.
|
||||
//! Gateway-owned image preparation and admission repair deadlines.
|
||||
//!
|
||||
//! Callers must persist each transition under the sandbox lifecycle fence. Times
|
||||
//! are gateway-assigned Unix milliseconds, never supervisor-supplied values.
|
||||
//! The timer remains armed after admission acceptance until compute becomes Ready.
|
||||
//! Preparation has an absolute ceiling. The first authenticated supervisor
|
||||
//! configuration report starts admission repair, which remains armed until Ready.
|
||||
|
||||
use openshell_core::proto::SandboxProvisioning;
|
||||
use openshell_core::time::{timestamp_from_millis, timestamp_to_millis};
|
||||
@@ -26,6 +27,8 @@ pub(super) struct ProvisioningDeadline {
|
||||
attempt_id: String,
|
||||
change: ConfigurationChange,
|
||||
first_rejection_at_ms: Option<i64>,
|
||||
preparation_deadline_at_ms: Option<i64>,
|
||||
admission_start_at_ms: Option<i64>,
|
||||
state: DeadlineState,
|
||||
}
|
||||
|
||||
@@ -50,6 +53,21 @@ impl ProvisioningDeadline {
|
||||
if record.deadline.is_some() && record.timeout_time.is_some() {
|
||||
return Err("provisioning cannot be armed and expired".into());
|
||||
}
|
||||
let preparation_deadline_at_ms = record
|
||||
.preparation_deadline
|
||||
.as_ref()
|
||||
.map(|value| millis(Some(value), "preparation deadline"))
|
||||
.transpose()?;
|
||||
let admission_start_at_ms = record
|
||||
.admission_start_time
|
||||
.as_ref()
|
||||
.map(|value| millis(Some(value), "admission start time"))
|
||||
.transpose()?;
|
||||
if admission_start_at_ms.is_some_and(|started| {
|
||||
preparation_deadline_at_ms.is_none_or(|ceiling| started >= ceiling)
|
||||
}) {
|
||||
return Err("admission must start before its preparation deadline".into());
|
||||
}
|
||||
let state = if record.timeout_time.is_some() {
|
||||
DeadlineState::Expired {
|
||||
expired_at_ms: millis(record.timeout_time.as_ref(), "timeout time")?,
|
||||
@@ -61,6 +79,13 @@ impl ProvisioningDeadline {
|
||||
} else {
|
||||
DeadlineState::Ready
|
||||
};
|
||||
if let Some(ceiling) = preparation_deadline_at_ms
|
||||
&& admission_start_at_ms.is_none()
|
||||
&& !matches!(state, DeadlineState::Expired { .. })
|
||||
&& !matches!(state, DeadlineState::Armed { deadline_at_ms } if deadline_at_ms == ceiling)
|
||||
{
|
||||
return Err("preparation must retain its absolute deadline until admission".into());
|
||||
}
|
||||
Ok(Self {
|
||||
attempt_id: record.attempt_id.clone(),
|
||||
change: ConfigurationChange {
|
||||
@@ -72,6 +97,8 @@ impl ProvisioningDeadline {
|
||||
.as_ref()
|
||||
.map(|value| millis(Some(value), "rejection time"))
|
||||
.transpose()?,
|
||||
preparation_deadline_at_ms,
|
||||
admission_start_at_ms,
|
||||
state,
|
||||
})
|
||||
}
|
||||
@@ -84,6 +111,12 @@ impl ProvisioningDeadline {
|
||||
record.first_rejection_time = self
|
||||
.first_rejection_at_ms
|
||||
.and_then(|value| timestamp_from_millis(value).ok());
|
||||
record.preparation_deadline = self
|
||||
.preparation_deadline_at_ms
|
||||
.and_then(|value| timestamp_from_millis(value).ok());
|
||||
record.admission_start_time = self
|
||||
.admission_start_at_ms
|
||||
.and_then(|value| timestamp_from_millis(value).ok());
|
||||
record.deadline = self
|
||||
.deadline_at_ms()
|
||||
.and_then(|value| timestamp_from_millis(value).ok());
|
||||
@@ -98,12 +131,38 @@ impl ProvisioningDeadline {
|
||||
attempt_id,
|
||||
change,
|
||||
first_rejection_at_ms: None,
|
||||
preparation_deadline_at_ms: None,
|
||||
admission_start_at_ms: None,
|
||||
state: DeadlineState::Armed {
|
||||
deadline_at_ms: now_ms.saturating_add(REPAIR_WINDOW_MS),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_preparing(&self) -> bool {
|
||||
self.preparation_deadline_at_ms.is_some() && self.admission_start_at_ms.is_none()
|
||||
}
|
||||
|
||||
/// Only an authenticated report for the current supervisor may call this.
|
||||
/// Persist it with the report: duplicate delivery or restart must not grant
|
||||
/// another admission window, and a late registration cannot revive compute.
|
||||
fn start_admission(&mut self, attempt_id: &str, now_ms: i64) -> bool {
|
||||
if attempt_id != self.attempt_id
|
||||
|| !self.is_preparing()
|
||||
|| now_ms < self.change.committed_at_ms
|
||||
|| self
|
||||
.deadline_at_ms()
|
||||
.is_none_or(|deadline| now_ms >= deadline)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
self.admission_start_at_ms = Some(now_ms);
|
||||
self.state = DeadlineState::Armed {
|
||||
deadline_at_ms: now_ms.saturating_add(REPAIR_WINDOW_MS),
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
pub fn deadline_at_ms(&self) -> Option<i64> {
|
||||
match self.state {
|
||||
DeadlineState::Armed { deadline_at_ms } => Some(deadline_at_ms),
|
||||
@@ -124,10 +183,12 @@ impl ProvisioningDeadline {
|
||||
{
|
||||
return false;
|
||||
}
|
||||
self.state = DeadlineState::Armed {
|
||||
deadline_at_ms: deadline_at_ms
|
||||
.max(change.committed_at_ms.saturating_add(REPAIR_WINDOW_MS)),
|
||||
};
|
||||
if !self.is_preparing() {
|
||||
self.state = DeadlineState::Armed {
|
||||
deadline_at_ms: deadline_at_ms
|
||||
.max(change.committed_at_ms.saturating_add(REPAIR_WINDOW_MS)),
|
||||
};
|
||||
}
|
||||
self.change = change;
|
||||
self.first_rejection_at_ms = None;
|
||||
true
|
||||
@@ -140,6 +201,7 @@ impl ProvisioningDeadline {
|
||||
return false;
|
||||
};
|
||||
if !self.matches(attempt_id, change_id)
|
||||
|| self.is_preparing()
|
||||
|| self.first_rejection_at_ms.is_some()
|
||||
|| now_ms < self.change.committed_at_ms
|
||||
|| now_ms >= deadline_at_ms
|
||||
@@ -173,6 +235,7 @@ impl ProvisioningDeadline {
|
||||
/// must not call this method. Late readiness requires an explicit retry.
|
||||
pub fn ready(&mut self, attempt_id: &str, change_id: &str, now_ms: i64) -> bool {
|
||||
if !self.matches(attempt_id, change_id)
|
||||
|| self.is_preparing()
|
||||
|| self
|
||||
.deadline_at_ms()
|
||||
.is_none_or(|deadline| now_ms >= deadline)
|
||||
@@ -197,7 +260,8 @@ pub fn timed_out(sandbox: &openshell_core::proto::Sandbox) -> bool {
|
||||
.is_some_and(|record| record.timeout_time.is_some())
|
||||
}
|
||||
|
||||
/// Create an independent attempt. Supervisor reconnects must never call this.
|
||||
/// Adopt an existing untimed attempt without granting it a new preparation phase.
|
||||
/// New create/start operations use `new_preparation_record` instead.
|
||||
pub fn new_record(now_ms: i64) -> SandboxProvisioning {
|
||||
let mut record = SandboxProvisioning::default();
|
||||
ProvisioningDeadline::new(
|
||||
@@ -212,6 +276,26 @@ pub fn new_record(now_ms: i64) -> SandboxProvisioning {
|
||||
record
|
||||
}
|
||||
|
||||
/// Create an independent attempt with a fixed preparation budget. The gateway
|
||||
/// validates the configured seconds before constructing the runtime. Reconnects
|
||||
/// and driver progress must never call this function.
|
||||
pub fn new_preparation_record(now_ms: i64, timeout_seconds: u32) -> SandboxProvisioning {
|
||||
let mut record = new_record(now_ms);
|
||||
let ceiling = now_ms.saturating_add(i64::from(timeout_seconds) * 1_000);
|
||||
record.preparation_deadline = timestamp_from_millis(ceiling).ok();
|
||||
record.deadline.clone_from(&record.preparation_deadline);
|
||||
record
|
||||
}
|
||||
|
||||
/// The caller has checked the report's authentication, instance fence and
|
||||
/// configuration generation. Persist this transition in the same CAS as admission.
|
||||
pub fn record_admission_start(record: &mut SandboxProvisioning, now_ms: i64) -> Result<(), String> {
|
||||
let mut deadline = ProvisioningDeadline::from_record(record)?;
|
||||
deadline.start_admission(&record.attempt_id, now_ms);
|
||||
deadline.write_record(record);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply the first accepted rejection under the same CAS as admission evidence.
|
||||
/// The report's generation and supervisor instance must already be validated.
|
||||
pub fn record_rejection(record: &mut SandboxProvisioning, now_ms: i64) -> Result<(), String> {
|
||||
@@ -250,6 +334,10 @@ pub(super) fn reconcile_readiness(sandbox: &mut openshell_core::proto::Sandbox,
|
||||
if deadline.ready(&record.attempt_id, &record.configuration_change_id, now_ms) {
|
||||
deadline.write_record(record);
|
||||
} else {
|
||||
let awaiting_registration = deadline.is_preparing()
|
||||
&& deadline
|
||||
.deadline_at_ms()
|
||||
.is_some_and(|value| now_ms < value);
|
||||
status.phase = SandboxPhase::Provisioning.into();
|
||||
status
|
||||
.conditions
|
||||
@@ -257,8 +345,18 @@ pub(super) fn reconcile_readiness(sandbox: &mut openshell_core::proto::Sandbox,
|
||||
status.conditions.push(SandboxCondition {
|
||||
r#type: "Ready".into(),
|
||||
status: "False".into(),
|
||||
reason: "ProvisioningDeadlineElapsed".into(),
|
||||
message: "Provisioning deadline elapsed; awaiting compute reclamation".into(),
|
||||
reason: if awaiting_registration {
|
||||
"ConfigurationPending"
|
||||
} else {
|
||||
"ProvisioningDeadlineElapsed"
|
||||
}
|
||||
.into(),
|
||||
message: if awaiting_registration {
|
||||
"Waiting for an authenticated supervisor configuration report"
|
||||
} else {
|
||||
"Provisioning deadline elapsed; awaiting compute reclamation"
|
||||
}
|
||||
.into(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
@@ -442,6 +540,7 @@ impl super::ComputeRuntime {
|
||||
return Ok(None);
|
||||
};
|
||||
let mut deadline = ProvisioningDeadline::from_record(record)?;
|
||||
let preparation_expired = deadline.is_preparing();
|
||||
if !deadline.expire(&record.attempt_id, &record.configuration_change_id, now_ms) {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -463,7 +562,9 @@ impl super::ComputeRuntime {
|
||||
.configuration_admission
|
||||
.as_ref()
|
||||
.map_or("", |admission| admission.error.as_str());
|
||||
let message = if diagnostic.is_empty() {
|
||||
let message = if preparation_expired {
|
||||
"Image preparation or initial supervisor startup exceeded its absolute deadline".to_string()
|
||||
} else if diagnostic.is_empty() {
|
||||
"Provisioning repair window expired after 300 seconds".to_string()
|
||||
} else {
|
||||
format!(
|
||||
@@ -476,7 +577,11 @@ impl super::ComputeRuntime {
|
||||
status.conditions.push(SandboxCondition {
|
||||
r#type: "Ready".into(),
|
||||
status: "False".into(),
|
||||
reason: "ProvisioningTimedOut".into(),
|
||||
reason: if preparation_expired {
|
||||
"ImagePreparationTimedOut"
|
||||
} else {
|
||||
"ProvisioningTimedOut"
|
||||
}.into(),
|
||||
message,
|
||||
transition_time: timestamp_from_millis(now_ms).ok(),
|
||||
});
|
||||
@@ -488,7 +593,8 @@ impl super::ComputeRuntime {
|
||||
self.sandbox_watch_bus.notify(current.object_id());
|
||||
tracing::warn!(
|
||||
sandbox_id = current.object_id(),
|
||||
"Sandbox provisioning repair window expired"
|
||||
preparation_expired,
|
||||
"Sandbox provisioning deadline expired"
|
||||
);
|
||||
Ok(Some(updated))
|
||||
}
|
||||
@@ -654,6 +760,87 @@ impl super::ComputeRuntime {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn existing_wire_record_does_not_gain_preparation_time() {
|
||||
use prost::Message;
|
||||
// Encoded before preparation timestamps existed: attempt a, change c,
|
||||
// configuration at epoch 0, and an admission deadline at 300 seconds.
|
||||
let bytes = [0x0a, 1, b'a', 0x12, 1, b'c', 0x1a, 0, 0x2a, 3, 8, 0xac, 2];
|
||||
let mut record = SandboxProvisioning::decode(bytes.as_slice()).unwrap();
|
||||
let before = record.clone();
|
||||
record_admission_start(&mut record, 299_999).unwrap();
|
||||
assert_eq!(record, before);
|
||||
assert!(record.preparation_deadline.is_none());
|
||||
assert!(!allows_admission(&record, 300_000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preparation_over_five_minutes_gets_a_full_admission_repair_window() {
|
||||
let record = new_preparation_record(0, 1800);
|
||||
let mut timer = ProvisioningDeadline::from_record(&record).unwrap();
|
||||
let attempt = record.attempt_id;
|
||||
let change_id = record.configuration_change_id;
|
||||
assert!(!timer.expire(&attempt, &change_id, 600_000));
|
||||
assert!(!timer.ready(&attempt, &change_id, 600_000));
|
||||
assert!(timer.start_admission(&attempt, 600_000));
|
||||
assert_eq!(timer.deadline_at_ms(), Some(900_000));
|
||||
assert!(timer.rejected(&attempt, &change_id, 601_000));
|
||||
assert_eq!(timer.deadline_at_ms(), Some(901_000));
|
||||
assert!(!timer.start_admission(&attempt, 800_000));
|
||||
assert!(timer.configuration_changed(&attempt, change("updated", 800_000)));
|
||||
assert_eq!(timer.deadline_at_ms(), Some(1_100_000));
|
||||
assert_eq!(timer.admission_start_at_ms, Some(600_000));
|
||||
assert_eq!(timer.preparation_deadline_at_ms, Some(1_800_000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preparation_config_changes_and_restart_preserve_the_absolute_ceiling() {
|
||||
use prost::Message;
|
||||
let mut record = new_preparation_record(0, 1800);
|
||||
let mut timer = ProvisioningDeadline::from_record(&record).unwrap();
|
||||
let attempt = record.attempt_id.clone();
|
||||
assert!(timer.configuration_changed(&attempt, change("first", 600_000)));
|
||||
assert!(timer.configuration_changed(&attempt, change("last", 1_799_000)));
|
||||
assert!(!timer.configuration_changed(&attempt, change("last", 1_799_500)));
|
||||
assert!(!timer.rejected(&attempt, "last", 1_799_500));
|
||||
timer.write_record(&mut record);
|
||||
let bytes = record.encode_to_vec();
|
||||
let restored = SandboxProvisioning::decode(bytes.as_slice()).unwrap();
|
||||
let mut timer = ProvisioningDeadline::from_record(&restored).unwrap();
|
||||
assert_eq!(timer.deadline_at_ms(), Some(1_800_000));
|
||||
assert!(!timer.start_admission("previous-attempt", 1_799_999));
|
||||
assert!(!timer.start_admission(&attempt, 1_800_000));
|
||||
assert!(timer.expire(&attempt, "last", 1_800_000));
|
||||
assert!(!timer.start_admission(&attempt, 1_799_999));
|
||||
assert!(!timer.ready(&attempt, "last", 1_799_999));
|
||||
assert!(!timer.configuration_changed(&attempt, change("late", 1_799_999)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admission_registration_roundtrip_does_not_restart_repair() {
|
||||
use prost::Message;
|
||||
let mut record = new_preparation_record(0, 1800);
|
||||
record_admission_start(&mut record, 600_000).unwrap();
|
||||
let before = record.clone();
|
||||
let bytes = record.encode_to_vec();
|
||||
let mut restored = SandboxProvisioning::decode(bytes.as_slice()).unwrap();
|
||||
record_admission_start(&mut restored, 899_999).unwrap();
|
||||
assert_eq!(restored, before);
|
||||
assert!(!allows_admission(&restored, 900_000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_preparation_timing_cannot_grant_admission() {
|
||||
let mut record = new_preparation_record(0, 1800);
|
||||
record.deadline = timestamp_from_millis(1_800_001).ok();
|
||||
assert!(ProvisioningDeadline::from_record(&record).is_err());
|
||||
record.deadline = None;
|
||||
assert!(ProvisioningDeadline::from_record(&record).is_err());
|
||||
record.deadline = record.preparation_deadline;
|
||||
record.admission_start_time = timestamp_from_millis(1_800_000).ok();
|
||||
assert!(ProvisioningDeadline::from_record(&record).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protobuf_roundtrip_retains_deadline_and_cleanup_progress() {
|
||||
use prost::Message;
|
||||
|
||||
@@ -116,6 +116,9 @@ pub struct GatewayFileSection {
|
||||
// ── Sandbox / SSH ────────────────────────────────────────────────────
|
||||
#[serde(default)]
|
||||
pub ssh_session_ttl_secs: Option<u64>,
|
||||
/// Absolute preparation budget for new attempts; existing deadlines persist.
|
||||
#[serde(default)]
|
||||
pub image_preparation_timeout_seconds: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub grpc_rate_limit_requests: Option<u64>,
|
||||
#[serde(default)]
|
||||
|
||||
@@ -4424,7 +4424,7 @@ pub(super) async fn handle_report_sandbox_configuration(
|
||||
.map_err(Status::internal)?;
|
||||
if crate::compute::provisioning_deadline::timed_out(&sandbox) {
|
||||
return Err(Status::failed_precondition(
|
||||
"provisioning repair window expired; explicitly start the sandbox after cleanup",
|
||||
"provisioning deadline expired; explicitly start the sandbox after cleanup",
|
||||
));
|
||||
}
|
||||
let current = sandbox
|
||||
@@ -4510,10 +4510,10 @@ pub(super) async fn handle_report_sandbox_configuration(
|
||||
.claim_provisioning_timeout(&sandbox, now_ms)
|
||||
.await
|
||||
.map_err(Status::internal)?;
|
||||
return Err(Status::failed_precondition(
|
||||
"provisioning repair window expired",
|
||||
));
|
||||
return Err(Status::failed_precondition("provisioning deadline expired"));
|
||||
}
|
||||
crate::compute::provisioning_deadline::record_admission_start(record, now_ms)
|
||||
.map_err(Status::internal)?;
|
||||
if reported == ConfigurationAdmissionState::Rejected {
|
||||
crate::compute::provisioning_deadline::record_rejection(record, now_ms)
|
||||
.map_err(Status::internal)?;
|
||||
@@ -7718,6 +7718,143 @@ mod tests {
|
||||
request
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn preparation_registration_starts_repair_once_after_a_cold_start() {
|
||||
use crate::compute::provisioning_deadline::new_preparation_record;
|
||||
use openshell_core::proto::{
|
||||
ConfigurationAdmissionState, ReportSandboxConfigurationRequest,
|
||||
SandboxConfigurationAdmission, SandboxPhase,
|
||||
};
|
||||
use openshell_core::time::timestamp_to_millis;
|
||||
let state = test_server_state().await;
|
||||
let sandbox_id = "sb-cold-registration";
|
||||
let mut sandbox = test_sandbox(
|
||||
sandbox_id,
|
||||
"cold-registration",
|
||||
openshell_policy::restrictive_default_policy(),
|
||||
Vec::new(),
|
||||
);
|
||||
sandbox.set_phase(SandboxPhase::Provisioning.into());
|
||||
let preparation = new_preparation_record(current_time_ms() - 600_000, 1800);
|
||||
sandbox.status.as_mut().unwrap().provisioning = Some(preparation.clone());
|
||||
state.store.put_message(&sandbox).await.unwrap();
|
||||
let instance_id = uuid::Uuid::new_v4().to_string();
|
||||
let request = || {
|
||||
with_sandbox(
|
||||
Request::new(ReportSandboxConfigurationRequest {
|
||||
sandbox_id: sandbox_id.into(),
|
||||
admission: Some(SandboxConfigurationAdmission {
|
||||
instance_id: instance_id.clone(),
|
||||
state: ConfigurationAdmissionState::Pending.into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
sandbox_id,
|
||||
)
|
||||
};
|
||||
// A duplicate report racing the initial registration must share one
|
||||
// persisted transition; either may acquire the lifecycle fence first.
|
||||
let (first, duplicate) = tokio::join!(
|
||||
handle_report_sandbox_configuration(&state, request()),
|
||||
handle_report_sandbox_configuration(&state, request()),
|
||||
);
|
||||
first.unwrap();
|
||||
duplicate.unwrap();
|
||||
let saved = state
|
||||
.store
|
||||
.get_message::<Sandbox>(sandbox_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let record = saved
|
||||
.status
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.provisioning
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
record.preparation_deadline,
|
||||
preparation.preparation_deadline
|
||||
);
|
||||
let start = timestamp_to_millis(record.admission_start_time.as_ref().unwrap()).unwrap();
|
||||
let deadline = timestamp_to_millis(record.deadline.as_ref().unwrap()).unwrap();
|
||||
assert_eq!(deadline - start, 300_000);
|
||||
handle_report_sandbox_configuration(&state, request())
|
||||
.await
|
||||
.unwrap();
|
||||
let repeated = state
|
||||
.store
|
||||
.get_message::<Sandbox>(sandbox_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
repeated
|
||||
.status
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.provisioning
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
record
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn preparation_expiry_rejects_registration_before_the_scanner_runs() {
|
||||
use crate::compute::provisioning_deadline::new_preparation_record;
|
||||
use openshell_core::proto::{
|
||||
ConfigurationAdmissionState, ReportSandboxConfigurationRequest,
|
||||
SandboxConfigurationAdmission, SandboxPhase,
|
||||
};
|
||||
let state = test_server_state().await;
|
||||
let sandbox_id = "sb-expired-preparation";
|
||||
let mut sandbox = test_sandbox(
|
||||
sandbox_id,
|
||||
"expired-preparation",
|
||||
openshell_policy::restrictive_default_policy(),
|
||||
Vec::new(),
|
||||
);
|
||||
sandbox.set_phase(SandboxPhase::Provisioning.into());
|
||||
sandbox.status.as_mut().unwrap().provisioning = Some(new_preparation_record(0, 1800));
|
||||
state.store.put_message(&sandbox).await.unwrap();
|
||||
let error = handle_report_sandbox_configuration(
|
||||
&state,
|
||||
with_sandbox(
|
||||
Request::new(ReportSandboxConfigurationRequest {
|
||||
sandbox_id: sandbox_id.into(),
|
||||
admission: Some(SandboxConfigurationAdmission {
|
||||
instance_id: uuid::Uuid::new_v4().to_string(),
|
||||
state: ConfigurationAdmissionState::Pending.into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
sandbox_id,
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert_eq!(error.code(), Code::FailedPrecondition);
|
||||
let expired = state
|
||||
.store
|
||||
.get_message::<Sandbox>(sandbox_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(expired.phase(), i32::from(SandboxPhase::Error));
|
||||
let status = expired.status.unwrap();
|
||||
assert!(status.provisioning.unwrap().admission_start_time.is_none());
|
||||
assert!(
|
||||
status
|
||||
.conditions
|
||||
.iter()
|
||||
.any(|condition| condition.reason == "ImagePreparationTimedOut")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn provisioning_timeout_rejects_supervisor_registration() {
|
||||
use openshell_core::proto::{
|
||||
@@ -7756,7 +7893,7 @@ mod tests {
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert_eq!(error.code(), Code::FailedPrecondition);
|
||||
assert!(error.message().contains("repair window expired"));
|
||||
assert!(error.message().contains("provisioning deadline expired"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -605,7 +605,12 @@ async fn handle_create_sandbox_inner(
|
||||
.status
|
||||
.as_mut()
|
||||
.expect("status initialized")
|
||||
.provisioning = Some(crate::compute::provisioning_deadline::new_record(now_ms));
|
||||
.provisioning = Some(
|
||||
crate::compute::provisioning_deadline::new_preparation_record(
|
||||
now_ms,
|
||||
state.config.image_preparation_timeout_seconds,
|
||||
),
|
||||
);
|
||||
crate::compute::provisioning_deadline::refresh_configuration(
|
||||
&state.store,
|
||||
&mut sandbox,
|
||||
|
||||
@@ -1732,6 +1732,9 @@ async fn build_compute_runtime(
|
||||
|
||||
let runtime = runtime
|
||||
.with_admission_policy(admission)
|
||||
.and_then(|runtime| {
|
||||
runtime.with_image_preparation_timeout(config.image_preparation_timeout_seconds)
|
||||
})
|
||||
.map_err(Error::config)?;
|
||||
Ok(runtime.with_telemetry_compute_driver(telemetry_compute_driver))
|
||||
}
|
||||
|
||||
@@ -126,12 +126,15 @@ mod tests {
|
||||
// inventories; the provider-environment file map is public-only. The
|
||||
// request has no provider-file capability field: older supervisors ignore
|
||||
// the additive file map while retaining the rest of the response.
|
||||
// Preparation timing adds two optional timestamps to SandboxProvisioning.
|
||||
// Existing rows decode with both absent and retain their active deadline;
|
||||
// no stored attempt gains another phase or time budget on upgrade.
|
||||
// Service authorization also extends both schemas additively. Legacy
|
||||
// payloads retain the safe Strip default.
|
||||
const PUBLIC_RPC_SCHEMA_SHA256: &str =
|
||||
"2e156c6ad3c8eb51bcd30dc13b173fe339b38207a1b1f98f7be2e0cad8e3bd45";
|
||||
"581125d215f2a1967eb73826c411c1e72a53fb3a30a20e85c51d96bbb518e078";
|
||||
const DURABLE_SCHEMA_SHA256: &str =
|
||||
"38165d9d76f49fcfe98a12f241e032838a2376c1d1a87ea2796fd33b9b1a3541";
|
||||
"c1e49c80c52a5c7458952b333e7ca9da2dd24478b41756b710ba29a5217b67d7";
|
||||
const PUBLIC_DURABLE_OVERLAP_SHA256: &str =
|
||||
"761dea31a521b0650840fe2a823ad6e36a265ed323ba4506889781d630df0ee3";
|
||||
// A persisted Sandbox without endpoint status retains its lifecycle fields;
|
||||
|
||||
@@ -2720,7 +2720,12 @@ fn sandbox_notes_for_view(
|
||||
} else {
|
||||
"compute cleanup pending"
|
||||
};
|
||||
let mut notes = format!("Provisioning timed out; {cleanup}");
|
||||
let mut notes =
|
||||
if record.preparation_deadline.is_some() && record.admission_start_time.is_none() {
|
||||
format!("Image preparation timed out; {cleanup}")
|
||||
} else {
|
||||
format!("Provisioning timed out; {cleanup}")
|
||||
};
|
||||
if !forwards.is_empty() {
|
||||
notes.push_str("; ");
|
||||
notes.push_str(&forwards);
|
||||
@@ -3340,6 +3345,26 @@ mod sandbox_notes_tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preparation_timeout_notes_identify_the_expired_phase() {
|
||||
let sandbox = Sandbox {
|
||||
status: Some(SandboxStatus {
|
||||
provisioning: Some(openshell_core::proto::SandboxProvisioning {
|
||||
preparation_deadline: openshell_core::time::timestamp_from_millis(1_800_000)
|
||||
.ok(),
|
||||
timeout_time: openshell_core::time::timestamp_from_millis(1_800_000).ok(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
sandbox_notes(&sandbox, String::new()),
|
||||
"Image preparation timed out; compute cleanup pending"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configuration_rejection_precedes_forwards_and_clears_after_repair() {
|
||||
let condition = SandboxCondition {
|
||||
|
||||
@@ -139,6 +139,10 @@ credential_drivers = ["kubernetes-secrets"]
|
||||
|
||||
ssh_session_ttl_secs = 3600
|
||||
|
||||
# Absolute image preparation and initial supervisor startup budget.
|
||||
# Applies to new attempts only; allowed values are 1 through 86400 seconds.
|
||||
image_preparation_timeout_seconds = 1800
|
||||
|
||||
# Reject invalid policy generations securely by default. Set
|
||||
# "retain_last_valid" only when availability takes priority.
|
||||
policy_validation_failure_mode = "fail_closed"
|
||||
|
||||
@@ -366,14 +366,11 @@ check the sandbox log for the specific error:
|
||||
openshell sandbox get my-sandbox --output json
|
||||
```
|
||||
|
||||
You have 300 seconds to fix the configuration. Replace the policy with
|
||||
`openshell policy set`, or fix the provider configuration. Until the workload
|
||||
first starts, you can also change filesystem, Landlock, and process settings.
|
||||
Each change to the policy, providers, or settings restarts the 300 seconds.
|
||||
You have 300 seconds to fix the configuration after the supervisor starts admission. Image preparation has its own deadline and does not consume that repair time. Replace the policy with `openshell policy set`, or fix the provider configuration. Until the workload first starts, you can also change filesystem, Landlock, and process settings. Each effective change to the policy, providers, or settings restarts the 300 seconds; writing an unchanged value does not.
|
||||
|
||||
If the time runs out, the sandbox moves to `Error` with the reason
|
||||
`ProvisioningTimedOut`. Fixing the configuration does not restart it. After you
|
||||
fix it, start the sandbox again, which begins a new 300-second window:
|
||||
fix it, start the sandbox again. Admission gets a new 300-second window after preparation:
|
||||
|
||||
```shell
|
||||
openshell sandbox start my-sandbox
|
||||
|
||||
@@ -915,30 +915,20 @@ Management operations remain available while startup is blocked. After repair,
|
||||
the supervisor completes startup without recreating the sandbox. Starting a
|
||||
stopped sandbox repeats configuration admission before launching its workload.
|
||||
|
||||
The gateway enforces a 300-second provisioning repair window, independently of
|
||||
the CLI wait timeout. An effective policy, settings, provider, profile, or
|
||||
attachment change resets the window from its stored change time. The first
|
||||
failed configuration load for that change grants another full window. Repeated
|
||||
failures and reconnects do not extend it; reaching `Ready` clears it.
|
||||
Image preparation and initial supervisor startup have an absolute deadline of 1800 seconds by default, independently of the CLI wait timeout. Operators can set `image_preparation_timeout_seconds` in `[openshell.gateway]` to any value from 1 through 86400. Each new create or explicit start stores its deadline. Progress events, configuration edits, and gateway or driver restarts cannot extend it. Changing the gateway setting affects new attempts only.
|
||||
|
||||
When the window expires, the sandbox enters `Error` with reason
|
||||
`ProvisioningTimedOut`. The gateway stops its workload and supervisor compute,
|
||||
retaining the sandbox record, diagnostic, and restartable storage. Cleanup can
|
||||
remain pending if the backend is unavailable; the gateway retries it. Inspect
|
||||
`provisioning` in JSON output for the deadline, timeout, and cleanup timestamps.
|
||||
TUI NOTES distinguishes pending cleanup from reclaimed compute.
|
||||
The first authenticated configuration report from the current supervisor ends preparation and starts a separate 300-second admission repair window. A slow image download therefore does not consume time reserved for fixing policy. During admission, an effective policy, settings, provider, profile, or attachment change resets the window from its stored change time. The first failed configuration load for that change grants another full window. Repeated failures, duplicate reports, and reconnects do not extend it; reaching `Ready` clears it.
|
||||
|
||||
Repair the configuration, wait for cleanup to complete, then explicitly restart:
|
||||
Preparation expiry sets the sandbox to `Error` with reason `ImagePreparationTimedOut`; admission repair expiry uses `ProvisioningTimedOut`. The gateway stops its workload and supervisor compute, retaining the sandbox record, diagnostic, and restartable storage. Cleanup can remain pending if the backend is unavailable; the gateway retries it. Inspect `provisioning` in JSON output for the active `phase`, `deadline`, original `preparation_deadline`, `admission_start_time`, and cleanup timestamps. TUI NOTES identifies preparation timeout and distinguishes pending cleanup from reclaimed compute.
|
||||
|
||||
For `ImagePreparationTimedOut`, inspect image preparation and supervisor startup diagnostics and check whether the configured preparation budget is sufficient. For `ProvisioningTimedOut`, repair the rejected configuration. In either case, wait for cleanup to complete, then explicitly restart:
|
||||
|
||||
```shell
|
||||
openshell sandbox get my-sandbox --output json
|
||||
openshell sandbox start my-sandbox
|
||||
```
|
||||
|
||||
Editing configuration after expiry does not restart compute. A retry gets a new
|
||||
300-second window, while static-policy restrictions from any previous activation
|
||||
remain in force. Timed-out records are retained even for ephemeral creates; use
|
||||
`sandbox delete` when you no longer need the diagnostic or stored state.
|
||||
Editing configuration after expiry does not restart compute. An explicit retry gets a new preparation deadline and a separate admission repair window, while static-policy restrictions from any previous activation remain in force. Attempts already active when the gateway is upgraded retain their stored deadline. Timed-out records are retained even for ephemeral creates; use `sandbox delete` when you no longer need the diagnostic or stored state.
|
||||
|
||||
| Phase | Description |
|
||||
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
+10
-2
@@ -1184,7 +1184,7 @@ message SandboxStatus {
|
||||
SandboxConfigurationAdmission configuration_admission = 11;
|
||||
// Durable first-acceptance marker. Absent on legacy records; never reset by restart.
|
||||
optional bool configuration_activated = 12;
|
||||
// Gateway-owned repair window. Retained after timeout for inspection and retry.
|
||||
// Gateway-owned provisioning deadlines, retained after timeout for inspection and retry.
|
||||
SandboxProvisioning provisioning = 13;
|
||||
// Consecutive policy-driven restart number in the current crash loop.
|
||||
uint32 restart_count = 14;
|
||||
@@ -3769,7 +3769,7 @@ message SandboxProvisioning {
|
||||
string configuration_change_id = 2;
|
||||
google.protobuf.Timestamp configuration_change_time = 3;
|
||||
google.protobuf.Timestamp first_rejection_time = 4;
|
||||
// Present only while the repair window is armed.
|
||||
// Active preparation or admission-repair deadline. Absent after Ready/timeout.
|
||||
google.protobuf.Timestamp deadline = 5;
|
||||
google.protobuf.Timestamp timeout_time = 6;
|
||||
// Set only after both supervisor and workload compute have been reclaimed.
|
||||
@@ -3781,6 +3781,14 @@ message SandboxProvisioning {
|
||||
// Attachment edits have their own durable clock; status writes do not change it.
|
||||
string attachment_change_id = 10;
|
||||
google.protobuf.Timestamp attachment_change_time = 11;
|
||||
// Absolute ceiling for image preparation and initial supervisor startup.
|
||||
// Set once per new attempt; progress, configuration writes, and restarts
|
||||
// cannot extend it. Absent on attempts created before preparation timing.
|
||||
google.protobuf.Timestamp preparation_deadline = 12;
|
||||
// First authenticated supervisor configuration report for this attempt.
|
||||
// Starts the admission repair window. Absent while preparing, including
|
||||
// after a preparation timeout. Duplicate reports never change this value.
|
||||
google.protobuf.Timestamp admission_start_time = 13;
|
||||
}
|
||||
|
||||
// Create-time request to expose one loopback HTTP service in a sandbox.
|
||||
|
||||
@@ -3118,7 +3118,7 @@ type SandboxStatus struct {
|
||||
ConfigurationAdmission *SandboxConfigurationAdmission `protobuf:"bytes,11,opt,name=configuration_admission,json=configurationAdmission,proto3" json:"configuration_admission,omitempty"`
|
||||
// Durable first-acceptance marker. Absent on legacy records; never reset by restart.
|
||||
ConfigurationActivated *bool `protobuf:"varint,12,opt,name=configuration_activated,json=configurationActivated,proto3,oneof" json:"configuration_activated,omitempty"`
|
||||
// Gateway-owned repair window. Retained after timeout for inspection and retry.
|
||||
// Gateway-owned provisioning deadlines, retained after timeout for inspection and retry.
|
||||
Provisioning *SandboxProvisioning `protobuf:"bytes,13,opt,name=provisioning,proto3" json:"provisioning,omitempty"`
|
||||
// Consecutive policy-driven restart number in the current crash loop.
|
||||
RestartCount uint32 `protobuf:"varint,14,opt,name=restart_count,json=restartCount,proto3" json:"restart_count,omitempty"`
|
||||
@@ -17637,7 +17637,7 @@ type SandboxProvisioning struct {
|
||||
ConfigurationChangeId string `protobuf:"bytes,2,opt,name=configuration_change_id,json=configurationChangeId,proto3" json:"configuration_change_id,omitempty"`
|
||||
ConfigurationChangeTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=configuration_change_time,json=configurationChangeTime,proto3" json:"configuration_change_time,omitempty"`
|
||||
FirstRejectionTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=first_rejection_time,json=firstRejectionTime,proto3" json:"first_rejection_time,omitempty"`
|
||||
// Present only while the repair window is armed.
|
||||
// Active preparation or admission-repair deadline. Absent after Ready/timeout.
|
||||
Deadline *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=deadline,proto3" json:"deadline,omitempty"`
|
||||
TimeoutTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=timeout_time,json=timeoutTime,proto3" json:"timeout_time,omitempty"`
|
||||
// Set only after both supervisor and workload compute have been reclaimed.
|
||||
@@ -17649,8 +17649,16 @@ type SandboxProvisioning struct {
|
||||
// Attachment edits have their own durable clock; status writes do not change it.
|
||||
AttachmentChangeId string `protobuf:"bytes,10,opt,name=attachment_change_id,json=attachmentChangeId,proto3" json:"attachment_change_id,omitempty"`
|
||||
AttachmentChangeTime *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=attachment_change_time,json=attachmentChangeTime,proto3" json:"attachment_change_time,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
// Absolute ceiling for image preparation and initial supervisor startup.
|
||||
// Set once per new attempt; progress, configuration writes, and restarts
|
||||
// cannot extend it. Absent on attempts created before preparation timing.
|
||||
PreparationDeadline *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=preparation_deadline,json=preparationDeadline,proto3" json:"preparation_deadline,omitempty"`
|
||||
// First authenticated supervisor configuration report for this attempt.
|
||||
// Starts the admission repair window. Absent while preparing, including
|
||||
// after a preparation timeout. Duplicate reports never change this value.
|
||||
AdmissionStartTime *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=admission_start_time,json=admissionStartTime,proto3" json:"admission_start_time,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SandboxProvisioning) Reset() {
|
||||
@@ -17760,6 +17768,20 @@ func (x *SandboxProvisioning) GetAttachmentChangeTime() *timestamppb.Timestamp {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SandboxProvisioning) GetPreparationDeadline() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.PreparationDeadline
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SandboxProvisioning) GetAdmissionStartTime() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.AdmissionStartTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create-time request to expose one loopback HTTP service in a sandbox.
|
||||
type SandboxServiceExposure struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -19141,7 +19163,7 @@ const file_openshell_proto_rawDesc = "" +
|
||||
"\x04path\x18\x04 \x01(\tR\x04path\x12=\n" +
|
||||
"\vlast_result\x18\x05 \x01(\x0e2\x1c.openshell.v1.EndpointResultR\n" +
|
||||
"lastResult\x12H\n" +
|
||||
"\x12last_reported_time\x18j \x01(\v2\x1a.google.protobuf.TimestampR\x10lastReportedTimeJ\x04\b\x06\x10\aR\x10last_reported_at\"\xce\x05\n" +
|
||||
"\x12last_reported_time\x18j \x01(\v2\x1a.google.protobuf.TimestampR\x10lastReportedTimeJ\x04\b\x06\x10\aR\x10last_reported_at\"\xeb\x06\n" +
|
||||
"\x13SandboxProvisioning\x12\x1d\n" +
|
||||
"\n" +
|
||||
"attempt_id\x18\x01 \x01(\tR\tattemptId\x126\n" +
|
||||
@@ -19155,7 +19177,9 @@ const file_openshell_proto_rawDesc = "" +
|
||||
"\x12cleanup_retry_time\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\x10cleanupRetryTime\x120\n" +
|
||||
"\x14attachment_change_id\x18\n" +
|
||||
" \x01(\tR\x12attachmentChangeId\x12P\n" +
|
||||
"\x16attachment_change_time\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\x14attachmentChangeTime\"\xaa\x01\n" +
|
||||
"\x16attachment_change_time\x18\v \x01(\v2\x1a.google.protobuf.TimestampR\x14attachmentChangeTime\x12M\n" +
|
||||
"\x14preparation_deadline\x18\f \x01(\v2\x1a.google.protobuf.TimestampR\x13preparationDeadline\x12L\n" +
|
||||
"\x14admission_start_time\x18\r \x01(\v2\x1a.google.protobuf.TimestampR\x12admissionStartTime\"\xaa\x01\n" +
|
||||
"\x16SandboxServiceExposure\x12\x18\n" +
|
||||
"\aservice\x18\x01 \x01(\tR\aservice\x12\x1f\n" +
|
||||
"\vtarget_port\x18\x02 \x01(\rR\n" +
|
||||
@@ -20108,180 +20132,182 @@ var file_openshell_proto_depIdxs = []int32{
|
||||
275, // 316: openshell.v1.SandboxProvisioning.cleanup_completed_time:type_name -> google.protobuf.Timestamp
|
||||
275, // 317: openshell.v1.SandboxProvisioning.cleanup_retry_time:type_name -> google.protobuf.Timestamp
|
||||
275, // 318: openshell.v1.SandboxProvisioning.attachment_change_time:type_name -> google.protobuf.Timestamp
|
||||
19, // 319: openshell.v1.SandboxServiceExposure.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode
|
||||
275, // 320: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp
|
||||
275, // 321: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp
|
||||
127, // 322: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential
|
||||
156, // 323: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding
|
||||
24, // 324: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest
|
||||
26, // 325: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest
|
||||
28, // 326: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest
|
||||
52, // 327: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest
|
||||
60, // 328: openshell.v1.OpenShell.BeginRootfsTarStaging:input_type -> openshell.v1.BeginRootfsTarStagingRequest
|
||||
62, // 329: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest
|
||||
63, // 330: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest
|
||||
53, // 331: openshell.v1.OpenShell.CreateSandboxTemplate:input_type -> openshell.v1.CreateSandboxTemplateRequest
|
||||
54, // 332: openshell.v1.OpenShell.GetSandboxTemplate:input_type -> openshell.v1.GetSandboxTemplateRequest
|
||||
55, // 333: openshell.v1.OpenShell.ListSandboxTemplates:input_type -> openshell.v1.ListSandboxTemplatesRequest
|
||||
56, // 334: openshell.v1.OpenShell.DeleteSandboxTemplate:input_type -> openshell.v1.DeleteSandboxTemplateRequest
|
||||
64, // 335: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest
|
||||
65, // 336: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest
|
||||
66, // 337: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest
|
||||
82, // 338: openshell.v1.OpenShell.GetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest
|
||||
67, // 339: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest
|
||||
68, // 340: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest
|
||||
69, // 341: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest
|
||||
87, // 342: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest
|
||||
89, // 343: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest
|
||||
90, // 344: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest
|
||||
91, // 345: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest
|
||||
93, // 346: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest
|
||||
97, // 347: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest
|
||||
99, // 348: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest
|
||||
105, // 349: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame
|
||||
106, // 350: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput
|
||||
113, // 351: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest
|
||||
114, // 352: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest
|
||||
115, // 353: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest
|
||||
120, // 354: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest
|
||||
121, // 355: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest
|
||||
145, // 356: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest
|
||||
147, // 357: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest
|
||||
149, // 358: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest
|
||||
116, // 359: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest
|
||||
133, // 360: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest
|
||||
135, // 361: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest
|
||||
137, // 362: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest
|
||||
139, // 363: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest
|
||||
117, // 364: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest
|
||||
152, // 365: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest
|
||||
290, // 366: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest
|
||||
291, // 367: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest
|
||||
160, // 368: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest
|
||||
170, // 369: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest
|
||||
172, // 370: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest
|
||||
174, // 371: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest
|
||||
247, // 372: openshell.v1.OpenShell.ReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest
|
||||
84, // 373: openshell.v1.OpenShell.ReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest
|
||||
177, // 374: openshell.v1.OpenShell.ReportSandboxConfiguration:input_type -> openshell.v1.ReportSandboxConfigurationRequest
|
||||
154, // 375: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest
|
||||
158, // 376: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest
|
||||
180, // 377: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest
|
||||
181, // 378: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest
|
||||
184, // 379: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage
|
||||
191, // 380: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest
|
||||
193, // 381: openshell.v1.OpenShell.FinalizeMainProcessExit:input_type -> openshell.v1.FinalizeMainProcessExitRequest
|
||||
199, // 382: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame
|
||||
201, // 383: openshell.v1.OpenShell.PeerRelay:input_type -> openshell.v1.PeerRelayFrame
|
||||
84, // 384: openshell.v1.OpenShell.PeerReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest
|
||||
247, // 385: openshell.v1.OpenShell.PeerReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest
|
||||
82, // 386: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest
|
||||
109, // 387: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest
|
||||
210, // 388: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest
|
||||
212, // 389: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest
|
||||
214, // 390: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest
|
||||
216, // 391: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest
|
||||
219, // 392: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest
|
||||
221, // 393: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest
|
||||
223, // 394: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest
|
||||
225, // 395: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest
|
||||
227, // 396: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest
|
||||
20, // 397: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest
|
||||
22, // 398: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest
|
||||
230, // 399: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest
|
||||
232, // 400: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest
|
||||
234, // 401: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest
|
||||
236, // 402: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest
|
||||
239, // 403: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest
|
||||
241, // 404: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest
|
||||
243, // 405: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest
|
||||
25, // 406: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse
|
||||
27, // 407: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse
|
||||
29, // 408: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse
|
||||
70, // 409: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse
|
||||
61, // 410: openshell.v1.OpenShell.BeginRootfsTarStaging:output_type -> openshell.v1.BeginRootfsTarStagingResponse
|
||||
70, // 411: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse
|
||||
71, // 412: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse
|
||||
57, // 413: openshell.v1.OpenShell.CreateSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse
|
||||
57, // 414: openshell.v1.OpenShell.GetSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse
|
||||
58, // 415: openshell.v1.OpenShell.ListSandboxTemplates:output_type -> openshell.v1.ListSandboxTemplatesResponse
|
||||
59, // 416: openshell.v1.OpenShell.DeleteSandboxTemplate:output_type -> openshell.v1.DeleteSandboxTemplateResponse
|
||||
72, // 417: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse
|
||||
73, // 418: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse
|
||||
74, // 419: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse
|
||||
83, // 420: openshell.v1.OpenShell.GetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse
|
||||
86, // 421: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse
|
||||
70, // 422: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse
|
||||
70, // 423: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse
|
||||
88, // 424: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse
|
||||
96, // 425: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse
|
||||
96, // 426: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse
|
||||
92, // 427: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse
|
||||
94, // 428: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse
|
||||
98, // 429: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse
|
||||
103, // 430: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent
|
||||
105, // 431: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame
|
||||
103, // 432: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent
|
||||
118, // 433: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse
|
||||
118, // 434: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse
|
||||
119, // 435: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse
|
||||
144, // 436: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse
|
||||
143, // 437: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse
|
||||
146, // 438: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse
|
||||
148, // 439: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse
|
||||
150, // 440: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse
|
||||
118, // 441: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse
|
||||
134, // 442: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse
|
||||
136, // 443: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse
|
||||
138, // 444: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse
|
||||
140, // 445: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse
|
||||
151, // 446: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse
|
||||
153, // 447: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse
|
||||
292, // 448: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse
|
||||
293, // 449: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse
|
||||
169, // 450: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse
|
||||
171, // 451: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse
|
||||
173, // 452: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse
|
||||
175, // 453: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse
|
||||
248, // 454: openshell.v1.OpenShell.ReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse
|
||||
85, // 455: openshell.v1.OpenShell.ReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse
|
||||
178, // 456: openshell.v1.OpenShell.ReportSandboxConfiguration:output_type -> openshell.v1.ReportSandboxConfigurationResponse
|
||||
157, // 457: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse
|
||||
159, // 458: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse
|
||||
183, // 459: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse
|
||||
182, // 460: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse
|
||||
185, // 461: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage
|
||||
192, // 462: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse
|
||||
194, // 463: openshell.v1.OpenShell.FinalizeMainProcessExit:output_type -> openshell.v1.FinalizeMainProcessExitResponse
|
||||
199, // 464: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame
|
||||
201, // 465: openshell.v1.OpenShell.PeerRelay:output_type -> openshell.v1.PeerRelayFrame
|
||||
85, // 466: openshell.v1.OpenShell.PeerReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse
|
||||
248, // 467: openshell.v1.OpenShell.PeerReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse
|
||||
83, // 468: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse
|
||||
110, // 469: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent
|
||||
211, // 470: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse
|
||||
213, // 471: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse
|
||||
215, // 472: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse
|
||||
217, // 473: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse
|
||||
220, // 474: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse
|
||||
222, // 475: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse
|
||||
224, // 476: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse
|
||||
226, // 477: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse
|
||||
229, // 478: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse
|
||||
21, // 479: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse
|
||||
23, // 480: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse
|
||||
231, // 481: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse
|
||||
233, // 482: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse
|
||||
235, // 483: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse
|
||||
237, // 484: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse
|
||||
240, // 485: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse
|
||||
242, // 486: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse
|
||||
244, // 487: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse
|
||||
406, // [406:488] is the sub-list for method output_type
|
||||
324, // [324:406] is the sub-list for method input_type
|
||||
324, // [324:324] is the sub-list for extension type_name
|
||||
324, // [324:324] is the sub-list for extension extendee
|
||||
0, // [0:324] is the sub-list for field type_name
|
||||
275, // 319: openshell.v1.SandboxProvisioning.preparation_deadline:type_name -> google.protobuf.Timestamp
|
||||
275, // 320: openshell.v1.SandboxProvisioning.admission_start_time:type_name -> google.protobuf.Timestamp
|
||||
19, // 321: openshell.v1.SandboxServiceExposure.authorization_mode:type_name -> openshell.v1.ServiceAuthorizationMode
|
||||
275, // 322: openshell.v1.UpdateProviderRequest.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp
|
||||
275, // 323: openshell.v1.GetSandboxProviderEnvironmentResponse.CredentialExpirationTimesEntry.value:type_name -> google.protobuf.Timestamp
|
||||
127, // 324: openshell.v1.GetSandboxProviderEnvironmentResponse.DynamicCredentialsEntry.value:type_name -> openshell.v1.ProviderProfileCredential
|
||||
156, // 325: openshell.v1.GetSandboxProviderEnvironmentResponse.StaticCredentialBindingsEntry.value:type_name -> openshell.v1.StaticCredentialBinding
|
||||
24, // 326: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest
|
||||
26, // 327: openshell.v1.OpenShell.GetCurrentUser:input_type -> openshell.v1.GetCurrentUserRequest
|
||||
28, // 328: openshell.v1.OpenShell.GetGatewayInfo:input_type -> openshell.v1.GetGatewayInfoRequest
|
||||
52, // 329: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest
|
||||
60, // 330: openshell.v1.OpenShell.BeginRootfsTarStaging:input_type -> openshell.v1.BeginRootfsTarStagingRequest
|
||||
62, // 331: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest
|
||||
63, // 332: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest
|
||||
53, // 333: openshell.v1.OpenShell.CreateSandboxTemplate:input_type -> openshell.v1.CreateSandboxTemplateRequest
|
||||
54, // 334: openshell.v1.OpenShell.GetSandboxTemplate:input_type -> openshell.v1.GetSandboxTemplateRequest
|
||||
55, // 335: openshell.v1.OpenShell.ListSandboxTemplates:input_type -> openshell.v1.ListSandboxTemplatesRequest
|
||||
56, // 336: openshell.v1.OpenShell.DeleteSandboxTemplate:input_type -> openshell.v1.DeleteSandboxTemplateRequest
|
||||
64, // 337: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest
|
||||
65, // 338: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest
|
||||
66, // 339: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest
|
||||
82, // 340: openshell.v1.OpenShell.GetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest
|
||||
67, // 341: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest
|
||||
68, // 342: openshell.v1.OpenShell.StopSandbox:input_type -> openshell.v1.StopSandboxRequest
|
||||
69, // 343: openshell.v1.OpenShell.StartSandbox:input_type -> openshell.v1.StartSandboxRequest
|
||||
87, // 344: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest
|
||||
89, // 345: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest
|
||||
90, // 346: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest
|
||||
91, // 347: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest
|
||||
93, // 348: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest
|
||||
97, // 349: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest
|
||||
99, // 350: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest
|
||||
105, // 351: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame
|
||||
106, // 352: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput
|
||||
113, // 353: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest
|
||||
114, // 354: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest
|
||||
115, // 355: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest
|
||||
120, // 356: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest
|
||||
121, // 357: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest
|
||||
145, // 358: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest
|
||||
147, // 359: openshell.v1.OpenShell.UpdateProviderProfiles:input_type -> openshell.v1.UpdateProviderProfilesRequest
|
||||
149, // 360: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest
|
||||
116, // 361: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest
|
||||
133, // 362: openshell.v1.OpenShell.GetProviderRefreshStatus:input_type -> openshell.v1.GetProviderRefreshStatusRequest
|
||||
135, // 363: openshell.v1.OpenShell.ConfigureProviderRefresh:input_type -> openshell.v1.ConfigureProviderRefreshRequest
|
||||
137, // 364: openshell.v1.OpenShell.RotateProviderCredential:input_type -> openshell.v1.RotateProviderCredentialRequest
|
||||
139, // 365: openshell.v1.OpenShell.DeleteProviderRefresh:input_type -> openshell.v1.DeleteProviderRefreshRequest
|
||||
117, // 366: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest
|
||||
152, // 367: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest
|
||||
290, // 368: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest
|
||||
291, // 369: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest
|
||||
160, // 370: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest
|
||||
170, // 371: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest
|
||||
172, // 372: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest
|
||||
174, // 373: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest
|
||||
247, // 374: openshell.v1.OpenShell.ReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest
|
||||
84, // 375: openshell.v1.OpenShell.ReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest
|
||||
177, // 376: openshell.v1.OpenShell.ReportSandboxConfiguration:input_type -> openshell.v1.ReportSandboxConfigurationRequest
|
||||
154, // 377: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest
|
||||
158, // 378: openshell.v1.OpenShell.ExchangeProviderSubjectToken:input_type -> openshell.v1.ExchangeProviderSubjectTokenRequest
|
||||
180, // 379: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest
|
||||
181, // 380: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest
|
||||
184, // 381: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage
|
||||
191, // 382: openshell.v1.OpenShell.ReportMainProcessExit:input_type -> openshell.v1.ReportMainProcessExitRequest
|
||||
193, // 383: openshell.v1.OpenShell.FinalizeMainProcessExit:input_type -> openshell.v1.FinalizeMainProcessExitRequest
|
||||
199, // 384: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame
|
||||
201, // 385: openshell.v1.OpenShell.PeerRelay:input_type -> openshell.v1.PeerRelayFrame
|
||||
84, // 386: openshell.v1.OpenShell.PeerReportProviderReadiness:input_type -> openshell.v1.ReportProviderReadinessRequest
|
||||
247, // 387: openshell.v1.OpenShell.PeerReportEndpointStatus:input_type -> openshell.v1.ReportEndpointStatusRequest
|
||||
82, // 388: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:input_type -> openshell.v1.GetSandboxProviderStatusRequest
|
||||
109, // 389: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest
|
||||
210, // 390: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest
|
||||
212, // 391: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest
|
||||
214, // 392: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest
|
||||
216, // 393: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest
|
||||
219, // 394: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest
|
||||
221, // 395: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest
|
||||
223, // 396: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest
|
||||
225, // 397: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest
|
||||
227, // 398: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest
|
||||
20, // 399: openshell.v1.OpenShell.IssueSandboxToken:input_type -> openshell.v1.IssueSandboxTokenRequest
|
||||
22, // 400: openshell.v1.OpenShell.RefreshSandboxToken:input_type -> openshell.v1.RefreshSandboxTokenRequest
|
||||
230, // 401: openshell.v1.OpenShell.CreateWorkspace:input_type -> openshell.v1.CreateWorkspaceRequest
|
||||
232, // 402: openshell.v1.OpenShell.GetWorkspace:input_type -> openshell.v1.GetWorkspaceRequest
|
||||
234, // 403: openshell.v1.OpenShell.ListWorkspaces:input_type -> openshell.v1.ListWorkspacesRequest
|
||||
236, // 404: openshell.v1.OpenShell.DeleteWorkspace:input_type -> openshell.v1.DeleteWorkspaceRequest
|
||||
239, // 405: openshell.v1.OpenShell.AddWorkspaceMember:input_type -> openshell.v1.AddWorkspaceMemberRequest
|
||||
241, // 406: openshell.v1.OpenShell.RemoveWorkspaceMember:input_type -> openshell.v1.RemoveWorkspaceMemberRequest
|
||||
243, // 407: openshell.v1.OpenShell.ListWorkspaceMembers:input_type -> openshell.v1.ListWorkspaceMembersRequest
|
||||
25, // 408: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse
|
||||
27, // 409: openshell.v1.OpenShell.GetCurrentUser:output_type -> openshell.v1.GetCurrentUserResponse
|
||||
29, // 410: openshell.v1.OpenShell.GetGatewayInfo:output_type -> openshell.v1.GetGatewayInfoResponse
|
||||
70, // 411: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse
|
||||
61, // 412: openshell.v1.OpenShell.BeginRootfsTarStaging:output_type -> openshell.v1.BeginRootfsTarStagingResponse
|
||||
70, // 413: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse
|
||||
71, // 414: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse
|
||||
57, // 415: openshell.v1.OpenShell.CreateSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse
|
||||
57, // 416: openshell.v1.OpenShell.GetSandboxTemplate:output_type -> openshell.v1.SandboxTemplateResponse
|
||||
58, // 417: openshell.v1.OpenShell.ListSandboxTemplates:output_type -> openshell.v1.ListSandboxTemplatesResponse
|
||||
59, // 418: openshell.v1.OpenShell.DeleteSandboxTemplate:output_type -> openshell.v1.DeleteSandboxTemplateResponse
|
||||
72, // 419: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse
|
||||
73, // 420: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse
|
||||
74, // 421: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse
|
||||
83, // 422: openshell.v1.OpenShell.GetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse
|
||||
86, // 423: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse
|
||||
70, // 424: openshell.v1.OpenShell.StopSandbox:output_type -> openshell.v1.SandboxResponse
|
||||
70, // 425: openshell.v1.OpenShell.StartSandbox:output_type -> openshell.v1.SandboxResponse
|
||||
88, // 426: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse
|
||||
96, // 427: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse
|
||||
96, // 428: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse
|
||||
92, // 429: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse
|
||||
94, // 430: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse
|
||||
98, // 431: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse
|
||||
103, // 432: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent
|
||||
105, // 433: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame
|
||||
103, // 434: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent
|
||||
118, // 435: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse
|
||||
118, // 436: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse
|
||||
119, // 437: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse
|
||||
144, // 438: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse
|
||||
143, // 439: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse
|
||||
146, // 440: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse
|
||||
148, // 441: openshell.v1.OpenShell.UpdateProviderProfiles:output_type -> openshell.v1.UpdateProviderProfilesResponse
|
||||
150, // 442: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse
|
||||
118, // 443: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse
|
||||
134, // 444: openshell.v1.OpenShell.GetProviderRefreshStatus:output_type -> openshell.v1.GetProviderRefreshStatusResponse
|
||||
136, // 445: openshell.v1.OpenShell.ConfigureProviderRefresh:output_type -> openshell.v1.ConfigureProviderRefreshResponse
|
||||
138, // 446: openshell.v1.OpenShell.RotateProviderCredential:output_type -> openshell.v1.RotateProviderCredentialResponse
|
||||
140, // 447: openshell.v1.OpenShell.DeleteProviderRefresh:output_type -> openshell.v1.DeleteProviderRefreshResponse
|
||||
151, // 448: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse
|
||||
153, // 449: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse
|
||||
292, // 450: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse
|
||||
293, // 451: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse
|
||||
169, // 452: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse
|
||||
171, // 453: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse
|
||||
173, // 454: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse
|
||||
175, // 455: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse
|
||||
248, // 456: openshell.v1.OpenShell.ReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse
|
||||
85, // 457: openshell.v1.OpenShell.ReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse
|
||||
178, // 458: openshell.v1.OpenShell.ReportSandboxConfiguration:output_type -> openshell.v1.ReportSandboxConfigurationResponse
|
||||
157, // 459: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse
|
||||
159, // 460: openshell.v1.OpenShell.ExchangeProviderSubjectToken:output_type -> openshell.v1.ExchangeProviderSubjectTokenResponse
|
||||
183, // 461: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse
|
||||
182, // 462: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse
|
||||
185, // 463: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage
|
||||
192, // 464: openshell.v1.OpenShell.ReportMainProcessExit:output_type -> openshell.v1.ReportMainProcessExitResponse
|
||||
194, // 465: openshell.v1.OpenShell.FinalizeMainProcessExit:output_type -> openshell.v1.FinalizeMainProcessExitResponse
|
||||
199, // 466: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame
|
||||
201, // 467: openshell.v1.OpenShell.PeerRelay:output_type -> openshell.v1.PeerRelayFrame
|
||||
85, // 468: openshell.v1.OpenShell.PeerReportProviderReadiness:output_type -> openshell.v1.ReportProviderReadinessResponse
|
||||
248, // 469: openshell.v1.OpenShell.PeerReportEndpointStatus:output_type -> openshell.v1.ReportEndpointStatusResponse
|
||||
83, // 470: openshell.v1.OpenShell.PeerGetSandboxProviderStatus:output_type -> openshell.v1.GetSandboxProviderStatusResponse
|
||||
110, // 471: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent
|
||||
211, // 472: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse
|
||||
213, // 473: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse
|
||||
215, // 474: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse
|
||||
217, // 475: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse
|
||||
220, // 476: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse
|
||||
222, // 477: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse
|
||||
224, // 478: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse
|
||||
226, // 479: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse
|
||||
229, // 480: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse
|
||||
21, // 481: openshell.v1.OpenShell.IssueSandboxToken:output_type -> openshell.v1.IssueSandboxTokenResponse
|
||||
23, // 482: openshell.v1.OpenShell.RefreshSandboxToken:output_type -> openshell.v1.RefreshSandboxTokenResponse
|
||||
231, // 483: openshell.v1.OpenShell.CreateWorkspace:output_type -> openshell.v1.CreateWorkspaceResponse
|
||||
233, // 484: openshell.v1.OpenShell.GetWorkspace:output_type -> openshell.v1.GetWorkspaceResponse
|
||||
235, // 485: openshell.v1.OpenShell.ListWorkspaces:output_type -> openshell.v1.ListWorkspacesResponse
|
||||
237, // 486: openshell.v1.OpenShell.DeleteWorkspace:output_type -> openshell.v1.DeleteWorkspaceResponse
|
||||
240, // 487: openshell.v1.OpenShell.AddWorkspaceMember:output_type -> openshell.v1.AddWorkspaceMemberResponse
|
||||
242, // 488: openshell.v1.OpenShell.RemoveWorkspaceMember:output_type -> openshell.v1.RemoveWorkspaceMemberResponse
|
||||
244, // 489: openshell.v1.OpenShell.ListWorkspaceMembers:output_type -> openshell.v1.ListWorkspaceMembersResponse
|
||||
408, // [408:490] is the sub-list for method output_type
|
||||
326, // [326:408] is the sub-list for method input_type
|
||||
326, // [326:326] is the sub-list for extension type_name
|
||||
326, // [326:326] is the sub-list for extension extendee
|
||||
0, // [0:326] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_openshell_proto_init() }
|
||||
|
||||
Reference in New Issue
Block a user