From b994e99a4188bef549e5fa1f944eb3546be43201 Mon Sep 17 00:00:00 2001
From: liuxu <liuxu156@huawei.com>
Date: Wed, 18 Sep 2024 11:40:15 +0800
Subject: [PATCH 151/156] sandbox:sandbox api update

Signed-off-by: liuxu <liuxu156@huawei.com>
---
 cmake/protoc.cmake                            |   1 +
 src/api/services/sandbox/sandbox.proto        | 107 +++---
 .../sandbox/sandbox/types/metrics.proto       |  30 ++
 .../sandbox/sandbox/types/platform.proto      |   3 +-
 .../sandbox/sandbox/types/sandbox.proto       |  15 +-
 src/daemon/sandbox/controller/controller.h    |  12 +-
 .../sandboxer/client/grpc_sandboxer_client.cc | 111 ++----
 .../sandboxer/client/grpc_sandboxer_client.h  |  18 +-
 .../sandboxer/sandboxer_controller.cc         |  19 +-
 .../sandboxer/sandboxer_controller.h          |   9 +-
 .../controller/shim/shim_controller.cc        |  11 +-
 .../sandbox/controller/shim/shim_controller.h |   9 +-
 src/daemon/sandbox/sandbox.cc                 | 221 +++++++++++-
 src/daemon/sandbox/sandbox.h                  |  24 ++
 src/daemon/sandbox/sandbox_ops.cc             | 333 ++++++++++++++----
 src/daemon/sandbox/sandbox_task.cc            |  99 ++++++
 src/daemon/sandbox/sandbox_task.h             |  48 +++
 17 files changed, 808 insertions(+), 262 deletions(-)
 create mode 100644 src/api/services/sandbox/sandbox/types/metrics.proto
 create mode 100644 src/daemon/sandbox/sandbox_task.cc
 create mode 100644 src/daemon/sandbox/sandbox_task.h

diff --git a/cmake/protoc.cmake b/cmake/protoc.cmake
index 6e2d1b84..6343fe3e 100644
--- a/cmake/protoc.cmake
+++ b/cmake/protoc.cmake
@@ -72,6 +72,7 @@ if (ENABLE_CRI_API_V1 AND ENABLE_SANDBOXER)
     PROTOC_CPP_GEN(sandbox ${SANDBOX_PROTOS_OUT_PATH} ${PROTOS_PATH}/sandbox/sandbox/types/sandbox.proto)
     PROTOC_CPP_GEN(sandbox ${SANDBOX_PROTOS_OUT_PATH} ${PROTOS_PATH}/sandbox/sandbox/types/mount.proto)
     PROTOC_CPP_GEN(sandbox ${SANDBOX_PROTOS_OUT_PATH} ${PROTOS_PATH}/sandbox/sandbox/types/platform.proto)
+    PROTOC_CPP_GEN(sandbox ${SANDBOX_PROTOS_OUT_PATH} ${PROTOS_PATH}/sandbox/sandbox/types/metrics.proto)
     PROTOC_CPP_GEN(sandbox ${SANDBOX_PROTOS_OUT_PATH} ${PROTOS_PATH}/sandbox/sandbox.proto)
     PROTOC_GRPC_GEN(sandbox ${SANDBOX_PROTOS_OUT_PATH} ${PROTOS_PATH}/sandbox/sandbox.proto)
 endif()
diff --git a/src/api/services/sandbox/sandbox.proto b/src/api/services/sandbox/sandbox.proto
index 87c7e27c..dcc78444 100644
--- a/src/api/services/sandbox/sandbox.proto
+++ b/src/api/services/sandbox/sandbox.proto
@@ -31,6 +31,7 @@ import "google/protobuf/timestamp.proto";
 import "sandbox/types/sandbox.proto";
 import "sandbox/types/mount.proto";
 import "sandbox/types/platform.proto";
+import "sandbox/types/metrics.proto";
 
 option go_package = "github.com/containerd/containerd/api/services/sandbox/v1;sandbox";
 
@@ -89,21 +90,22 @@ service Controller {
 	rpc Create(ControllerCreateRequest) returns (ControllerCreateResponse);
 	rpc Start(ControllerStartRequest) returns (ControllerStartResponse);
 	rpc Platform(ControllerPlatformRequest) returns (ControllerPlatformResponse);
-	rpc Prepare(PrepareRequest) returns (PrepareResponse);
-	rpc Purge(PurgeRequest) returns (PurgeResponse);
-	rpc UpdateResources(UpdateResourcesRequest) returns (UpdateResourcesResponse);
 	rpc Stop(ControllerStopRequest) returns (ControllerStopResponse);
 	rpc Wait(ControllerWaitRequest) returns (ControllerWaitResponse);
 	rpc Status(ControllerStatusRequest) returns (ControllerStatusResponse);
 	rpc Shutdown(ControllerShutdownRequest) returns (ControllerShutdownResponse);
+	rpc Metrics(ControllerMetricsRequest) returns (ControllerMetricsResponse);
+	rpc Update(ControllerUpdateRequest) returns (ControllerUpdateResponse);
 }
 
 message ControllerCreateRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
-	repeated containerd.types.Mount rootfs = 3;
-	google.protobuf.Any options = 4;
-	string netns_path = 5;
+	string sandbox_id = 1;
+	repeated containerd.types.Mount rootfs = 2;
+	google.protobuf.Any options = 3;
+	string netns_path = 4;
+	map<string, string> annotations = 5;
+	containerd.types.Sandbox sandbox = 6;
+	string sandboxer = 10;
 }
 
 message ControllerCreateResponse {
@@ -111,8 +113,8 @@ message ControllerCreateResponse {
 }
 
 message ControllerStartRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
+	string sandbox_id = 1;
+	string sandboxer = 10;
 }
 
 message ControllerStartResponse {
@@ -120,12 +122,16 @@ message ControllerStartResponse {
 	uint32 pid = 2;
 	google.protobuf.Timestamp created_at = 3;
 	map<string, string> labels = 4;
-	string task_address = 5;
+	// Address of the sandbox for containerd to connect,
+	// for calling Task or other APIs serving in the sandbox.
+	// it is in the form of ttrpc+unix://path/to/uds or grpc+vsock://<vsock cid>:<port>.
+	string address = 5;
+	uint32 version = 6;
 }
 
 message ControllerPlatformRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
+	string sandbox_id = 1;
+	string sandboxer = 10;
 }
 
 message ControllerPlatformResponse {
@@ -133,16 +139,16 @@ message ControllerPlatformResponse {
 }
 
 message ControllerStopRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
-	uint32 timeout_secs = 3;
+	string sandbox_id = 1;
+	uint32 timeout_secs = 2;
+	string sandboxer = 10;
 }
 
 message ControllerStopResponse {}
 
 message ControllerWaitRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
+	string sandbox_id = 1;
+	string sandboxer = 10;
 }
 
 message ControllerWaitResponse {
@@ -151,61 +157,48 @@ message ControllerWaitResponse {
 }
 
 message ControllerStatusRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
-	bool verbose = 3;
+	string sandbox_id = 1;
+	bool verbose = 2;
+	string sandboxer = 10;
 }
 
 message ControllerStatusResponse {
 	string sandbox_id = 1;
 	uint32 pid = 2;
 	string state = 3;
-	string task_address = 4;
-	map<string, string> info = 5;
-	google.protobuf.Timestamp created_at = 6;
-	google.protobuf.Timestamp exited_at = 7;
-	google.protobuf.Any extra = 8;
+	map<string, string> info = 4;
+	google.protobuf.Timestamp created_at = 5;
+	google.protobuf.Timestamp exited_at = 6;
+	google.protobuf.Any extra = 7;
+	// Address of the sandbox for containerd to connect,
+	// for calling Task or other APIs serving in the sandbox.
+	// it is in the form of ttrpc+unix://path/to/uds or grpc+vsock://<vsock cid>:<port>.
+	string address = 8;
+	uint32 version = 9;
 }
 
 message ControllerShutdownRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
+	string sandbox_id = 1;
+	string sandboxer = 10;
 }
 
 message ControllerShutdownResponse {}
 
