diff --git a/.github/workflows/branch-e2e.yml b/.github/workflows/branch-e2e.yml index 2b4d9d5d46..fe1a928246 100644 --- a/.github/workflows/branch-e2e.yml +++ b/.github/workflows/branch-e2e.yml @@ -159,7 +159,7 @@ jobs: matrix: include: - agent_sandbox_api: v1beta1 - agent_sandbox_version: v0.5.0 + agent_sandbox_version: v0.5.4 topology: combined extra_helm_values: "" - agent_sandbox_api: v1alpha1 @@ -167,7 +167,7 @@ jobs: topology: combined extra_helm_values: "" - agent_sandbox_api: v1beta1 - agent_sandbox_version: v0.5.0 + agent_sandbox_version: v0.5.4 topology: sidecar extra_helm_values: deploy/helm/openshell/ci/values-sidecar.yaml permissions: diff --git a/.github/workflows/e2e-kubernetes-test.yml b/.github/workflows/e2e-kubernetes-test.yml index 3e36570144..1c32e400e4 100644 --- a/.github/workflows/e2e-kubernetes-test.yml +++ b/.github/workflows/e2e-kubernetes-test.yml @@ -36,7 +36,7 @@ on: description: "Agent Sandbox release to install before OpenShell" required: false type: string - default: "v0.5.0" + default: "v0.5.4" e2e-task: description: "mise task to run for the Kubernetes e2e job" required: false diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index 937e774db7..6a1bacc186 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -214,7 +214,7 @@ jobs: RELEASE_NAME: openshell RELEASE_NAMESPACE: openshell KIND_GATEWAY_NAME: kind - AGENT_SANDBOX_VERSION: v0.5.0 + AGENT_SANDBOX_VERSION: v0.5.4 steps: - name: Checkout Agent Sandbox helper uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/crates/openshell-driver-kubernetes/src/driver.rs b/crates/openshell-driver-kubernetes/src/driver.rs index 84d7029de4..84cf521ad6 100644 --- a/crates/openshell-driver-kubernetes/src/driver.rs +++ b/crates/openshell-driver-kubernetes/src/driver.rs @@ -4656,7 +4656,7 @@ mod tests { } #[test] - fn stopped_status_requires_published_condition() { + fn stopped_status_requires_suspended_true_condition() { let resource = ApiResource::from_gvk(&GroupVersionKind::gvk( SANDBOX_GROUP, SANDBOX_VERSION_V1ALPHA1, @@ -4672,9 +4672,26 @@ mod tests { sandbox.data = serde_json::json!({ "status": { - "conditions": [{"type": "Suspended", "status": "True"}] + "conditions": [{ + "type": "Suspended", + "status": "False", + "reason": "NotSuspended" + }] } }); + assert!( + !kubernetes_sandbox_has_stopped_condition(&sandbox), + "v0.5.4 publishes Suspended=False while the sandbox is running" + ); + + sandbox.data["status"]["conditions"][0]["reason"] = serde_json::json!("PodTerminating"); + assert!( + !kubernetes_sandbox_has_stopped_condition(&sandbox), + "Suspended=False remains a progress signal while the pod terminates" + ); + + sandbox.data["status"]["conditions"][0]["status"] = serde_json::json!("True"); + sandbox.data["status"]["conditions"][0]["reason"] = serde_json::json!("PodTerminated"); assert!(kubernetes_sandbox_has_stopped_condition(&sandbox)); } diff --git a/crates/openshell-server/src/compute/mod.rs b/crates/openshell-server/src/compute/mod.rs index 51f05a395c..3df564e66f 100644 --- a/crates/openshell-server/src/compute/mod.rs +++ b/crates/openshell-server/src/compute/mod.rs @@ -3981,9 +3981,10 @@ fn derive_phase(status: Option<&DriverSandboxStatus>) -> SandboxPhase { } // `Ready=True` means the sandbox is usable through this gateway and must - // win over a `Suspended=True` condition. Agent Sandbox v1beta1 sets - // `Suspended=True (PodTerminated)` on stop and does not clear it on resume, - // so a resumed CR carries both `Ready=True` and a stale `Suspended=True`. + // win over a `Suspended=True` condition. Older Agent Sandbox v1beta1 + // releases set `Suspended=True (PodTerminated)` on stop and do not clear it + // on resume, so a resumed CR carries both `Ready=True` and a stale + // `Suspended=True`. // Treating any `Suspended=True` as Stopped would pin the resumed sandbox at // Starting forever (issue #2932). A genuine stop leaves `Ready` unset or // False, so `Suspended` still resolves to Stopped in that case. @@ -5595,6 +5596,31 @@ mod tests { assert_eq!(derive_phase(Some(&status)), SandboxPhase::Ready); } + #[test] + fn derive_phase_returns_ready_for_v054_running_conditions() { + let status = DriverSandboxStatus { + conditions: vec![ + DriverCondition { + r#type: "Suspended".to_string(), + status: "False".to_string(), + reason: "NotSuspended".to_string(), + message: "Sandbox is not suspended".to_string(), + last_transition_time: String::new(), + }, + DriverCondition { + r#type: "Ready".to_string(), + status: "True".to_string(), + reason: "DependenciesReady".to_string(), + message: "Pod is Ready; Service Exists".to_string(), + last_transition_time: String::new(), + }, + ], + ..Default::default() + }; + + assert_eq!(derive_phase(Some(&status)), SandboxPhase::Ready); + } + #[test] fn build_platform_config_omits_typed_cpu_and_memory_resources() { let template = SandboxTemplate { diff --git a/deploy/helm/openshell/README.md b/deploy/helm/openshell/README.md index 5879d617ff..d5953460d4 100644 --- a/deploy/helm/openshell/README.md +++ b/deploy/helm/openshell/README.md @@ -14,7 +14,7 @@ This chart deploys the OpenShell gateway into a Kubernetes cluster. It is publis The Kubernetes Agent Sandbox CRDs and controller must be installed on the cluster before deploying OpenShell. Install them with: ```shell -kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/manifest.yaml +kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/sandbox.yaml ``` The chart does not install this cluster-scoped dependency. By default, it diff --git a/deploy/helm/openshell/README.md.gotmpl b/deploy/helm/openshell/README.md.gotmpl index 73ebb39c88..6901491d4c 100644 --- a/deploy/helm/openshell/README.md.gotmpl +++ b/deploy/helm/openshell/README.md.gotmpl @@ -14,7 +14,7 @@ This chart deploys the OpenShell gateway into a Kubernetes cluster. It is publis The Kubernetes Agent Sandbox CRDs and controller must be installed on the cluster before deploying OpenShell. Install them with: ```shell -kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/manifest.yaml +kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/sandbox.yaml ``` The chart does not install this cluster-scoped dependency. By default, it diff --git a/docs/kubernetes/setup.mdx b/docs/kubernetes/setup.mdx index 221f935eb6..ce6e85ad46 100644 --- a/docs/kubernetes/setup.mdx +++ b/docs/kubernetes/setup.mdx @@ -33,7 +33,7 @@ OpenShell uses the [Agent Sandbox](https://agent-sandbox.sigs.k8s.io) Kubernetes Apply the latest release manifest: ```shell -kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/manifest.yaml +kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/latest/download/sandbox.yaml ``` This creates the `agent-sandbox-system` namespace, installs the `sandboxes.agents.x-k8s.io` CRD, and starts the controller. diff --git a/docs/reference/sandbox-compute-drivers.mdx b/docs/reference/sandbox-compute-drivers.mdx index 656ae43bb6..c468ccbcb2 100644 --- a/docs/reference/sandbox-compute-drivers.mdx +++ b/docs/reference/sandbox-compute-drivers.mdx @@ -430,7 +430,10 @@ The Kubernetes driver creates namespaced `agents.x-k8s.io` `Sandbox` resources f Stop patches the existing resource rather than deleting it. For `v1beta1`, the driver sets `spec.operatingMode` to `Suspended` or `Running`. For `v1alpha1`, it sets `spec.replicas` to `0` or `1`. The Sandbox resource and its -workspace PVC keep their identity across both operations. +workspace PVC keep their identity across both operations. A stop completes only +when the controller reports the `Suspended` condition with status `True`; +v0.5.4 and later also retain this condition with status `False` while the +sandbox is running. If Agent Sandbox is upgraded in place, restart the OpenShell gateway after the controller and CRD rollout completes so the gateway can detect the served API versions again. diff --git a/e2e/support/install-agent-sandbox.sh b/e2e/support/install-agent-sandbox.sh index 7b33a53eea..bf2ed656b8 100644 --- a/e2e/support/install-agent-sandbox.sh +++ b/e2e/support/install-agent-sandbox.sh @@ -6,7 +6,25 @@ # context arguments (for example, --context kind-e2e) as script arguments. set -euo pipefail -agent_sandbox_version="${AGENT_SANDBOX_VERSION:-v0.5.0}" +agent_sandbox_version="${AGENT_SANDBOX_VERSION:-v0.5.4}" + +agent_sandbox_manifest_asset() { + local version="${1#v}" + local major minor patch + IFS=. read -r major minor patch <<<"${version}" + patch="${patch%%[-+]*}" + + if [[ ! "${major}" =~ ^[0-9]+$ || ! "${minor}" =~ ^[0-9]+$ || ! "${patch}" =~ ^[0-9]+$ ]]; then + echo "error: invalid Agent Sandbox release version: $1" >&2 + return 1 + fi + + if (( 10#${major} > 0 || 10#${minor} > 5 || (10#${minor} == 5 && 10#${patch} >= 2) )); then + echo "sandbox.yaml" + else + echo "manifest.yaml" + fi +} wait_for_agent_sandbox_crd() { local deadline @@ -32,7 +50,8 @@ wait_for_agent_sandbox_crd() { echo "Installing agent-sandbox CRDs and controller (${agent_sandbox_version})..." agent_sandbox_base="https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${agent_sandbox_version}" -kubectl "$@" apply -f "${agent_sandbox_base}/manifest.yaml" +agent_sandbox_manifest="$(agent_sandbox_manifest_asset "${agent_sandbox_version}")" +kubectl "$@" apply -f "${agent_sandbox_base}/${agent_sandbox_manifest}" wait_for_agent_sandbox_crd "$@" kubectl "$@" -n agent-sandbox-system rollout status \ deployment/agent-sandbox-controller --timeout=300s diff --git a/e2e/with-kube-gateway.sh b/e2e/with-kube-gateway.sh index f83c8bafe1..3a200e95d4 100755 --- a/e2e/with-kube-gateway.sh +++ b/e2e/with-kube-gateway.sh @@ -61,7 +61,7 @@ source "${ROOT}/e2e/support/gateway-common.sh" # Upstream agent-sandbox release. The Kubernetes driver supports the v1beta1 # Sandbox API introduced in v0.5.0 and falls back to v1alpha1 for v0.4.6 # clusters. Override this env var to exercise the v1alpha1 controller release. -AGENT_SANDBOX_VERSION="${AGENT_SANDBOX_VERSION:-v0.5.0}" +AGENT_SANDBOX_VERSION="${AGENT_SANDBOX_VERSION:-v0.5.4}" e2e_preserve_mise_dirs e2e_align_docker_host_with_cli_context diff --git a/tasks/scripts/helm-k3s-local.sh b/tasks/scripts/helm-k3s-local.sh index 82b8d5cfc8..26d9eaacf4 100755 --- a/tasks/scripts/helm-k3s-local.sh +++ b/tasks/scripts/helm-k3s-local.sh @@ -36,7 +36,7 @@ PRELOAD_SANDBOX_IMAGE="${HELM_K3S_PRELOAD_SANDBOX_IMAGE-${DEFAULT_SANDBOX_PRELOA # The Kubernetes driver supports the v1beta1 Sandbox API introduced in v0.5.0 # and falls back to v1alpha1 for v0.4.6 clusters. Override this env var to # exercise the v1alpha1 controller release. -AGENT_SANDBOX_VERSION="${AGENT_SANDBOX_VERSION:-v0.5.0}" +AGENT_SANDBOX_VERSION="${AGENT_SANDBOX_VERSION:-v0.5.4}" default_kubeconfig="${ROOT}/kubeconfig" if [[ -n "${HELM_K3S_KUBECONFIG:-}" ]]; then @@ -142,8 +142,28 @@ merge_kubeconfig() { apply_base_manifests() { require_kubectl local base="https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${AGENT_SANDBOX_VERSION}" + local manifest + manifest="$(agent_sandbox_manifest_asset "${AGENT_SANDBOX_VERSION}")" echo "Applying agent-sandbox manifest (${AGENT_SANDBOX_VERSION})..." - kubectl --kubeconfig="${KUBECONFIG_TARGET}" apply -f "${base}/manifest.yaml" + kubectl --kubeconfig="${KUBECONFIG_TARGET}" apply -f "${base}/${manifest}" +} + +agent_sandbox_manifest_asset() { + local version="${1#v}" + local major minor patch + IFS=. read -r major minor patch <<<"${version}" + patch="${patch%%[-+]*}" + + if [[ ! "${major}" =~ ^[0-9]+$ || ! "${minor}" =~ ^[0-9]+$ || ! "${patch}" =~ ^[0-9]+$ ]]; then + echo "error: invalid Agent Sandbox release version: $1" >&2 + return 1 + fi + + if (( 10#${major} > 0 || 10#${minor} > 5 || (10#${minor} == 5 && 10#${patch} >= 2) )); then + echo "sandbox.yaml" + else + echo "manifest.yaml" + fi } configure_ghcr_credentials() {