-message PrepareRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
-	string container_id = 3;
-	string exec_id = 4;
-	google.protobuf.Any spec = 5;
-	repeated containerd.types.Mount rootfs = 6;
-	string stdin = 7;
-	string stdout = 8;
-	string stderr = 9;
-	bool terminal = 10;
-}
-
-message PrepareResponse {
-	string bundle = 1;
+message ControllerMetricsRequest {
+	string sandbox_id = 1;
+	string sandboxer = 10;
 }
 
-message PurgeRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
-	string container_id = 3;
-	string exec_id = 4;
+message ControllerMetricsResponse {
+	types.Metric metrics = 1;
 }
 
-message PurgeResponse {}
-
-message UpdateResourcesRequest {
-	string sandboxer = 1;
-	string sandbox_id = 2;
-	string container_id = 3;
-	google.protobuf.Any resources = 4;
-	map<string, string> annotations = 5;
+message ControllerUpdateRequest {
+	string sandbox_id = 1;
+	string sandboxer = 2;
+	containerd.types.Sandbox sandbox = 3;
+	repeated string fields = 4;
 }
 
-message UpdateResourcesResponse {}
+message ControllerUpdateResponse {
+}
\ No newline at end of file
diff --git a/src/api/services/sandbox/sandbox/types/metrics.proto b/src/api/services/sandbox/sandbox/types/metrics.proto
new file mode 100644
index 00000000..61185939
--- /dev/null
+++ b/src/api/services/sandbox/sandbox/types/metrics.proto
@@ -0,0 +1,30 @@
+/*
+	Copyright The containerd Authors.
+
+	Licensed under the Apache License, Version 2.0 (the "License");
+	you may not use this file except in compliance with the License.
+	You may obtain a copy of the License at
+
+		http://www.apache.org/licenses/LICENSE-2.0
+
+	Unless required by applicable law or agreed to in writing, software
+	distributed under the License is distributed on an "AS IS" BASIS,
+	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+	See the License for the specific language governing permissions and
+	limitations under the License.
+*/
+
+syntax = "proto3";
+
+package containerd.types;
+
+import "google/protobuf/any.proto";
+import "google/protobuf/timestamp.proto";
+
+option go_package = "github.com/containerd/containerd/api/types;types";
+
+message Metric {
+	google.protobuf.Timestamp timestamp = 1;
+	string id = 2;
+	google.protobuf.Any data = 3;
+}
\ No newline at end of file
diff --git a/src/api/services/sandbox/sandbox/types/platform.proto b/src/api/services/sandbox/sandbox/types/platform.proto
index b6088251..102e6e2b 100644
--- a/src/api/services/sandbox/sandbox/types/platform.proto
+++ b/src/api/services/sandbox/sandbox/types/platform.proto
@@ -26,4 +26,5 @@ message Platform {
 	string os = 1;
 	string architecture = 2;
 	string variant = 3;
-}
+	string os_version = 4;
+}
\ No newline at end of file
diff --git a/src/api/services/sandbox/sandbox/types/sandbox.proto b/src/api/services/sandbox/sandbox/types/sandbox.proto
index 7b9d196b..6fe08d40 100644
--- a/src/api/services/sandbox/sandbox/types/sandbox.proto
+++ b/src/api/services/sandbox/sandbox/types/sandbox.proto
@@ -40,14 +40,15 @@ message Sandbox {
 	// Spec is sandbox configuration (kin of OCI runtime spec), spec's data will be written to a config.json file in the
 	// bundle directory (similary to OCI spec).
 	google.protobuf.Any spec = 3;
-	// Sandboxer is the name of the sandbox controller who manages the sandbox.
-	string sandboxer = 4;
 	// Labels provides an area to include arbitrary data on containers.
-	map<string, string> labels = 5;
+	map<string, string> labels = 4;
 	// CreatedAt is the time the container was first created.
-	google.protobuf.Timestamp created_at = 6;
+	google.protobuf.Timestamp created_at = 5;
 	// UpdatedAt is the last time the container was mutated.
-	google.protobuf.Timestamp updated_at = 7;
+	google.protobuf.Timestamp updated_at = 6;
 	// Extensions allow clients to provide optional blobs that can be handled by runtime.
-	map<string, google.protobuf.Any> extensions = 8;
-}
+	map<string, google.protobuf.Any> extensions = 7;
+	// Sandboxer is the name of the sandbox controller who manages the sandbox.
+	string sandboxer = 10;
+
+}
\ No newline at end of file
diff --git a/src/daemon/sandbox/controller/controller.h b/src/daemon/sandbox/controller/controller.h
index 9ad45855..60d2dee5 100644
--- a/src/daemon/sandbox/controller/controller.h
+++ b/src/daemon/sandbox/controller/controller.h
@@ -24,6 +24,7 @@
 
 #include "errors.h"
 #include "api_v1.pb.h"
+#include "sandbox.pb.h"
 
 namespace sandbox {
 
@@ -65,6 +66,7 @@ struct ControllerSandboxInfo {
     uint32_t pid;
     uint64_t createdAt;
     std::string taskAddress;
+    std::string version;
     google::protobuf::Map<std::string, std::string> labels;
 };
 
@@ -78,6 +80,7 @@ struct ControllerSandboxStatus {
     uint32_t pid;
     std::string state;
     std::string taskAddress;
+    std::string version;
     google::protobuf::Map<std::string, std::string> info;
     uint64_t createdAt;
     uint64_t exitedAt;
@@ -123,11 +126,10 @@ public:
                         Errors &error) = 0;
     virtual std::unique_ptr<ControllerSandboxInfo> Start(const std::string &sandboxId, Errors &error) = 0 ;
     virtual std::unique_ptr<ControllerPlatformInfo> Platform(const std::string &sandboxId, Errors &error) = 0;
-    virtual std::string Prepare(const std::string &sandboxId,
-                                const ControllerPrepareParams &params,
-                                Errors &error) = 0;
-    virtual bool Purge(const std::string &sandboxId, const std::string &containerId,
-                       const std::string &execId, Errors &error) = 0;
+    virtual bool Prepare(containerd::types::Sandbox &apiSandbox,
+                         std::vector<std::string> &fields, Errors &error) = 0;
+    virtual bool Purge(containerd::types::Sandbox &apiSandbox,
+                       std::vector<std::string> &fields, Errors &error) = 0;
     virtual bool UpdateResources(const std::string &sandboxId,
                                  const ControllerUpdateResourcesParams &params,
                                  Errors &error) = 0;
diff --git a/src/daemon/sandbox/controller/sandboxer/client/grpc_sandboxer_client.cc b/src/daemon/sandbox/controller/sandboxer/client/grpc_sandboxer_client.cc
index 11c2b014..e042ad45 100644
--- a/src/daemon/sandbox/controller/sandboxer/client/grpc_sandboxer_client.cc
+++ b/src/daemon/sandbox/controller/sandboxer/client/grpc_sandboxer_client.cc
@@ -74,6 +74,7 @@ auto SandboxerClient::InitCreateRequest(containerd::services::sandbox::v1::Contr
         }
     }
     request.set_netns_path(params.netNSPath);
+    // The arg sandbox is useless for now
     return true;
 }
 
@@ -117,7 +118,8 @@ void SandboxerClient::StartResponseToSandboxInfo(const containerd::services::san
     sandboxInfo.id = response.sandbox_id();
     sandboxInfo.pid = response.pid();
     sandboxInfo.createdAt = TimestampToNanos(response.created_at());
-    sandboxInfo.taskAddress = response.task_address();
+    sandboxInfo.taskAddress = response.address();
+    sandboxInfo.version = response.version();
     sandboxInfo.labels = response.labels();
 }
 
@@ -144,53 +146,27 @@ auto SandboxerClient::Start(const std::string &sandboxId, ControllerSandboxInfo
     return true;
 }
 
-auto SandboxerClient::InitPrepareRequest(containerd::services::sandbox::v1::PrepareRequest &request,
-                                         const std::string &sandboxId, const ControllerPrepareParams &params) -> bool
+void SandboxerClient::InitUpdateRequest(containerd::services::sandbox::v1::ControllerUpdateRequest &request,
+                                         containerd::types::Sandbox &apiSandbox,
+                                         std::vector<std::string> &fields)
 {
-    if (params.spec == nullptr) {
-        ERROR("Sandboxer controller prepare request failed, spec is null");
-        return false;
-    }
-    request.mutable_spec()->set_value(*(params.spec));
-    request.set_sandboxer(m_sandboxer);
-    request.set_sandbox_id(sandboxId);
-    request.set_container_id(params.containerId);
-    request.set_exec_id(params.execId);
-    for (const auto &entry : params.rootfs) {
-        if (entry != nullptr) {
-            Mount* mount = request.add_rootfs();
-            InitMountInfo(*mount, *entry);
-        }
-    }
-    if (params.streamInfo != nullptr) {
-        request.set_stdin(params.streamInfo->stdin);
-        request.set_stdout(params.streamInfo->stdout);
-        request.set_stderr(params.streamInfo->stderr);
-        request.set_terminal(params.streamInfo->terminal);
-    } else {
-        request.set_stdin("");
-        request.set_stdout("");
-        request.set_stderr("");
-        request.set_terminal(false);
-    }
-
-    return true;
+    request.set_sandbox_id(apiSandbox.sandbox_id());
+    request.set_sandboxer(apiSandbox.sandboxer());
+    *(request.mutable_sandbox()) = apiSandbox;
+    *(request.mutable_fields()) = {fields.begin(), fields.end()};
 }
 
-auto SandboxerClient::Prepare(const std::string &sandboxId, const ControllerPrepareParams &params, std::string &bundle,
-                              Errors &error) -> bool
+auto SandboxerClient::Prepare(containerd::types::Sandbox &apiSandbox,
+                              std::vector<std::string> &fields, Errors &error) -> bool
 {
     grpc::ClientContext context;
-    containerd::services::sandbox::v1::PrepareRequest request;
-    containerd::services::sandbox::v1::PrepareResponse response;
+    containerd::services::sandbox::v1::ControllerUpdateRequest request;
+    containerd::services::sandbox::v1::ControllerUpdateResponse response;
     grpc::Status status;
 
-    if (!InitPrepareRequest(request, sandboxId, params)) {
-        error.SetError("Failed to init prepare request for sandboxer prepare request, sandbox id: " + sandboxId);
-        return false;
-    }
+    InitUpdateRequest(request, apiSandbox, fields);
 
-    status = m_stub->Prepare(&context, request, &response);
+    status = m_stub->Update(&context, request, &response);
     if (!status.ok()) {
         error.SetError(status.error_message());
         ERROR("Sandboxer controller prepare request failed, error_code: %d: %s", status.error_code(),
@@ -198,25 +174,20 @@ auto SandboxerClient::Prepare(const std::string &sandboxId, const ControllerPrep
         return false;
     }
 
-    bundle = response.bundle();
-
     return true;
 }
 
-auto SandboxerClient::Purge(const std::string &sandboxId, const std::string &containerId,
-                            const std::string &execId, Errors &error) -> bool
+auto SandboxerClient::Purge(containerd::types::Sandbox &apiSandbox,
+                              std::vector<std::string> &fields, Errors &error) -> bool
 {
     grpc::ClientContext context;
-    containerd::services::sandbox::v1::PurgeRequest request;
-    containerd::services::sandbox::v1::PurgeResponse response;
+    containerd::services::sandbox::v1::ControllerUpdateRequest request;
+    containerd::services::sandbox::v1::ControllerUpdateResponse response;
     grpc::Status status;
 
-    request.set_sandboxer(m_sandboxer);
-    request.set_sandbox_id(sandboxId);
-    request.set_container_id(containerId);
-    request.set_exec_id(execId);
+    InitUpdateRequest(request, apiSandbox, fields);
 
-    status = m_stub->Purge(&context, request, &response);
+    status = m_stub->Update(&context, request, &response);
     if (!status.ok()) {
         error.SetError(status.error_message());
         ERROR("Sandboxer controller purge request failed, error_code: %d: %s", status.error_code(),
@@ -227,44 +198,9 @@ auto SandboxerClient::Purge(const std::string &sandboxId, const std::string &con
     return true;
 }
 
-auto SandboxerClient::InitUpdateResourcesRequest(containerd::services::sandbox::v1::UpdateResourcesRequest &request,
-                                                 const std::string &sandboxId,
-                                                 const ControllerUpdateResourcesParams &params) -> bool
-{
-    if (params.resources == nullptr) {
-        ERROR("Sandboxer controller update resources request failed, resources is null");
-        return false;
-    }
-    request.mutable_resources()->set_value(*(params.resources));
-    request.set_sandboxer(m_sandboxer);
-    request.set_sandbox_id(sandboxId);
-    request.set_container_id(params.containerId);
-    request.mutable_annotations()->insert(params.annotations.begin(), params.annotations.end());
-    return true;
-}
-
 auto SandboxerClient::UpdateResources(const std::string &sandboxId, const ControllerUpdateResourcesParams &params,
                                       Errors &error) -> bool
 {
-    grpc::ClientContext context;
-    containerd::services::sandbox::v1::UpdateResourcesRequest request;
-    containerd::services::sandbox::v1::UpdateResourcesResponse response;
-    grpc::Status status;
-
-    if (!InitUpdateResourcesRequest(request, sandboxId, params)) {
-        error.SetError("Failed to init update-resources request for sandboxer update-resources request, sandbox id: " +
-                       sandboxId);
-        return false;
-    }
-
-    status = m_stub->UpdateResources(&context, request, &response);
-    if (!status.ok()) {
-        error.SetError(status.error_message());
-        ERROR("Sandboxer controller update resources request failed, error_code: %d: %s", status.error_code(),
-              status.error_message().c_str());
-        return false;
-    }
-
     return true;
 }
 
@@ -331,7 +267,8 @@ void SandboxerClient::StatusResponseToSandboxStatus(const containerd::services::
     sandboxStatus.id = response.sandbox_id();
     sandboxStatus.pid = response.pid();
     sandboxStatus.state = response.state();
-    sandboxStatus.taskAddress = response.task_address();
+    sandboxStatus.taskAddress = response.address();
+    sandboxStatus.version = response.version();
     sandboxStatus.info.insert(response.info().begin(), response.info().end());
     sandboxStatus.createdAt = TimestampToNanos(response.created_at());
     sandboxStatus.exitedAt = TimestampToNanos(response.exited_at());
diff --git a/src/daemon/sandbox/controller/sandboxer/client/grpc_sandboxer_client.h b/src/daemon/sandbox/controller/sandboxer/client/grpc_sandboxer_client.h
index accca16b..eeb5d7f2 100644
--- a/src/daemon/sandbox/controller/sandboxer/client/grpc_sandboxer_client.h
+++ b/src/daemon/sandbox/controller/sandboxer/client/grpc_sandboxer_client.h
@@ -48,11 +48,11 @@ public:
 
     auto Platform(const std::string &sandboxId, ControllerPlatformInfo &platformInfo, Errors &error) -> bool;
 
-    auto Prepare(const std::string &sandboxId, const ControllerPrepareParams &params, std::string &bundle,
-                 Errors &error) -> bool;
+    auto Prepare(containerd::types::Sandbox &apiSandbox,
+                 std::vector<std::string> &fields, Errors &error) -> bool;
 
-    auto Purge(const std::string &sandboxId, const std::string &containerId,
-               const std::string &execId, Errors &error) -> bool;
+    auto Purge(containerd::types::Sandbox &apiSandbox,
+               std::vector<std::string> &fields, Errors &error) -> bool;
 
     auto UpdateResources(const std::string &sandboxId, const ControllerUpdateResourcesParams &params,
                          Errors &error) -> bool;
@@ -72,11 +72,11 @@ private:
                            const ControllerCreateParams &params) -> bool;
     void StartResponseToSandboxInfo(const containerd::services::sandbox::v1::ControllerStartResponse &response,
                                     ControllerSandboxInfo &sandboxInfo);
-    auto InitPrepareRequest(containerd::services::sandbox::v1::PrepareRequest &request,
-                            const std::string &sandboxId, const ControllerPrepareParams &params) -> bool;
-    auto InitUpdateResourcesRequest(containerd::services::sandbox::v1::UpdateResourcesRequest &request,
-                                    const std::string &sandboxId,
-                                    const ControllerUpdateResourcesParams &params) -> bool;
+    void InitUpdateRequest(containerd::services::sandbox::v1::ControllerUpdateRequest &request,
+                            containerd::types::Sandbox &apiSandbox, std::vector<std::string> &fields);
+    // auto InitUpdateResourcesRequest(containerd::services::sandbox::v1::UpdateResourcesRequest &request,
+    //                                 const std::string &sandboxId,
+    //                                 const ControllerUpdateResourcesParams &params) -> bool;
     void PlatformResponseToPlatformInfo(const containerd::services::sandbox::v1::ControllerPlatformResponse &response,
                                         ControllerPlatformInfo &platformInfo);
     void StatusResponseToSandboxStatus(const containerd::services::sandbox::v1::ControllerStatusResponse &response,
diff --git a/src/daemon/sandbox/controller/sandboxer/sandboxer_controller.cc b/src/daemon/sandbox/controller/sandboxer/sandboxer_controller.cc
index d35f1118..70cab015 100644
--- a/src/daemon/sandbox/controller/sandboxer/sandboxer_controller.cc
+++ b/src/daemon/sandbox/controller/sandboxer/sandboxer_controller.cc
@@ -61,21 +61,18 @@ std::unique_ptr<ControllerPlatformInfo> SandboxerController::Platform(const std:
     return platformInfo;
 }
 
-std::string SandboxerController::Prepare(const std::string &sandboxId,
-                                         const ControllerPrepareParams &params,
-                                         Errors &error)
+bool SandboxerController::Prepare(containerd::types::Sandbox &apiSandbox,
+                                  std::vector<std::string> &fields,
+                                  Errors &error)
 {
-    std::string bundle;
-    if (!m_client->Prepare(sandboxId, params, bundle, error)) {
-        return "";
-    }
-    return bundle;
+    return m_client->Prepare(apiSandbox, fields, error);
 }
 
-bool SandboxerController::Purge(const std::string &sandboxId, const std::string &containerId,
-                                const std::string &execId, Errors &error)
+bool SandboxerController::Purge(containerd::types::Sandbox &apiSandbox,
+                                std::vector<std::string> &fields,
+                                Errors &error)
 {
-    return m_client->Purge(sandboxId, containerId, execId, error);
+    return m_client->Purge(apiSandbox, fields, error);
 }
 
 bool SandboxerController::UpdateResources(const std::string &sandboxId,
diff --git a/src/daemon/sandbox/controller/sandboxer/sandboxer_controller.h b/src/daemon/sandbox/controller/sandboxer/sandboxer_controller.h
index ff66d3d8..8cb7fe7c 100644
--- a/src/daemon/sandbox/controller/sandboxer/sandboxer_controller.h
+++ b/src/daemon/sandbox/controller/sandboxer/sandboxer_controller.h
@@ -37,11 +37,10 @@ public:
                 Errors &error) override;
     std::unique_ptr<ControllerSandboxInfo> Start(const std::string &sandboxId, Errors &error) override;
     std::unique_ptr<ControllerPlatformInfo> Platform(const std::string &sandboxId, Errors &error) override;
-    std::string Prepare(const std::string &sandboxId,
-                        const ControllerPrepareParams &params,
-                        Errors &error) override;
-    bool Purge(const std::string &sandboxId, const std::string &containerId,
-               const std::string &execId, Errors &error) override;
+    bool Prepare(containerd::types::Sandbox &apiSandbox, std::vector<std::string> &fields,
+                 Errors &error) override;
+    bool Purge(containerd::types::Sandbox &apiSandbox, std::vector<std::string> &fields,
+               Errors &error) override;
     bool UpdateResources(const std::string &sandboxId,
                          const ControllerUpdateResourcesParams &params,
                          Errors &error) override;
diff --git a/src/daemon/sandbox/controller/shim/shim_controller.cc b/src/daemon/sandbox/controller/shim/shim_controller.cc
index ce09c076..14c99168 100644
--- a/src/daemon/sandbox/controller/shim/shim_controller.cc
+++ b/src/daemon/sandbox/controller/shim/shim_controller.cc
@@ -340,15 +340,14 @@ std::unique_ptr<ControllerPlatformInfo> ShimController::Platform(const std::stri
     return nullptr;
 }
 
-std::string ShimController::Prepare(const std::string &sandboxId,
-                                    const ControllerPrepareParams &params,
-                                    Errors &error)
+bool ShimController::Prepare(containerd::types::Sandbox &apiSandbox,
+                             std::vector<std::string> &fields, Errors &error)
 {
-    return std::string("");
+    return true;
 }
 
-bool ShimController::Purge(const std::string &sandboxId, const std::string &containerId,
-                           const std::string &execId, Errors &error)
+bool ShimController::Purge(containerd::types::Sandbox &apiSandbox,
+                           std::vector<std::string> &fields, Errors &error)
 {
     return true;
 }
diff --git a/src/daemon/sandbox/controller/shim/shim_controller.h b/src/daemon/sandbox/controller/shim/shim_controller.h
index 5d097bac..1985ddc0 100644
--- a/src/daemon/sandbox/controller/shim/shim_controller.h
+++ b/src/daemon/sandbox/controller/shim/shim_controller.h
@@ -45,11 +45,10 @@ public:
                 Errors &error) override;
     std::unique_ptr<ControllerSandboxInfo> Start(const std::string &sandboxId, Errors &error) override;
     std::unique_ptr<ControllerPlatformInfo> Platform(const std::string &sandboxId, Errors &error) override;
-    std::string Prepare(const std::string &sandboxId,
-                        const ControllerPrepareParams &params,
-                        Errors &error) override;
-    bool Purge(const std::string &sandboxId, const std::string &containerId,
-               const std::string &execId, Errors &error) override;
+    bool Prepare(containerd::types::Sandbox &apiSandbox, std::vector<std::string> &fields,
+                 Errors &error) override;
+    bool Purge(containerd::types::Sandbox &apiSandbox, std::vector<std::string> &fields,
+               Errors &error) override;
     bool UpdateResources(const std::string &sandboxId,
                          const ControllerUpdateResourcesParams &params,
                          Errors &error) override;
diff --git a/src/daemon/sandbox/sandbox.cc b/src/daemon/sandbox/sandbox.cc
index dec082bc..97b77f22 100644
--- a/src/daemon/sandbox/sandbox.cc
+++ b/src/daemon/sandbox/sandbox.cc
@@ -494,6 +494,7 @@ auto Sandbox::Load(Errors &error) -> bool
     }
 
     LoadNetworkSetting();
+    LoadSandboxTasks();
 
     // When the sandbox status acquisition fails or wait fails, the sandbox status is set to not ready,
     // and the user decides whether to delete the sandbox.
@@ -698,6 +699,7 @@ auto Sandbox::Start(Errors &error) -> bool
     m_state.pid = info->pid;
     m_state.createdAt = info->createdAt;
     m_taskAddress = info->taskAddress;
+    m_version = info->version;
     m_state.status = SANDBOX_STATUS_RUNNING;
 
     if (!SaveState(error)) {
@@ -814,7 +816,7 @@ void Sandbox::Status(runtime::v1::PodSandboxStatus &status)
 
 auto Sandbox::GenerateSandboxStateJson(sandbox_state *state) -> std::string
 {
-    __isula_auto_free parser_error error;
+    __isula_auto_free parser_error error = NULL;
     std::string ret;
     __isula_auto_free char *state_json = NULL;
     state_json = sandbox_state_generate_json(state, NULL, &(error));
@@ -874,7 +876,7 @@ auto Sandbox::SaveNetworkSetting(Errors &error) -> bool
 
 auto Sandbox::GenerateSandboxMetadataJson(sandbox_metadata *metadata) -> std::string
 {
-    __isula_auto_free parser_error error;
+    __isula_auto_free parser_error error = NULL;
     std::string ret;
     __isula_auto_free char *metadata_json = NULL;
     metadata_json = sandbox_metadata_generate_json(metadata, NULL, &(error));
@@ -1096,6 +1098,11 @@ auto Sandbox::GetNetworkSettingsPath() -> std::string
     return m_rootdir + std::string("/") + NETWORK_SETTINGS_JSON;
 }
 
+auto Sandbox::GetTasksJsonPath() -> std::string
+{
+    return m_rootdir + std::string("/") + SANDBOX_TASKS_JSON;
+}
+
 void Sandbox::FillSandboxMetadata(sandbox_metadata* metadata, Errors &error)
 {
     std::string jsonStr;
@@ -1116,4 +1123,214 @@ void Sandbox::FillSandboxMetadata(sandbox_metadata* metadata, Errors &error)
 
     metadata->sandbox_config_json = util_strdup_s(jsonStr.c_str());
 }
+
+auto Sandbox::AddTaskById(const char *task_id, sandbox_task *task) -> bool
+{
+    
+    std::string taskId = std::string(task_id);
+    auto iter = m_tasks.find(taskId);
+
+    if (iter != m_tasks.end()) {
+        ERROR("Failed to add exits sandbox task %s for sandbox: %s",
+            task_id, m_id.c_str());
+        return false;
+    }
+    m_tasks[taskId] = std::make_shared<SandboxTask>(task);
+    return true;
+}
+
+auto Sandbox::ReadSandboxTasksJson() -> sandbox_tasks *
+{
+    const std::string path = GetTasksJsonPath();
+    __isula_auto_free parser_error err = nullptr;
+    sandbox_tasks *tasksArray = nullptr;
+
+    ReadGuard<RWMutex> lock(m_tasksMutex);
+    tasksArray = sandbox_tasks_parse_file(path.c_str(), nullptr, &err);
+    if (tasksArray == nullptr) {
+        WARN("Failed to read %s tasks json: %s", path.c_str(), err);
+    }
+    return tasksArray;
+}
+
+auto Sandbox::WriteSandboxTasksJson(std::string &tasks_json) -> bool
+{
+    int nret = 0;
+    const std::string path = GetTasksJsonPath();
+
+    WriteGuard<RWMutex> lock(m_tasksMutex);
+    nret = util_atomic_write_file(path.c_str(), tasks_json.c_str(), tasks_json.size(), CONFIG_FILE_MODE, false);
+    if (nret != 0) {
+        SYSERROR("Failed to write file %s", path.c_str());
+    }
+    return nret == 0;
+}
+
+auto Sandbox::DeleteSandboxTasksJson() -> bool
+{
+    int get_err = 0;
+    const std::string path = GetTasksJsonPath();
+
+    WriteGuard<RWMutex> lock(m_tasksMutex);
+    if (util_fileself_exists(path.c_str()) &&
+        !util_force_remove_file(path.c_str(), &get_err)) {
+        errno = get_err;
+        SYSERROR("Failed to remove file %s", path.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+void Sandbox::AddSandboxTasksByArray(sandbox_tasks *tasksArray)
+{
+    size_t i;
+
+    WriteGuard<RWMutex> lock(m_tasksMutex);
+    for (i = 0; i < tasksArray->tasks_len; i++) {
+        if (!AddTaskById(tasksArray->tasks[i]->task_id, tasksArray->tasks[i])) {
+            return;
+        }
+        tasksArray->tasks[i] = nullptr;
+    }
+    tasksArray->tasks_len = 0;
+}
+
+void Sandbox::LoadSandboxTasks()
+{
+    sandbox_tasks *tasksArray = nullptr;
+
+    tasksArray = ReadSandboxTasksJson();
+    if (tasksArray == nullptr) {
+        return;
+    }
+
+    AddSandboxTasksByArray(tasksArray);
+
+    free_sandbox_tasks(tasksArray);
+}
+
+auto Sandbox::SaveSandboxTasks() -> bool
+{
+    std::string tasks_json;
+
+    if (m_tasks.empty()) {
+        return DeleteSandboxTasksJson();
+    }
+
+    tasks_json = GetAnySandboxTasks();
+    if (tasks_json.empty()) {
+        ERROR("Failed to get sandbox tasks json for sandbox: '%s'", m_id.c_str());
+        return false;
+    }
+
+    return WriteSandboxTasksJson(tasks_json);
+}
+
+auto Sandbox::AddSandboxTasks(sandbox_task *task) -> bool
+{
+    if (task == nullptr) {
+        return true;
+    }
+    if (task->task_id == nullptr) {
+        return false;
+    }
+
+    WriteGuard<RWMutex> lock(m_tasksMutex);
+
+    return AddTaskById(task->task_id, task);
+}
+
+auto Sandbox::GetAnySandboxTasks() -> std::string
+{
+    __isula_auto_free parser_error err = nullptr;
+    sandbox_tasks tasksArray = { 0 };
+    size_t i = 0;
+    __isula_auto_free char *tasks_json = nullptr;
+
+    tasksArray.tasks = (sandbox_task **)util_smart_calloc_s(sizeof(sandbox_task *), m_tasks.size());
+    if (tasksArray.tasks == nullptr) {
+        SYSERROR("Out of memory.");
+        return std::string("");
+    }
+
+    ReadGuard<RWMutex> lock(m_tasksMutex);
+    for (auto const& [_, val] : m_tasks) {
+        /* 
+         * We ignore that the processes are modified 
+         * when we generate tasks json string. 
+         * Because no matter whether a process is deleted or added, 
+         * the Update of sandbox api will be called eventually.
+         * 
+         * And we ignore that the task is freed after we do GetTask().
+         * Because the only way to free task is DeleteSandboxTasks()
+         * which needs write lock of m_tasksMutex.
+        */ 
+        tasksArray.tasks[i] = val->GetTask();
+        i++;
+    }
+    tasksArray.tasks_len = m_tasks.size();
+
+    tasks_json = sandbox_tasks_generate_json(&tasksArray, nullptr, &(err));
+    if (tasks_json == nullptr || strlen(tasks_json) == 0) {
+        ERROR("Failed to get sandbox tasks json for sandbox: '%s'", m_id.c_str());
+        free(tasksArray.tasks);
+        return std::string("");
+    }
+
+    free(tasksArray.tasks);
+    return std::string(tasks_json);
+}
+
+void Sandbox::DeleteSandboxTasks(const char *containerId)
+{
+    if (containerId == nullptr) {
+        return;
+    }
+
+    std::string taskId = std::string(containerId);
+
+    WriteGuard<RWMutex> lock(m_tasksMutex);
+    auto iter = m_tasks.find(taskId);
+    if (iter == m_tasks.end()) {
+        return;
+    }
+    m_tasks.erase(iter);
+}
+
+auto Sandbox::AddSandboxTasksProcess(const char *containerId, sandbox_process *processes) -> bool
+{
+    if (containerId == nullptr || processes == nullptr) {
+        ERROR("Empty args.");
+        return false;
+    }
+
+    std::string taskId = std::string(containerId);
+
+    ReadGuard<RWMutex> lock(m_tasksMutex);
+    auto iter = m_tasks.find(taskId);
+    if (iter == m_tasks.end()) {
+        SYSERROR("Failed to find container %s", containerId);
+        return false;
+    }
+    
+    return iter->second->AddSandboxTasksProcess(processes);
+}
+
+void Sandbox::DeleteSandboxTasksProcess(const char *containerId, const char *execId)
+{
+    if (containerId == nullptr || execId == nullptr) {
+        return;
+    }
+
+    std::string taskId = std::string(containerId);
+
+    ReadGuard<RWMutex> lock(m_tasksMutex);
+    auto iter = m_tasks.find(taskId);
+    if (iter == m_tasks.end()) {
+        return;
+    }
+    iter->second->DeleteSandboxTasksProcess(execId);
+}
+
 }
\ No newline at end of file
diff --git a/src/daemon/sandbox/sandbox.h b/src/daemon/sandbox/sandbox.h
index 42fbee2a..437b6113 100644
--- a/src/daemon/sandbox/sandbox.h
+++ b/src/daemon/sandbox/sandbox.h
@@ -30,12 +30,14 @@
 #include "controller_manager.h"
 #include "cstruct_wrapper.h"
 #include "read_write_lock.h"
+#include "sandbox_task.h"
 
 namespace sandbox {
 
 const std::string SANDBOX_METADATA_JSON = "sandbox_metadata.json";
 const std::string SANDBOX_STATE_JSON = "sandbox_state.json";
 const std::string NETWORK_SETTINGS_JSON = "network_settings.json";
+const std::string SANDBOX_TASKS_JSON = "sandbox_tasks.json";
 
 // Keep consistent with the default values set in containerd and cri-o.
 const uint32_t DEFAULT_STOP_TIMEOUT = 10;
@@ -138,6 +140,15 @@ public:
     auto Remove(Errors &error) -> bool;
     void Status(runtime::v1::PodSandboxStatus &status);
 
+    // for sandbox api update
+    void LoadSandboxTasks();
+    auto SaveSandboxTasks() -> bool;
+    auto AddSandboxTasks(sandbox_task *task) -> bool;
+    auto GetAnySandboxTasks() -> std::string;
+    void DeleteSandboxTasks(const char *containerId);
+    auto AddSandboxTasksProcess(const char *containerId, sandbox_process *processes) -> bool;
+    void DeleteSandboxTasksProcess(const char *containerId, const char *execId);
+
 private:
     auto SaveState(Errors &error) -> bool;
     auto SaveMetadata(Errors &error) -> bool;
@@ -161,6 +172,7 @@ private:
     auto GetMetadataJsonPath() ->  std::string;
     auto GetStatePath() -> std::string;
     auto GetNetworkSettingsPath() -> std::string;
+    auto GetTasksJsonPath() -> std::string;
 
     void FillSandboxState(sandbox_state *state);
     void FillSandboxMetadata(sandbox_metadata* metadata, Errors &error);
@@ -177,6 +189,12 @@ private:
 
     void updateSelinuxLabels(std::string &selinuxLabels);
 
+    auto AddTaskById(const char *task_id, sandbox_task *task) -> bool;
+    auto ReadSandboxTasksJson() -> sandbox_tasks *;
+    auto WriteSandboxTasksJson(std::string &tasks_json) -> bool;
+    auto DeleteSandboxTasksJson() -> bool;
+    void AddSandboxTasksByArray(sandbox_tasks *tasksArray);
+
 private:
     // Since the cri module will operate concurrently on the sandbox instance,
     // use m_mutex to ensure the correctness of the sandbox instance
@@ -191,6 +209,7 @@ private:
     std::string m_rootdir;
     std::string m_statedir;
     std::string m_taskAddress;
+    uint32_t m_version;
     StatsInfo m_statsInfo;
     // Store network information in the sandbox, which is convenient for the cri module to obtain
     // and update the network settings of the pause container in the shim-controller.
@@ -211,6 +230,11 @@ private:
     // vsock ports
     std::mutex m_vsockPortsMutex;
     std::set<uint32_t> m_vsockPorts;
+
+    // use m_tasksMutex to ensure the correctness of the tasks
+    RWMutex m_tasksMutex;
+    // for sandbox api update, containerId --> tasks
+    std::map<std::string, std::shared_ptr<SandboxTask>> m_tasks;
 };
 
 } // namespace sandbox
diff --git a/src/daemon/sandbox/sandbox_ops.cc b/src/daemon/sandbox/sandbox_ops.cc
index 22cfea95..96e541a4 100644
--- a/src/daemon/sandbox/sandbox_ops.cc
+++ b/src/daemon/sandbox/sandbox_ops.cc
@@ -16,11 +16,18 @@
 
 #include <isula_libutils/auto_cleanup.h>
 #include <isula_libutils/log.h>
+#include <google/protobuf/util/time_util.h>
 
 #include "controller_manager.h"
 #include "sandbox_manager.h"
+#include "sandbox.h"
 #include "namespace.h"
 #include "utils.h"
+#include "utils_timestamp.h"
+
+const std::string SANDBOX_EXTENSIONS_TASKS = "extensions.tasks";
+const std::string SANDBOX_TASKS_KEY = "tasks";
+const std::string SANDBOX_TASKS_TYPEURL = "github.com/containerd/containerd/Tasks";
 
 static inline bool validate_sandbox_info(const container_sandbox_info *sandbox)
 {
@@ -28,106 +35,166 @@ static inline bool validate_sandbox_info(const container_sandbox_info *sandbox)
             sandbox->id != NULL);
 }
 
-static int generate_ctrl_rootfs(sandbox::ControllerPrepareParams &params,
+static int generate_ctrl_rootfs(sandbox_task *task,
                                 const container_config_v2_common_config *config)
 {
+    size_t len = 1;
     if (nullptr == config->base_fs) {
         ERROR("Container %s has no base fs", config->id);
         return -1;
     }
 
     // TODO: rootfs's options left to be configured
-    std::unique_ptr<sandbox::ControllerMountInfo> mount_info(new sandbox::ControllerMountInfo());
-    mount_info->type = MOUNT_TYPE_BIND;
-    mount_info->source = config->base_fs;
-    params.rootfs.push_back(std::move(mount_info));
+    task->rootfs = (sandbox_mount **)util_smart_calloc_s(sizeof(sandbox_mount *), len);
+    if (task->rootfs == nullptr) {
+        ERROR("Out of memory.");
+        return -1;
+    }
+    task->rootfs[0] = (sandbox_mount *)util_common_calloc_s(sizeof(sandbox_mount));
+    if (task->rootfs[0] == nullptr) {
+        ERROR("Out of memory.");
+        return -1;
+    }
+    task->rootfs_len = len;
+    task->rootfs[0]->type = util_strdup_s(MOUNT_TYPE_BIND);
+    task->rootfs[0]->source = util_strdup_s(config->base_fs);
 
     return 0;
 }
 
-static int do_sandbox_prepare(const container_config_v2_common_config *config,
-                              const char *exec_id, const char *oci_spec,
-                              const char * console_fifos[], bool tty)
+static int do_sandbox_prepare(std::shared_ptr<sandbox::Sandbox> &sandbox, containerd::types::Sandbox &apiSandbox)
 {
     Errors err;
-    sandbox::ControllerPrepareParams params;
-    std::unique_ptr<sandbox::ControllerStreamInfo> stream_info(new sandbox::ControllerStreamInfo());
-    const container_sandbox_info *sandbox_info = nullptr;
-
-    if (nullptr == config || nullptr == config->id) {
-        ERROR("Invalid parameter: config");
-        return -1;
-    }
+    std::vector<std::string> fields;
+    
+    fields.push_back(SANDBOX_EXTENSIONS_TASKS);
 
-    sandbox_info = config->sandbox_info;
-    if (false == validate_sandbox_info(sandbox_info)) {
-        ERROR("Invalid parameter: sandbox");
+    auto controller = sandbox::ControllerManager::GetInstance()->GetController(sandbox->GetSandboxer());
+    if (nullptr == controller) {
+        ERROR("Invalid sandboxer name: %s", sandbox->GetSandboxer().c_str());
         return -1;
     }
 
-    if (nullptr == console_fifos) {
-        ERROR("Invlaid parameter: console_fifos");
+    if (!controller->Prepare(apiSandbox, fields, err)) {
+        ERROR("Failed to prepare in container controller prepare: %s", err.GetCMessage());
         return -1;
     }
 
-    params.containerId = config->id;
-    params.execId = (nullptr == exec_id) ? "" : exec_id;
-    params.spec = std::unique_ptr<std::string>(new std::string(oci_spec));
-
-    if (generate_ctrl_rootfs(params, config) != 0) {
-        ERROR("Invalid rootfs");
-        return -1;
-    }
+    return 0;
+}
 
-    stream_info->stdin = (nullptr == console_fifos[0]) ? "" : console_fifos[0];
-    stream_info->stdout = (nullptr == console_fifos[1]) ? "" : console_fifos[1];
-    stream_info->stderr = (nullptr == console_fifos[2]) ? "" : console_fifos[2];
-    stream_info->terminal = tty;
-    params.streamInfo = std::move(stream_info);
+static int do_sandbox_purge(std::shared_ptr<sandbox::Sandbox> &sandbox, containerd::types::Sandbox &apiSandbox)
+{
+    Errors err;
+    std::vector<std::string> fields;
+    
+    fields.push_back(SANDBOX_EXTENSIONS_TASKS);
 
-    auto controller = sandbox::ControllerManager::GetInstance()->GetController(sandbox_info->sandboxer);
+    auto controller = sandbox::ControllerManager::GetInstance()->GetController(sandbox->GetSandboxer());
     if (nullptr == controller) {
-        ERROR("Invalid sandboxer name: %s", sandbox_info->sandboxer);
+        ERROR("Invalid sandboxer name: %s", sandbox->GetSandboxer().c_str());
         return -1;
     }
 
-    std::string bundle = controller->Prepare(sandbox_info->id, params, err);
-    if (err.NotEmpty()) {
-        ERROR("Failed to prepare in container controller prepare: %s", err.GetCMessage());
+    if (!controller->Purge(apiSandbox, fields, err)) {
+        ERROR("Failed to purge: %s", err.GetCMessage());
         return -1;
     }
 
     return 0;
 }
 
-static int do_sandbox_purge(const container_config_v2_common_config *config,
-                            const char *exec_id)
+static oci_runtime_spec *clone_oci_runtime_spec(const oci_runtime_spec *oci_spec)
 {
-    Errors err;
-    const container_sandbox_info *sandbox_info = nullptr;
+    __isula_auto_free char *json_str = nullptr;
+    __isula_auto_free parser_error err = nullptr;
+    oci_runtime_spec *ret = nullptr;
+
+    json_str = oci_runtime_spec_generate_json(oci_spec, nullptr, &err);
+    if (json_str == nullptr) {
+        ERROR("Failed to generate spec json: %s", err);
+        return nullptr;
+    }
+    ret = oci_runtime_spec_parse_data(json_str, nullptr, &err);
+    if (ret == nullptr) {
+        ERROR("Failed to generate spec: %s", err);
+    }
+    return ret;
+}
+
+static defs_process *clone_defs_process(defs_process *process_spec)
+{
+    __isula_auto_free char *json_str = nullptr;
+    __isula_auto_free parser_error err = nullptr;
+    defs_process *ret = nullptr;
+
+    json_str = defs_process_generate_json(process_spec, nullptr, &err);
+    if (json_str == nullptr) {
+        ERROR("Failed to generate process spec json: %s", err);
+        return nullptr;
+    }
+    ret = defs_process_parse_data(json_str, nullptr, &err);
+    if (ret == nullptr) {
+        ERROR("Failed to generate process spec: %s", err);
+    }
+    return ret;
+}
 
+static std::shared_ptr<sandbox::Sandbox> get_prepare_sandbox(const container_config_v2_common_config *config)
+{
     if (nullptr == config || nullptr == config->id) {
         ERROR("Invalid parameter: config");
-        return -1;
+        return nullptr;
     }
 
-    sandbox_info = config->sandbox_info;
+    auto sandbox_info = config->sandbox_info;
     if (false == validate_sandbox_info(sandbox_info)) {
         ERROR("Invalid parameter: sandbox");
-        return -1;
+        return nullptr;
     }
 
-    auto controller = sandbox::ControllerManager::GetInstance()->GetController(sandbox_info->sandboxer);
-    if (nullptr == controller) {
-        ERROR("Invalid sandboxer name: %s", sandbox_info->sandboxer);
-        return -1;
+    auto sandbox = sandbox::SandboxManager::GetInstance()->GetSandbox(sandbox_info->id);
+    if (nullptr == sandbox) {
+        ERROR("Sandbox not found");
+        return nullptr;
     }
+    return sandbox;
+}
 
-    if (!controller->Purge(sandbox_info->id, config->id,
-                           (nullptr == exec_id) ? "" : exec_id, err)) {
-        ERROR("Failed to purge: %s", err.GetCMessage());
+static int init_prepare_api_sandbox(std::shared_ptr<sandbox::Sandbox> sandbox, const char *containerId,
+                                     containerd::types::Sandbox &apiSandbox)
+{
+    google::protobuf::Map<std::string, std::string> *labels = apiSandbox.mutable_labels();
+    google::protobuf::Map<std::string, google::protobuf::Any> *extensions = apiSandbox.mutable_extensions();
+    google::protobuf::Any any;
+    auto created_at = new (std::nothrow) google::protobuf::Timestamp;
+    auto updated_at = new (std::nothrow) google::protobuf::Timestamp;
+    
+    apiSandbox.set_sandbox_id(sandbox->GetId());
+    apiSandbox.mutable_runtime()->set_name(sandbox->GetRuntime());
+    // TODO how get options
+    // apiSandbox.mutable_runtime()->set_options(sandbox->GetRuntime());
+    // Just ignore spec
+    (*labels)[std::string("name")] = sandbox->GetName();
+
+    *created_at = google::protobuf::util::TimeUtil::NanosecondsToTimestamp(
+                              sandbox->GetCreatedAt());
+    apiSandbox.set_allocated_created_at(created_at);
+    *updated_at = google::protobuf::util::TimeUtil::NanosecondsToTimestamp(util_get_now_time_nanos());
+    apiSandbox.set_allocated_updated_at(updated_at);
+
+    auto any_type_url = any.mutable_type_url();
+    *any_type_url = SANDBOX_TASKS_TYPEURL;
+    auto any_value = any.mutable_value();
+    *any_value = sandbox->GetAnySandboxTasks();
+    if ((*any_value).empty()) {
+        ERROR("Failed to get any sandbox tasks");
         return -1;
     }
+    DEBUG("Get any sandbox tasks %s", (*any_value).c_str());
+    (*extensions)[SANDBOX_TASKS_KEY] = any;
+
+    apiSandbox.set_sandboxer(sandbox->GetSandboxer());
 
     return 0;
 }
@@ -136,45 +203,177 @@ int sandbox_prepare_container(const container_config_v2_common_config *config,
                               const oci_runtime_spec *oci_spec,
                               const char * console_fifos[], bool tty)
 {
-    __isula_auto_free char *json_oci_spec = nullptr;
-    __isula_auto_free parser_error err = nullptr;
+    sandbox_task *task = nullptr;
+    containerd::types::Sandbox apiSandbox;
+    int ret = -1;
 
     INFO("Prepare container for sandbox");
 
-    json_oci_spec = oci_runtime_spec_generate_json(oci_spec, nullptr, &err);
-    if (nullptr == json_oci_spec) {
-        ERROR("Failed to generate container spec json: %s", err);
+    if (nullptr == console_fifos) {
+        ERROR("Invlaid parameter: console_fifos");
+        return -1;
+    }
+
+    auto sandbox = get_prepare_sandbox(config);
+    if (sandbox == nullptr) {
+        ERROR("Sandbox not found");
+        return -1;
+    }
+
+    task = (sandbox_task *)util_common_calloc_s(sizeof(sandbox_task));
+    if (task == nullptr) {
+        ERROR("Out of memory.");
         return -1;
     }
-    return do_sandbox_prepare(config, nullptr, json_oci_spec, console_fifos, tty);
+    task->task_id = util_strdup_s(config->id);
+    task->spec = clone_oci_runtime_spec(oci_spec);
+    if (task->spec == nullptr) {
+        ERROR("Out of memory.");
+        goto free_out;
+    }
+    if (generate_ctrl_rootfs(task, config) != 0) {
+        ERROR("Invalid rootfs");
+        goto free_out;
+    }
+    task->stdin = util_strdup_s((nullptr == console_fifos[0]) ? "" : console_fifos[0]);
+    task->stdout = util_strdup_s((nullptr == console_fifos[1]) ? "" : console_fifos[1]);
+    task->stderr = util_strdup_s((nullptr == console_fifos[2]) ? "" : console_fifos[2]);
+
+    if (!sandbox->AddSandboxTasks(task)) {
+        ERROR("Failed to add sandbox %s task.", config->id);
+        goto free_out;
+    }
+    task = nullptr;
+    ret = init_prepare_api_sandbox(sandbox, config->id, apiSandbox);
+    if (ret != 0) {
+        ERROR("Failed to init %s api sandbox.", config->id);
+        goto del_out;
+    }
+    ret = do_sandbox_prepare(sandbox, apiSandbox);
+
+del_out:
+    if (ret != 0) {
+        sandbox->DeleteSandboxTasks(config->id);
+    }
+    if (!sandbox->SaveSandboxTasks()) {
+        ERROR("Failed to Save %s sandbox tasks.", config->id);
+        ret = -1;
+    }
+free_out:
+    free_sandbox_task(task);
+    return ret;
 }
 
 int sandbox_prepare_exec(const container_config_v2_common_config *config,
                          const char *exec_id, defs_process *process_spec,
                          const char * console_fifos[], bool tty)
 {
-    __isula_auto_free char *json_process = nullptr;
-    __isula_auto_free parser_error err = nullptr;
+    sandbox_process *process = nullptr;
+    containerd::types::Sandbox apiSandbox;
+    int ret = -1;
 
     INFO("Prepare exec for container in sandbox");
 
-    json_process = defs_process_generate_json(process_spec, nullptr, &err);
-    if (nullptr == json_process) {
-        ERROR("Failed to generate process spec json: %s", err);
+    if (nullptr == console_fifos) {
+        ERROR("Invlaid parameter: console_fifos");
         return -1;
     }
 
-    return do_sandbox_prepare(config, exec_id, json_process, console_fifos, tty);
+    auto sandbox = get_prepare_sandbox(config);
+    if (sandbox == nullptr) {
+        ERROR("Sandbox not found");
+        return -1;
+    }
+
+    process = (sandbox_process *)util_common_calloc_s(sizeof(sandbox_process));
+    if (process == nullptr) {
+        ERROR("Out of memory.");
+        return -1;
+    }
+    process->exec_id = util_strdup_s(exec_id);
+    process->spec = clone_defs_process(process_spec);
+    if (process->spec == nullptr) {
+        ERROR("Out of memory.");
+        goto free_out;
+    }
+    process->stdin = util_strdup_s((nullptr == console_fifos[0]) ? "" : console_fifos[0]);
+    process->stdout = util_strdup_s((nullptr == console_fifos[1]) ? "" : console_fifos[1]);
+    process->stderr = util_strdup_s((nullptr == console_fifos[2]) ? "" : console_fifos[2]);
+
+    if (!sandbox->AddSandboxTasksProcess(config->id, process)) {
+        ERROR("Failed to add sandbox %s process.", config->id);
+        goto free_out;
+    }
+    process = nullptr;
+    ret = init_prepare_api_sandbox(sandbox, config->id, apiSandbox);
+    if (ret != 0) {
+        ERROR("Failed to init %s api sandbox.", config->id);
+        goto del_out;
+    }
+    ret = do_sandbox_prepare(sandbox, apiSandbox);
+
+del_out:
+    if (ret != 0) {
+        sandbox->DeleteSandboxTasksProcess(config->id, exec_id);
+    }
+    if (!sandbox->SaveSandboxTasks()) {
+        ERROR("Failed to Save %s sandbox tasks.", config->id);
+        ret = -1;
+    }
+free_out:
+    free_sandbox_process(process);
+    return ret;
 }
 
 int sandbox_purge_container(const container_config_v2_common_config *config)
 {
-    return do_sandbox_purge(config, nullptr);
+    containerd::types::Sandbox apiSandbox;
+
+    INFO("Purge container for sandbox");
+
+    auto sandbox = get_prepare_sandbox(config);
+    if (sandbox == nullptr) {
+        ERROR("Sandbox not found");
+        return -1;
+    }
+
+    sandbox->DeleteSandboxTasks(config->id);
+    if (!sandbox->SaveSandboxTasks()) {
+        ERROR("Failed to Save %s sandbox tasks.", config->id);
+        return -1;
+    }
+
+    if (init_prepare_api_sandbox(sandbox, config->id, apiSandbox) != 0) {
+        ERROR("Failed to init %s api sandbox.", config->id);
+        return -1;
+    }
+    return do_sandbox_purge(sandbox, apiSandbox);
 }
 
 int sandbox_purge_exec(const container_config_v2_common_config *config, const char *exec_id)
 {
-    return do_sandbox_purge(config, exec_id);
+    containerd::types::Sandbox apiSandbox;
+
+    INFO("Purge exec for container in sandbox");
+
+    auto sandbox = get_prepare_sandbox(config);
+    if (sandbox == nullptr) {
+        ERROR("Sandbox not found");
+        return -1;
+    }
+
+    sandbox->DeleteSandboxTasksProcess(config->id, exec_id);
+    if (!sandbox->SaveSandboxTasks()) {
+        ERROR("Failed to Save %s sandbox tasks.", config->id);
+        return -1;
+    }
+
+    if (init_prepare_api_sandbox(sandbox, config->id, apiSandbox) != 0) {
+        ERROR("Failed to init %s api sandbox.", exec_id);
+        return -1;
+    }
+
+    return do_sandbox_purge(sandbox, apiSandbox);
 }
 
 int sandbox_on_sandbox_exit(const char *sandbox_id, int exit_code)
diff --git a/src/daemon/sandbox/sandbox_task.cc b/src/daemon/sandbox/sandbox_task.cc
new file mode 100644
index 00000000..b1efc340
--- /dev/null
+++ b/src/daemon/sandbox/sandbox_task.cc
@@ -0,0 +1,99 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: liuxu
+ * Create: 2024-10-22
+ * Description: provide sandbox class definition
+ *********************************************************************************/
+#include "sandbox_task.h"
+
+#include <mutex>
+
+#include <isula_libutils/log.h>
+
+#include "utils.h"
+#include "errors.h"
+
+namespace sandbox {
+
+SandboxTask::SandboxTask(sandbox_task *task): m_task(task)
+{
+}
+
+SandboxTask::~SandboxTask()
+{
+    free_sandbox_task(m_task);
+    m_task = nullptr;
+}
+
+auto SandboxTask::GetTask() -> sandbox_task *
+{
+    ReadGuard<RWMutex> lock(m_taskMutex);
+    return m_task;
+}
+
+auto SandboxTask::AddSandboxTasksProcess(sandbox_process *processes) -> bool
+{
+    if (processes == nullptr) {
+        ERROR("Empty args.");
+        return false;
+    }
+
+    WriteGuard<RWMutex> lock(m_taskMutex);
+    if (util_mem_realloc((void **)(&m_task->processes),
+                         (m_task->processes_len + 1) * sizeof(sandbox_process *),
+                         (void *)m_task->processes,
+                         m_task->processes_len * sizeof(sandbox_process *)) != 0) {  
+        ERROR("Out of memory");
+        return false;
+    }
+    m_task->processes[m_task->processes_len] = processes;
+    m_task->processes_len++;
+
+    return true;
+}
+
+auto SandboxTask::FindProcessByID(const char *execId) -> int
+{
+    int i;
+    int processes_len = m_task->processes_len;
+
+    if (m_task->processes == nullptr) {
+        return -1;
+    }
+
+    for (i = 0; i < processes_len; i++) {
+        if (strcmp(m_task->processes[i]->exec_id, execId) == 0) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+void SandboxTask::DeleteSandboxTasksProcess(const char *execId)
+{
+    if (execId == nullptr) {
+        return;
+    }
+
+    int idx;
+
+    WriteGuard<RWMutex> lock(m_taskMutex);
+    idx = FindProcessByID(execId);
+    if (idx < 0) {
+        return;
+    }
+    free_sandbox_process(m_task->processes[idx]);
+    (void)memcpy((void **)&m_task->processes[idx], (void **)&m_task->processes[idx + 1],
+                 (m_task->processes_len - idx - 1) * sizeof(void *));
+    m_task->processes_len--;
+}
+
+}
\ No newline at end of file
diff --git a/src/daemon/sandbox/sandbox_task.h b/src/daemon/sandbox/sandbox_task.h
new file mode 100644
index 00000000..1bd0ce58
--- /dev/null
+++ b/src/daemon/sandbox/sandbox_task.h
@@ -0,0 +1,48 @@
+/******************************************************************************
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
+ * iSulad licensed under the Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *     http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
+ * PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ * Author: liuxu
+ * Create: 2024-10-22
+ * Description: provide sandbox class definition
+ *********************************************************************************/
+
+#ifndef DAEMON_SANDBOX_SANDBOX_TASK_H
+#define DAEMON_SANDBOX_SANDBOX_TASK_H
+
+#include <string>
+#include <mutex>
+
+#include <isula_libutils/sandbox_tasks.h>
+
+#include "api_v1.grpc.pb.h"
+#include "errors.h"
+#include "read_write_lock.h"
+
+namespace sandbox {
+
+class SandboxTask : public std::enable_shared_from_this<SandboxTask> {
+public:
+    SandboxTask(sandbox_task *task);
+    ~SandboxTask();
+
+    auto GetTask() -> sandbox_task *;
+    auto AddSandboxTasksProcess(sandbox_process *processes) -> bool;
+    void DeleteSandboxTasksProcess(const char *execId);
+
+private:
+    auto FindProcessByID(const char *execId) -> int;
+private:
+    // Do not modify m_task concurrently.
+    RWMutex m_taskMutex;
+    sandbox_task *m_task;
+};
+} // namespace sandbox
+
+#endif // DAEMON_SANDBOX_SANDBOX_TASK_H
\ No newline at end of file
-- 
2.34.1

