495
									
								
								api/next.pb.txt
									
									
									
									
									
								
							
							
						
						
									
										495
									
								
								api/next.pb.txt
									
									
									
									
									
								
							| @@ -928,6 +928,13 @@ file { | ||||
|       } | ||||
|       json_name: "extensions" | ||||
|     } | ||||
|     field { | ||||
|       name: "sandbox" | ||||
|       number: 11 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandbox" | ||||
|     } | ||||
|     nested_type { | ||||
|       name: "LabelsEntry" | ||||
|       field { | ||||
| @@ -2929,6 +2936,494 @@ file { | ||||
|   } | ||||
|   syntax: "proto3" | ||||
| } | ||||
| file { | ||||
|   name: "github.com/containerd/containerd/api/types/sandbox.proto" | ||||
|   package: "containerd.types" | ||||
|   dependency: "gogoproto/gogo.proto" | ||||
|   dependency: "google/protobuf/any.proto" | ||||
|   dependency: "google/protobuf/timestamp.proto" | ||||
|   message_type { | ||||
|     name: "Sandbox" | ||||
|     field { | ||||
|       name: "sandbox_id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandboxId" | ||||
|     } | ||||
|     field { | ||||
|       name: "runtime" | ||||
|       number: 2 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox.Runtime" | ||||
|       options { | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "runtime" | ||||
|     } | ||||
|     field { | ||||
|       name: "spec" | ||||
|       number: 3 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".google.protobuf.Any" | ||||
|       json_name: "spec" | ||||
|     } | ||||
|     field { | ||||
|       name: "labels" | ||||
|       number: 4 | ||||
|       label: LABEL_REPEATED | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox.LabelsEntry" | ||||
|       options { | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "labels" | ||||
|     } | ||||
|     field { | ||||
|       name: "created_at" | ||||
|       number: 5 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".google.protobuf.Timestamp" | ||||
|       options { | ||||
|         65010: 1 | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "createdAt" | ||||
|     } | ||||
|     field { | ||||
|       name: "updated_at" | ||||
|       number: 6 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".google.protobuf.Timestamp" | ||||
|       options { | ||||
|         65010: 1 | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "updatedAt" | ||||
|     } | ||||
|     field { | ||||
|       name: "extensions" | ||||
|       number: 7 | ||||
|       label: LABEL_REPEATED | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox.ExtensionsEntry" | ||||
|       options { | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "extensions" | ||||
|     } | ||||
|     nested_type { | ||||
|       name: "Runtime" | ||||
|       field { | ||||
|         name: "name" | ||||
|         number: 1 | ||||
|         label: LABEL_OPTIONAL | ||||
|         type: TYPE_STRING | ||||
|         json_name: "name" | ||||
|       } | ||||
|       field { | ||||
|         name: "options" | ||||
|         number: 2 | ||||
|         label: LABEL_OPTIONAL | ||||
|         type: TYPE_MESSAGE | ||||
|         type_name: ".google.protobuf.Any" | ||||
|         json_name: "options" | ||||
|       } | ||||
|     } | ||||
|     nested_type { | ||||
|       name: "LabelsEntry" | ||||
|       field { | ||||
|         name: "key" | ||||
|         number: 1 | ||||
|         label: LABEL_OPTIONAL | ||||
|         type: TYPE_STRING | ||||
|         json_name: "key" | ||||
|       } | ||||
|       field { | ||||
|         name: "value" | ||||
|         number: 2 | ||||
|         label: LABEL_OPTIONAL | ||||
|         type: TYPE_STRING | ||||
|         json_name: "value" | ||||
|       } | ||||
|       options { | ||||
|         map_entry: true | ||||
|       } | ||||
|     } | ||||
|     nested_type { | ||||
|       name: "ExtensionsEntry" | ||||
|       field { | ||||
|         name: "key" | ||||
|         number: 1 | ||||
|         label: LABEL_OPTIONAL | ||||
|         type: TYPE_STRING | ||||
|         json_name: "key" | ||||
|       } | ||||
|       field { | ||||
|         name: "value" | ||||
|         number: 2 | ||||
|         label: LABEL_OPTIONAL | ||||
|         type: TYPE_MESSAGE | ||||
|         type_name: ".google.protobuf.Any" | ||||
|         json_name: "value" | ||||
|       } | ||||
|       options { | ||||
|         map_entry: true | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   options { | ||||
|     go_package: "github.com/containerd/containerd/api/types;types" | ||||
|   } | ||||
|   weak_dependency: 0 | ||||
|   syntax: "proto3" | ||||
| } | ||||
| file { | ||||
|   name: "github.com/containerd/containerd/api/services/sandbox/v1/sandbox.proto" | ||||
|   package: "containerd.services.sandbox.v1" | ||||
|   dependency: "google/protobuf/any.proto" | ||||
|   dependency: "google/protobuf/timestamp.proto" | ||||
|   dependency: "gogoproto/gogo.proto" | ||||
|   dependency: "github.com/containerd/containerd/api/types/sandbox.proto" | ||||
|   dependency: "github.com/containerd/containerd/api/types/mount.proto" | ||||
|   message_type { | ||||
|     name: "StoreCreateRequest" | ||||
|     field { | ||||
|       name: "sandbox" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox" | ||||
|       options { | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "sandbox" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreCreateResponse" | ||||
|     field { | ||||
|       name: "sandbox" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox" | ||||
|       options { | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "sandbox" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreUpdateRequest" | ||||
|     field { | ||||
|       name: "sandbox" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox" | ||||
|       options { | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "sandbox" | ||||
|     } | ||||
|     field { | ||||
|       name: "fields" | ||||
|       number: 2 | ||||
|       label: LABEL_REPEATED | ||||
|       type: TYPE_STRING | ||||
|       json_name: "fields" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreUpdateResponse" | ||||
|     field { | ||||
|       name: "sandbox" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox" | ||||
|       options { | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "sandbox" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreDeleteRequest" | ||||
|     field { | ||||
|       name: "sandbox_id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandboxId" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreDeleteResponse" | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreListRequest" | ||||
|     field { | ||||
|       name: "filters" | ||||
|       number: 1 | ||||
|       label: LABEL_REPEATED | ||||
|       type: TYPE_STRING | ||||
|       json_name: "filters" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreListResponse" | ||||
|     field { | ||||
|       name: "list" | ||||
|       number: 1 | ||||
|       label: LABEL_REPEATED | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox" | ||||
|       options { | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "list" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreGetRequest" | ||||
|     field { | ||||
|       name: "sandbox_id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandboxId" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "StoreGetResponse" | ||||
|     field { | ||||
|       name: "sandbox" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Sandbox" | ||||
|       json_name: "sandbox" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "ControllerStartRequest" | ||||
|     field { | ||||
|       name: "sandbox_id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandboxId" | ||||
|     } | ||||
|     field { | ||||
|       name: "rootfs" | ||||
|       number: 2 | ||||
|       label: LABEL_REPEATED | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".containerd.types.Mount" | ||||
|       json_name: "rootfs" | ||||
|     } | ||||
|     field { | ||||
|       name: "options" | ||||
|       number: 3 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".google.protobuf.Any" | ||||
|       json_name: "options" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "ControllerStartResponse" | ||||
|     field { | ||||
|       name: "sandbox_id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandboxId" | ||||
|     } | ||||
|     field { | ||||
|       name: "pid" | ||||
|       number: 2 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_UINT32 | ||||
|       json_name: "pid" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "ControllerShutdownRequest" | ||||
|     field { | ||||
|       name: "sandbox_id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandboxId" | ||||
|     } | ||||
|     field { | ||||
|       name: "timeout_secs" | ||||
|       number: 2 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_UINT32 | ||||
|       json_name: "timeoutSecs" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "ControllerShutdownResponse" | ||||
|   } | ||||
|   message_type { | ||||
|     name: "ControllerWaitRequest" | ||||
|     field { | ||||
|       name: "sandbox_id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandboxId" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "ControllerWaitResponse" | ||||
|     field { | ||||
|       name: "exit_status" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_UINT32 | ||||
|       json_name: "exitStatus" | ||||
|     } | ||||
|     field { | ||||
|       name: "exited_at" | ||||
|       number: 2 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".google.protobuf.Timestamp" | ||||
|       options { | ||||
|         65010: 1 | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "exitedAt" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "ControllerStatusRequest" | ||||
|     field { | ||||
|       name: "sandbox_id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "sandboxId" | ||||
|     } | ||||
|   } | ||||
|   message_type { | ||||
|     name: "ControllerStatusResponse" | ||||
|     field { | ||||
|       name: "id" | ||||
|       number: 1 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "id" | ||||
|     } | ||||
|     field { | ||||
|       name: "pid" | ||||
|       number: 2 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_UINT32 | ||||
|       json_name: "pid" | ||||
|     } | ||||
|     field { | ||||
|       name: "state" | ||||
|       number: 3 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_STRING | ||||
|       json_name: "state" | ||||
|     } | ||||
|     field { | ||||
|       name: "exit_status" | ||||
|       number: 4 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_UINT32 | ||||
|       json_name: "exitStatus" | ||||
|     } | ||||
|     field { | ||||
|       name: "exited_at" | ||||
|       number: 5 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".google.protobuf.Timestamp" | ||||
|       options { | ||||
|         65010: 1 | ||||
|         65001: 0 | ||||
|       } | ||||
|       json_name: "exitedAt" | ||||
|     } | ||||
|     field { | ||||
|       name: "extra" | ||||
|       number: 6 | ||||
|       label: LABEL_OPTIONAL | ||||
|       type: TYPE_MESSAGE | ||||
|       type_name: ".google.protobuf.Any" | ||||
|       json_name: "extra" | ||||
|     } | ||||
|   } | ||||
|   service { | ||||
|     name: "Store" | ||||
|     method { | ||||
|       name: "Create" | ||||
|       input_type: ".containerd.services.sandbox.v1.StoreCreateRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.StoreCreateResponse" | ||||
|     } | ||||
|     method { | ||||
|       name: "Update" | ||||
|       input_type: ".containerd.services.sandbox.v1.StoreUpdateRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.StoreUpdateResponse" | ||||
|     } | ||||
|     method { | ||||
|       name: "Delete" | ||||
|       input_type: ".containerd.services.sandbox.v1.StoreDeleteRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.StoreDeleteResponse" | ||||
|     } | ||||
|     method { | ||||
|       name: "List" | ||||
|       input_type: ".containerd.services.sandbox.v1.StoreListRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.StoreListResponse" | ||||
|     } | ||||
|     method { | ||||
|       name: "Get" | ||||
|       input_type: ".containerd.services.sandbox.v1.StoreGetRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.StoreGetResponse" | ||||
|     } | ||||
|   } | ||||
|   service { | ||||
|     name: "Controller" | ||||
|     method { | ||||
|       name: "Start" | ||||
|       input_type: ".containerd.services.sandbox.v1.ControllerStartRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.ControllerStartResponse" | ||||
|     } | ||||
|     method { | ||||
|       name: "Shutdown" | ||||
|       input_type: ".containerd.services.sandbox.v1.ControllerShutdownRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.ControllerShutdownResponse" | ||||
|     } | ||||
|     method { | ||||
|       name: "Wait" | ||||
|       input_type: ".containerd.services.sandbox.v1.ControllerWaitRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.ControllerWaitResponse" | ||||
|     } | ||||
|     method { | ||||
|       name: "Status" | ||||
|       input_type: ".containerd.services.sandbox.v1.ControllerStatusRequest" | ||||
|       output_type: ".containerd.services.sandbox.v1.ControllerStatusResponse" | ||||
|     } | ||||
|   } | ||||
|   options { | ||||
|     go_package: "github.com/containerd/containerd/api/services/sandbox/v1;sandbox" | ||||
|   } | ||||
|   weak_dependency: 2 | ||||
|   syntax: "proto3" | ||||
| } | ||||
| file { | ||||
|   name: "github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto" | ||||
|   package: "containerd.services.snapshots.v1" | ||||
|   | ||||
| @@ -81,10 +81,12 @@ type Container struct { | ||||
| 	// that should be unique against other extensions. When updating extension | ||||
| 	// data, one should only update the specified extension using field paths | ||||
| 	// to select a specific map key. | ||||
| 	Extensions           map[string]types.Any `protobuf:"bytes,10,rep,name=extensions,proto3" json:"extensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||
| 	XXX_NoUnkeyedLiteral struct{}             `json:"-"` | ||||
| 	XXX_unrecognized     []byte               `json:"-"` | ||||
| 	XXX_sizecache        int32                `json:"-"` | ||||
| 	Extensions map[string]types.Any `protobuf:"bytes,10,rep,name=extensions,proto3" json:"extensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||
| 	// Sandbox ID this container belongs to. | ||||
| 	Sandbox              string   `protobuf:"bytes,11,opt,name=sandbox,proto3" json:"sandbox,omitempty"` | ||||
| 	XXX_NoUnkeyedLiteral struct{} `json:"-"` | ||||
| 	XXX_unrecognized     []byte   `json:"-"` | ||||
| 	XXX_sizecache        int32    `json:"-"` | ||||
| } | ||||
|  | ||||
| func (m *Container) Reset()      { *m = Container{} } | ||||
| @@ -594,58 +596,59 @@ func init() { | ||||
| } | ||||
|  | ||||
| var fileDescriptor_311afb8e15951042 = []byte{ | ||||
| 	// 815 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x72, 0x12, 0x4d, | ||||
| 	0x14, 0xce, 0x00, 0x19, 0xc2, 0xe1, 0xaf, 0xfa, 0xff, 0xea, 0x1f, 0x71, 0x1c, 0xab, 0x80, 0xb0, | ||||
| 	0xa2, 0x2c, 0x1d, 0x12, 0xb4, 0xcc, 0xcd, 0x4d, 0xc8, 0xad, 0xd4, 0xc4, 0x4a, 0x4d, 0x74, 0xa3, | ||||
| 	0x8b, 0x38, 0x40, 0x87, 0x8c, 0xcc, 0xcd, 0xe9, 0x86, 0x92, 0x72, 0x11, 0x7d, 0x03, 0x77, 0x3e, | ||||
| 	0x82, 0xaf, 0x92, 0xa5, 0x4b, 0x57, 0xd1, 0x50, 0x3e, 0x88, 0x35, 0x3d, 0x3d, 0xcc, 0x84, 0x8b, | ||||
| 	0x42, 0x62, 0x76, 0x7d, 0xe8, 0xf3, 0x9d, 0xf3, 0xf5, 0x77, 0xfa, 0x6b, 0x06, 0x76, 0x9b, 0x3a, | ||||
| 	0x3d, 0x6e, 0xd7, 0x94, 0xba, 0x6d, 0x96, 0xeb, 0xb6, 0x45, 0x35, 0xdd, 0xc2, 0x6e, 0x23, 0xba, | ||||
| 	0xd4, 0x1c, 0xbd, 0x4c, 0xb0, 0xdb, 0xd1, 0xeb, 0x98, 0x84, 0xbf, 0x93, 0x72, 0x67, 0x31, 0x12, | ||||
| 	0x29, 0x8e, 0x6b, 0x53, 0x1b, 0xcd, 0x87, 0x38, 0x25, 0xc0, 0x28, 0x91, 0xac, 0xce, 0xa2, 0x9c, | ||||
| 	0x69, 0xda, 0x4d, 0x9b, 0x65, 0x97, 0xbd, 0x95, 0x0f, 0x94, 0x6f, 0x35, 0x6d, 0xbb, 0x69, 0xe0, | ||||
| 	0x32, 0x8b, 0x6a, 0xed, 0xa3, 0xb2, 0x66, 0x75, 0xf9, 0xd6, 0xed, 0xc1, 0x2d, 0x6c, 0x3a, 0x34, | ||||
| 	0xd8, 0x2c, 0x0c, 0x6e, 0x1e, 0xe9, 0xd8, 0x68, 0x1c, 0x9a, 0x1a, 0x69, 0xf1, 0x8c, 0xfc, 0x60, | ||||
| 	0x06, 0xd5, 0x4d, 0x4c, 0xa8, 0x66, 0x3a, 0x7e, 0x42, 0xf1, 0xb3, 0x08, 0xa9, 0x8d, 0x80, 0x22, | ||||
| 	0xca, 0x42, 0x4c, 0x6f, 0x48, 0x42, 0x41, 0x28, 0xa5, 0xaa, 0x62, 0xef, 0x2c, 0x1f, 0x7b, 0xbc, | ||||
| 	0xa9, 0xc6, 0xf4, 0x06, 0xda, 0x07, 0xd1, 0xd0, 0x6a, 0xd8, 0x20, 0x52, 0xac, 0x10, 0x2f, 0xa5, | ||||
| 	0x2b, 0xcb, 0xca, 0x1f, 0x8f, 0xaa, 0xf4, 0xab, 0x2a, 0xbb, 0x0c, 0xba, 0x65, 0x51, 0xb7, 0xab, | ||||
| 	0xf2, 0x3a, 0x28, 0x03, 0xb3, 0xba, 0xa9, 0x35, 0xb1, 0x14, 0xf7, 0x9a, 0xa9, 0x7e, 0x80, 0x9e, | ||||
| 	0x41, 0xd2, 0x6d, 0x5b, 0x1e, 0x47, 0x29, 0x51, 0x10, 0x4a, 0xe9, 0xca, 0x83, 0xa9, 0x1a, 0xa9, | ||||
| 	0x3e, 0x56, 0x0d, 0x8a, 0xa0, 0x12, 0x24, 0x88, 0x83, 0xeb, 0xd2, 0x2c, 0x2b, 0x96, 0x51, 0x7c, | ||||
| 	0x35, 0x94, 0x40, 0x0d, 0x65, 0xdd, 0xea, 0xaa, 0x2c, 0x03, 0x15, 0x20, 0x4d, 0x2c, 0xcd, 0x21, | ||||
| 	0xc7, 0x36, 0xa5, 0xd8, 0x95, 0x44, 0xc6, 0x2a, 0xfa, 0x13, 0x9a, 0x87, 0x7f, 0x82, 0xf0, 0xb0, | ||||
| 	0x85, 0xbb, 0x52, 0xf2, 0x62, 0xca, 0x53, 0xdc, 0x45, 0x1b, 0x00, 0x75, 0x17, 0x6b, 0x14, 0x37, | ||||
| 	0x0e, 0x35, 0x2a, 0xcd, 0xb1, 0xa6, 0xf2, 0x50, 0xd3, 0xe7, 0xc1, 0x08, 0xaa, 0x73, 0xa7, 0x67, | ||||
| 	0xf9, 0x99, 0x4f, 0xdf, 0xf3, 0x82, 0x9a, 0xe2, 0xb8, 0x75, 0xea, 0x15, 0x69, 0x3b, 0x8d, 0xa0, | ||||
| 	0x48, 0x6a, 0x9a, 0x22, 0x1c, 0xb7, 0x4e, 0x51, 0x0d, 0x00, 0xbf, 0xa3, 0xd8, 0x22, 0xba, 0x6d, | ||||
| 	0x11, 0x09, 0xd8, 0xd0, 0x1e, 0x4d, 0xa5, 0xe5, 0x56, 0x1f, 0xce, 0x06, 0x57, 0x4d, 0x78, 0x6d, | ||||
| 	0xd4, 0x48, 0x55, 0x79, 0x05, 0xd2, 0x91, 0xc9, 0xa2, 0xff, 0x20, 0xee, 0xc9, 0xc2, 0x2e, 0x8f, | ||||
| 	0xea, 0x2d, 0xbd, 0x19, 0x77, 0x34, 0xa3, 0x8d, 0xa5, 0x98, 0x3f, 0x63, 0x16, 0xac, 0xc6, 0x96, | ||||
| 	0x05, 0x79, 0x0f, 0x92, 0x7c, 0x56, 0x08, 0x41, 0xc2, 0xd2, 0x4c, 0xcc, 0x71, 0x6c, 0x8d, 0x14, | ||||
| 	0x48, 0xda, 0x0e, 0x65, 0xd4, 0x63, 0xbf, 0x99, 0x5c, 0x90, 0x24, 0x1f, 0xc0, 0xbf, 0x03, 0x74, | ||||
| 	0x47, 0xb0, 0xb9, 0x13, 0x65, 0x33, 0xae, 0x64, 0xc8, 0xb1, 0x78, 0x0f, 0xfe, 0xdf, 0xc1, 0xb4, | ||||
| 	0x2f, 0x88, 0x8a, 0xdf, 0xb6, 0x31, 0xa1, 0xe3, 0x2c, 0x52, 0x3c, 0x86, 0xcc, 0xc5, 0x74, 0xe2, | ||||
| 	0xd8, 0x16, 0xc1, 0x68, 0x1f, 0x52, 0x7d, 0x89, 0x19, 0x2c, 0x5d, 0xb9, 0x3b, 0xcd, 0x20, 0xb8, | ||||
| 	0xf0, 0x61, 0x91, 0xe2, 0x22, 0xdc, 0xd8, 0xd5, 0x49, 0xd8, 0x8a, 0x04, 0xd4, 0x24, 0x48, 0x1e, | ||||
| 	0xe9, 0x06, 0xc5, 0x2e, 0x91, 0x84, 0x42, 0xbc, 0x94, 0x52, 0x83, 0xb0, 0x68, 0x40, 0x76, 0x10, | ||||
| 	0xc2, 0xe9, 0xa9, 0x00, 0x61, 0x63, 0x06, 0xbb, 0x1c, 0xbf, 0x48, 0x95, 0xe2, 0x1b, 0xc8, 0x6e, | ||||
| 	0xb0, 0xeb, 0x3c, 0x24, 0xde, 0xdf, 0x17, 0xa3, 0x05, 0x37, 0x87, 0x7a, 0x5d, 0x9b, 0xf2, 0x5f, | ||||
| 	0x04, 0xc8, 0xbe, 0x60, 0x1e, 0xbb, 0xfe, 0x93, 0xa1, 0x35, 0x48, 0xfb, 0x7e, 0x66, 0xef, 0x39, | ||||
| 	0xbf, 0xb5, 0xc3, 0x0f, 0xc1, 0xb6, 0xf7, 0xe4, 0xef, 0x69, 0xa4, 0xa5, 0xf2, 0x67, 0xc3, 0x5b, | ||||
| 	0x7b, 0xb2, 0x0c, 0x11, 0xbd, 0x36, 0x59, 0x16, 0x20, 0xbb, 0x89, 0x0d, 0x3c, 0x42, 0x95, 0x71, | ||||
| 	0x66, 0xa9, 0x41, 0xe6, 0xc2, 0x7d, 0xdc, 0xc3, 0x84, 0x78, 0xef, 0xff, 0x93, 0x2b, 0x72, 0x8b, | ||||
| 	0xb0, 0xaa, 0xfc, 0x9c, 0x05, 0x08, 0x2f, 0x3c, 0xea, 0x40, 0x7c, 0x07, 0x53, 0xf4, 0x70, 0x82, | ||||
| 	0x72, 0x23, 0x6c, 0x2f, 0x2f, 0x4d, 0x8d, 0xe3, 0x72, 0xbf, 0x87, 0x84, 0x77, 0x54, 0x34, 0xc9, | ||||
| 	0x5f, 0xe6, 0x48, 0x5b, 0xcb, 0x2b, 0x97, 0x40, 0xf2, 0xe6, 0x1f, 0x05, 0x00, 0x6f, 0xeb, 0x80, | ||||
| 	0xba, 0x58, 0x33, 0xaf, 0xc0, 0x61, 0x69, 0x5a, 0x24, 0x9f, 0xe8, 0x82, 0x80, 0x4e, 0x40, 0xf4, | ||||
| 	0x1d, 0x8a, 0x26, 0x39, 0xc8, 0xe8, 0x87, 0x43, 0x5e, 0xbd, 0x0c, 0x94, 0x8b, 0x70, 0x02, 0xa2, | ||||
| 	0xef, 0x85, 0x89, 0x08, 0x8c, 0xf6, 0xf7, 0x44, 0x04, 0xc6, 0x39, 0xee, 0x15, 0x88, 0xbe, 0x3f, | ||||
| 	0x26, 0x22, 0x30, 0xda, 0x4a, 0x72, 0x76, 0xc8, 0xf9, 0x5b, 0xde, 0x97, 0x60, 0xf5, 0xf5, 0xe9, | ||||
| 	0x79, 0x6e, 0xe6, 0xdb, 0x79, 0x6e, 0xe6, 0x43, 0x2f, 0x27, 0x9c, 0xf6, 0x72, 0xc2, 0xd7, 0x5e, | ||||
| 	0x4e, 0xf8, 0xd1, 0xcb, 0x09, 0x2f, 0xb7, 0xaf, 0xf0, 0x71, 0xbb, 0x16, 0x46, 0x35, 0x91, 0x75, | ||||
| 	0xbc, 0xff, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x77, 0xc8, 0x5c, 0x78, 0x2d, 0x0b, 0x00, 0x00, | ||||
| 	// 832 bytes of a gzipped FileDescriptorProto | ||||
| 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x72, 0x1a, 0x47, | ||||
| 	0x14, 0xd5, 0x00, 0x1a, 0xc4, 0x25, 0x55, 0x49, 0x75, 0x08, 0x99, 0x4c, 0xaa, 0x00, 0xb1, 0xa2, | ||||
| 	0x52, 0xc9, 0x20, 0x91, 0x54, 0xf4, 0xca, 0x46, 0xe8, 0x55, 0x49, 0xa4, 0x94, 0x6a, 0x94, 0x6c, | ||||
| 	0xe2, 0x85, 0x3c, 0x40, 0x0b, 0x8d, 0x99, 0x97, 0xa7, 0x1b, 0x4a, 0x94, 0x17, 0xb2, 0xff, 0xc0, | ||||
| 	0x7f, 0xe1, 0xb5, 0xff, 0x42, 0x4b, 0x2f, 0xbd, 0x92, 0x2d, 0xca, 0x1f, 0xe2, 0xea, 0x9e, 0x1e, | ||||
| 	0x66, 0xc4, 0xc3, 0x06, 0xc9, 0xda, 0xf5, 0xa5, 0xef, 0xb9, 0xf7, 0xf4, 0xb9, 0x7d, 0x7a, 0x80, | ||||
| 	0xc3, 0xb6, 0x49, 0xcf, 0xbb, 0x0d, 0xad, 0xe9, 0xda, 0xd5, 0xa6, 0xeb, 0x50, 0xc3, 0x74, 0xb0, | ||||
| 	0xdf, 0x8a, 0x2f, 0x0d, 0xcf, 0xac, 0x12, 0xec, 0xf7, 0xcc, 0x26, 0x26, 0xd1, 0xef, 0xa4, 0xda, | ||||
| 	0x5b, 0x8d, 0x45, 0x9a, 0xe7, 0xbb, 0xd4, 0x45, 0xcb, 0x11, 0x4e, 0x0b, 0x31, 0x5a, 0x2c, 0xab, | ||||
| 	0xb7, 0xaa, 0xe6, 0xda, 0x6e, 0xdb, 0xe5, 0xd9, 0x55, 0xb6, 0x0a, 0x80, 0xea, 0x0f, 0x6d, 0xd7, | ||||
| 	0x6d, 0x5b, 0xb8, 0xca, 0xa3, 0x46, 0xf7, 0xac, 0x6a, 0x38, 0x7d, 0xb1, 0xf5, 0xe3, 0xe8, 0x16, | ||||
| 	0xb6, 0x3d, 0x1a, 0x6e, 0x96, 0x46, 0x37, 0xcf, 0x4c, 0x6c, 0xb5, 0x4e, 0x6d, 0x83, 0x74, 0x44, | ||||
| 	0x46, 0x71, 0x34, 0x83, 0x9a, 0x36, 0x26, 0xd4, 0xb0, 0xbd, 0x20, 0xa1, 0xfc, 0x5a, 0x86, 0xcc, | ||||
| 	0x4e, 0x48, 0x11, 0xe5, 0x21, 0x61, 0xb6, 0x14, 0xa9, 0x24, 0x55, 0x32, 0x75, 0x79, 0x70, 0x5d, | ||||
| 	0x4c, 0xfc, 0xb9, 0xab, 0x27, 0xcc, 0x16, 0x3a, 0x06, 0xd9, 0x32, 0x1a, 0xd8, 0x22, 0x4a, 0xa2, | ||||
| 	0x94, 0xac, 0x64, 0x6b, 0xeb, 0xda, 0x67, 0x8f, 0xaa, 0x0d, 0xab, 0x6a, 0x87, 0x1c, 0xba, 0xe7, | ||||
| 	0x50, 0xbf, 0xaf, 0x8b, 0x3a, 0x28, 0x07, 0x8b, 0xa6, 0x6d, 0xb4, 0xb1, 0x92, 0x64, 0xcd, 0xf4, | ||||
| 	0x20, 0x40, 0xff, 0x40, 0xda, 0xef, 0x3a, 0x8c, 0xa3, 0x92, 0x2a, 0x49, 0x95, 0x6c, 0xed, 0xb7, | ||||
| 	0xb9, 0x1a, 0xe9, 0x01, 0x56, 0x0f, 0x8b, 0xa0, 0x0a, 0xa4, 0x88, 0x87, 0x9b, 0xca, 0x22, 0x2f, | ||||
| 	0x96, 0xd3, 0x02, 0x35, 0xb4, 0x50, 0x0d, 0x6d, 0xdb, 0xe9, 0xeb, 0x3c, 0x03, 0x95, 0x20, 0x4b, | ||||
| 	0x1c, 0xc3, 0x23, 0xe7, 0x2e, 0xa5, 0xd8, 0x57, 0x64, 0xce, 0x2a, 0xfe, 0x13, 0x5a, 0x86, 0xaf, | ||||
| 	0xc2, 0xf0, 0xb4, 0x83, 0xfb, 0x4a, 0xfa, 0x76, 0xca, 0xdf, 0xb8, 0x8f, 0x76, 0x00, 0x9a, 0x3e, | ||||
| 	0x36, 0x28, 0x6e, 0x9d, 0x1a, 0x54, 0x59, 0xe2, 0x4d, 0xd5, 0xb1, 0xa6, 0xff, 0x86, 0x23, 0xa8, | ||||
| 	0x2f, 0x5d, 0x5d, 0x17, 0x17, 0x5e, 0xbe, 0x2b, 0x4a, 0x7a, 0x46, 0xe0, 0xb6, 0x29, 0x2b, 0xd2, | ||||
| 	0xf5, 0x5a, 0x61, 0x91, 0xcc, 0x3c, 0x45, 0x04, 0x6e, 0x9b, 0xa2, 0x06, 0x00, 0xbe, 0xa0, 0xd8, | ||||
| 	0x21, 0xa6, 0xeb, 0x10, 0x05, 0xf8, 0xd0, 0xfe, 0x98, 0x4b, 0xcb, 0xbd, 0x21, 0x9c, 0x0f, 0xae, | ||||
| 	0x9e, 0x62, 0x6d, 0xf4, 0x58, 0x55, 0xa4, 0x40, 0x9a, 0x18, 0x4e, 0xab, 0xe1, 0x5e, 0x28, 0x59, | ||||
| 	0xae, 0x45, 0x18, 0xaa, 0x1b, 0x90, 0x8d, 0xcd, 0x1c, 0x7d, 0x03, 0x49, 0x26, 0x18, 0xbf, 0x56, | ||||
| 	0x3a, 0x5b, 0xb2, 0xe9, 0xf7, 0x0c, 0xab, 0x8b, 0x95, 0x44, 0x30, 0x7d, 0x1e, 0x6c, 0x26, 0xd6, | ||||
| 	0x25, 0xf5, 0x08, 0xd2, 0x62, 0x8a, 0x08, 0x41, 0xca, 0x31, 0x6c, 0x2c, 0x70, 0x7c, 0x8d, 0x34, | ||||
| 	0x48, 0xbb, 0x1e, 0xe5, 0x87, 0x4a, 0x7c, 0x62, 0xa6, 0x61, 0x92, 0x7a, 0x02, 0x5f, 0x8f, 0x1c, | ||||
| 	0x64, 0x02, 0x9b, 0x9f, 0xe2, 0x6c, 0xa6, 0x95, 0x8c, 0x38, 0x96, 0x7f, 0x81, 0x6f, 0x0f, 0x30, | ||||
| 	0x1d, 0x4a, 0xa5, 0xe3, 0xa7, 0x5d, 0x4c, 0xe8, 0x34, 0xf3, 0x94, 0xcf, 0x21, 0x77, 0x3b, 0x9d, | ||||
| 	0x78, 0xae, 0x43, 0x30, 0x3a, 0x86, 0xcc, 0x50, 0x7c, 0x0e, 0xcb, 0xd6, 0x7e, 0x9e, 0x67, 0x44, | ||||
| 	0x62, 0x24, 0x51, 0x91, 0xf2, 0x2a, 0x7c, 0x77, 0x68, 0x92, 0xa8, 0x15, 0x09, 0xa9, 0x29, 0x90, | ||||
| 	0x3e, 0x33, 0x2d, 0x8a, 0x7d, 0xa2, 0x48, 0xa5, 0x24, 0x1b, 0x95, 0x08, 0xcb, 0x16, 0xe4, 0x47, | ||||
| 	0x21, 0x82, 0x9e, 0x0e, 0x10, 0x35, 0xe6, 0xb0, 0xbb, 0xf1, 0x8b, 0x55, 0x29, 0x3f, 0x81, 0xfc, | ||||
| 	0x0e, 0xbf, 0xe8, 0x63, 0xe2, 0x7d, 0x79, 0x31, 0x3a, 0xf0, 0xfd, 0x58, 0xaf, 0x07, 0x53, 0xfe, | ||||
| 	0x95, 0x04, 0xf9, 0xff, 0xb8, 0xfb, 0x1e, 0xfe, 0x64, 0x68, 0x0b, 0xb2, 0x81, 0xd3, 0xf9, 0x4b, | ||||
| 	0x2f, 0x6e, 0xed, 0xf8, 0x13, 0xb1, 0xcf, 0x3e, 0x06, 0x47, 0x06, 0xe9, 0xe8, 0xe2, 0x41, 0x61, | ||||
| 	0x6b, 0x26, 0xcb, 0x18, 0xd1, 0x07, 0x93, 0x65, 0x05, 0xf2, 0xbb, 0xd8, 0xc2, 0x13, 0x54, 0x99, | ||||
| 	0x66, 0x96, 0x06, 0xe4, 0x6e, 0xdd, 0xc7, 0x23, 0x4c, 0x08, 0xfb, 0x32, 0xfc, 0x75, 0x4f, 0x6e, | ||||
| 	0x31, 0x56, 0xb5, 0x0f, 0x8b, 0x00, 0xd1, 0x85, 0x47, 0x3d, 0x48, 0x1e, 0x60, 0x8a, 0x7e, 0x9f, | ||||
| 	0xa1, 0xdc, 0x04, 0xdb, 0xab, 0x6b, 0x73, 0xe3, 0x84, 0xdc, 0xcf, 0x20, 0xc5, 0x8e, 0x8a, 0x66, | ||||
| 	0xf9, 0x98, 0x4e, 0xb4, 0xb5, 0xba, 0x71, 0x07, 0xa4, 0x68, 0xfe, 0x42, 0x02, 0x60, 0x5b, 0x27, | ||||
| 	0xd4, 0xc7, 0x86, 0x7d, 0x0f, 0x0e, 0x6b, 0xf3, 0x22, 0xc5, 0x44, 0x57, 0x24, 0x74, 0x09, 0x72, | ||||
| 	0xe0, 0x50, 0x34, 0xcb, 0x41, 0x26, 0x3f, 0x1c, 0xea, 0xe6, 0x5d, 0xa0, 0x42, 0x84, 0x4b, 0x90, | ||||
| 	0x03, 0x2f, 0xcc, 0x44, 0x60, 0xb2, 0xbf, 0x67, 0x22, 0x30, 0xcd, 0x71, 0x8f, 0x40, 0x0e, 0xfc, | ||||
| 	0x31, 0x13, 0x81, 0xc9, 0x56, 0x52, 0xf3, 0x63, 0xce, 0xdf, 0x63, 0xff, 0x11, 0xeb, 0x8f, 0xaf, | ||||
| 	0x6e, 0x0a, 0x0b, 0x6f, 0x6f, 0x0a, 0x0b, 0xcf, 0x07, 0x05, 0xe9, 0x6a, 0x50, 0x90, 0xde, 0x0c, | ||||
| 	0x0a, 0xd2, 0xfb, 0x41, 0x41, 0xfa, 0x7f, 0xff, 0x1e, 0x7f, 0x7b, 0xb7, 0xa2, 0xa8, 0x21, 0xf3, | ||||
| 	0x8e, 0xbf, 0x7e, 0x0c, 0x00, 0x00, 0xff, 0xff, 0xef, 0xe7, 0xe1, 0xb7, 0x47, 0x0b, 0x00, 0x00, | ||||
| } | ||||
|  | ||||
| // Reference imports to suppress errors if they are not otherwise used. | ||||
| @@ -960,6 +963,13 @@ func (m *Container) MarshalToSizedBuffer(dAtA []byte) (int, error) { | ||||
| 		i -= len(m.XXX_unrecognized) | ||||
| 		copy(dAtA[i:], m.XXX_unrecognized) | ||||
| 	} | ||||
| 	if len(m.Sandbox) > 0 { | ||||
| 		i -= len(m.Sandbox) | ||||
| 		copy(dAtA[i:], m.Sandbox) | ||||
| 		i = encodeVarintContainers(dAtA, i, uint64(len(m.Sandbox))) | ||||
| 		i-- | ||||
| 		dAtA[i] = 0x5a | ||||
| 	} | ||||
| 	if len(m.Extensions) > 0 { | ||||
| 		for k := range m.Extensions { | ||||
| 			v := m.Extensions[k] | ||||
| @@ -1563,6 +1573,10 @@ func (m *Container) Size() (n int) { | ||||
| 			n += mapEntrySize + 1 + sovContainers(uint64(mapEntrySize)) | ||||
| 		} | ||||
| 	} | ||||
| 	l = len(m.Sandbox) | ||||
| 	if l > 0 { | ||||
| 		n += 1 + l + sovContainers(uint64(l)) | ||||
| 	} | ||||
| 	if m.XXX_unrecognized != nil { | ||||
| 		n += len(m.XXX_unrecognized) | ||||
| 	} | ||||
| @@ -1788,6 +1802,7 @@ func (this *Container) String() string { | ||||
| 		`CreatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.CreatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, | ||||
| 		`UpdatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, | ||||
| 		`Extensions:` + mapStringForExtensions + `,`, | ||||
| 		`Sandbox:` + fmt.Sprintf("%v", this.Sandbox) + `,`, | ||||
| 		`XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, | ||||
| 		`}`, | ||||
| 	}, "") | ||||
| @@ -2480,6 +2495,38 @@ func (m *Container) Unmarshal(dAtA []byte) error { | ||||
| 			} | ||||
| 			m.Extensions[mapkey] = *mapvalue | ||||
| 			iNdEx = postIndex | ||||
| 		case 11: | ||||
| 			if wireType != 2 { | ||||
| 				return fmt.Errorf("proto: wrong wireType = %d for field Sandbox", wireType) | ||||
| 			} | ||||
| 			var stringLen uint64 | ||||
| 			for shift := uint(0); ; shift += 7 { | ||||
| 				if shift >= 64 { | ||||
| 					return ErrIntOverflowContainers | ||||
| 				} | ||||
| 				if iNdEx >= l { | ||||
| 					return io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 				b := dAtA[iNdEx] | ||||
| 				iNdEx++ | ||||
| 				stringLen |= uint64(b&0x7F) << shift | ||||
| 				if b < 0x80 { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			intStringLen := int(stringLen) | ||||
| 			if intStringLen < 0 { | ||||
| 				return ErrInvalidLengthContainers | ||||
| 			} | ||||
| 			postIndex := iNdEx + intStringLen | ||||
| 			if postIndex < 0 { | ||||
| 				return ErrInvalidLengthContainers | ||||
| 			} | ||||
| 			if postIndex > l { | ||||
| 				return io.ErrUnexpectedEOF | ||||
| 			} | ||||
| 			m.Sandbox = string(dAtA[iNdEx:postIndex]) | ||||
| 			iNdEx = postIndex | ||||
| 		default: | ||||
| 			iNdEx = preIndex | ||||
| 			skippy, err := skipContainers(dAtA[iNdEx:]) | ||||
|   | ||||
| @@ -114,6 +114,9 @@ message Container { | ||||
| 	// data, one should only update the specified extension using field paths | ||||
| 	// to select a specific map key. | ||||
| 	map<string, google.protobuf.Any> extensions = 10 [(gogoproto.nullable) = false]; | ||||
|  | ||||
| 	// Sandbox ID this container belongs to. | ||||
| 	string sandbox = 11; | ||||
| } | ||||
|  | ||||
| message GetContainerRequest { | ||||
|   | ||||
							
								
								
									
										17
									
								
								api/services/sandbox/v1/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								api/services/sandbox/v1/doc.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandbox | ||||
							
								
								
									
										4333
									
								
								api/services/sandbox/v1/sandbox.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4333
									
								
								api/services/sandbox/v1/sandbox.pb.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										133
									
								
								api/services/sandbox/v1/sandbox.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								api/services/sandbox/v1/sandbox.proto
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| /* | ||||
| 	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"; | ||||
|  | ||||
| // Sandbox is a v2 runtime extension that allows more complex execution environments for containers. | ||||
| // This adds a notion of groups of containers that share same lifecycle and/or resources. | ||||
| // A few good fits for sandbox can be: | ||||
| // - A "pause" container in k8s, that acts as a parent process for child containers to hold network namespace. | ||||
| // - (micro)VMs that launch a VM process and executes containers inside guest OS. | ||||
| // containerd in this case remains implementation agnostic and delegates sandbox handling to runtimes. | ||||
| // See proposal and discussion here: https://github.com/containerd/containerd/issues/4131 | ||||
| package containerd.services.sandbox.v1; | ||||
|  | ||||
| import "google/protobuf/any.proto"; | ||||
| import "google/protobuf/timestamp.proto"; | ||||
| import weak "gogoproto/gogo.proto"; | ||||
|  | ||||
| import "github.com/containerd/containerd/api/types/sandbox.proto"; | ||||
| import "github.com/containerd/containerd/api/types/mount.proto"; | ||||
|  | ||||
| option go_package = "github.com/containerd/containerd/api/services/sandbox/v1;sandbox"; | ||||
|  | ||||
| // Store provides a metadata storage interface for sandboxes. Similarly to `Containers`, | ||||
| // sandbox object includes info required to start a new instance, but no runtime state. | ||||
| // When running a new sandbox instance, store objects are used as base type to create from. | ||||
| service Store { | ||||
| 	rpc Create(StoreCreateRequest) returns (StoreCreateResponse); | ||||
| 	rpc Update(StoreUpdateRequest) returns (StoreUpdateResponse); | ||||
| 	rpc Delete(StoreDeleteRequest) returns (StoreDeleteResponse); | ||||
| 	rpc List(StoreListRequest) returns (StoreListResponse); | ||||
| 	rpc Get(StoreGetRequest) returns (StoreGetResponse); | ||||
| } | ||||
|  | ||||
| message StoreCreateRequest { | ||||
| 	containerd.types.Sandbox sandbox = 1 [(gogoproto.nullable) = false]; | ||||
| } | ||||
|  | ||||
| message StoreCreateResponse { | ||||
| 	containerd.types.Sandbox sandbox = 1 [(gogoproto.nullable) = false]; | ||||
| } | ||||
|  | ||||
| message StoreUpdateRequest { | ||||
| 	containerd.types.Sandbox sandbox = 1 [(gogoproto.nullable) = false]; | ||||
| 	repeated string fields = 2; | ||||
| } | ||||
|  | ||||
| message StoreUpdateResponse { | ||||
| 	containerd.types.Sandbox sandbox = 1 [(gogoproto.nullable) = false]; | ||||
| } | ||||
|  | ||||
| message StoreDeleteRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message StoreDeleteResponse {} | ||||
|  | ||||
| message StoreListRequest { | ||||
| 	repeated string filters = 1; | ||||
| } | ||||
|  | ||||
| message StoreListResponse { | ||||
| 	repeated containerd.types.Sandbox list = 1 [(gogoproto.nullable) = false]; | ||||
| } | ||||
|  | ||||
| message StoreGetRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message StoreGetResponse { | ||||
| 	containerd.types.Sandbox sandbox = 1; | ||||
| } | ||||
|  | ||||
| // Controller is an interface to manage runtime sandbox instances. | ||||
| service Controller { | ||||
| 	rpc Start(ControllerStartRequest) returns (ControllerStartResponse); | ||||
| 	rpc Shutdown(ControllerShutdownRequest) returns (ControllerShutdownResponse); | ||||
| 	rpc Wait(ControllerWaitRequest) returns (ControllerWaitResponse); | ||||
| 	rpc Status(ControllerStatusRequest) returns (ControllerStatusResponse); | ||||
| } | ||||
|  | ||||
| message ControllerStartRequest { | ||||
| 	string sandbox_id = 1; | ||||
| 	repeated containerd.types.Mount rootfs = 2; | ||||
| 	google.protobuf.Any options = 3; | ||||
| } | ||||
|  | ||||
| message ControllerStartResponse { | ||||
| 	string sandbox_id = 1; | ||||
| 	uint32 pid = 2; | ||||
| } | ||||
|  | ||||
| message ControllerShutdownRequest { | ||||
| 	string sandbox_id = 1; | ||||
| 	uint32 timeout_secs = 2; | ||||
| } | ||||
|  | ||||
| message ControllerShutdownResponse {} | ||||
|  | ||||
| message ControllerWaitRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message ControllerWaitResponse { | ||||
| 	uint32 exit_status = 1; | ||||
| 	google.protobuf.Timestamp exited_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | ||||
| } | ||||
|  | ||||
| message ControllerStatusRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message ControllerStatusResponse { | ||||
| 	string id = 1; | ||||
| 	uint32 pid = 2; | ||||
| 	string state = 3; | ||||
| 	uint32 exit_status = 4; | ||||
| 	google.protobuf.Timestamp exited_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | ||||
| 	google.protobuf.Any extra = 6; | ||||
| } | ||||
							
								
								
									
										1148
									
								
								api/types/sandbox.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1148
									
								
								api/types/sandbox.pb.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										52
									
								
								api/types/sandbox.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								api/types/sandbox.proto
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /* | ||||
| 	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 weak "gogoproto/gogo.proto"; | ||||
| import "google/protobuf/any.proto"; | ||||
| import "google/protobuf/timestamp.proto"; | ||||
|  | ||||
| option go_package = "github.com/containerd/containerd/api/types;types"; | ||||
|  | ||||
| // Sandbox represents a sandbox metadata object that keeps all info required by controller to | ||||
| // work with a particular instance. | ||||
| message Sandbox { | ||||
| 	// SandboxID is a unique instance identifier within namespace | ||||
| 	string sandbox_id = 1; | ||||
| 	message Runtime { | ||||
| 		// Name is the name of the runtime. | ||||
| 		string name = 1; | ||||
| 		// Options specify additional runtime initialization options for the shim (this data will be available in StartShim). | ||||
| 		// Typically this data expected to be runtime shim implementation specific. | ||||
| 		google.protobuf.Any options = 2; | ||||
| 	} | ||||
| 	// Runtime specifies which runtime to use for executing this container. | ||||
| 	Runtime runtime = 2 [(gogoproto.nullable) = false]; | ||||
| 	// 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; | ||||
| 	// Labels provides an area to include arbitrary data on containers. | ||||
| 	map<string, string> labels  = 4 [(gogoproto.nullable) = false]; | ||||
| 	// CreatedAt is the time the container was first created. | ||||
| 	google.protobuf.Timestamp created_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | ||||
| 	// UpdatedAt is the last time the container was mutated. | ||||
| 	google.protobuf.Timestamp updated_at = 6 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | ||||
| 	// Extensions allow clients to provide optional blobs that can be handled by runtime. | ||||
| 	map<string, google.protobuf.Any> extensions = 7 [(gogoproto.nullable) = false]; | ||||
| } | ||||
							
								
								
									
										24
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								client.go
									
									
									
									
									
								
							| @@ -35,6 +35,7 @@ import ( | ||||
| 	introspectionapi "github.com/containerd/containerd/api/services/introspection/v1" | ||||
| 	leasesapi "github.com/containerd/containerd/api/services/leases/v1" | ||||
| 	namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1" | ||||
| 	sandboxsapi "github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| 	snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1" | ||||
| 	"github.com/containerd/containerd/api/services/tasks/v1" | ||||
| 	versionservice "github.com/containerd/containerd/api/services/version/v1" | ||||
| @@ -54,13 +55,14 @@ import ( | ||||
| 	"github.com/containerd/containerd/plugin" | ||||
| 	"github.com/containerd/containerd/remotes" | ||||
| 	"github.com/containerd/containerd/remotes/docker" | ||||
| 	"github.com/containerd/containerd/sandbox" | ||||
| 	"github.com/containerd/containerd/services/introspection" | ||||
| 	"github.com/containerd/containerd/snapshots" | ||||
| 	snproxy "github.com/containerd/containerd/snapshots/proxy" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	ptypes "github.com/gogo/protobuf/types" | ||||
| 	ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| 	specs "github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"golang.org/x/sync/semaphore" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/backoff" | ||||
| @@ -688,6 +690,26 @@ func (c *Client) EventService() EventService { | ||||
| 	return NewEventServiceFromClient(eventsapi.NewEventsClient(c.conn)) | ||||
| } | ||||
|  | ||||
| // SandboxStore returns the underlying sandbox store client | ||||
| func (c *Client) SandboxStore() sandbox.Store { | ||||
| 	if c.sandboxStore != nil { | ||||
| 		return c.sandboxStore | ||||
| 	} | ||||
| 	c.connMu.Lock() | ||||
| 	defer c.connMu.Unlock() | ||||
| 	return NewRemoteSandboxStore(sandboxsapi.NewStoreClient(c.conn)) | ||||
| } | ||||
|  | ||||
| // SandboxController returns the underlying sandbox controller client | ||||
| func (c *Client) SandboxController() sandbox.Controller { | ||||
| 	if c.sandboxController != nil { | ||||
| 		return c.sandboxController | ||||
| 	} | ||||
| 	c.connMu.Lock() | ||||
| 	defer c.connMu.Unlock() | ||||
| 	return NewSandboxRemoteController(sandboxsapi.NewControllerClient(c.conn)) | ||||
| } | ||||
|  | ||||
| // VersionService returns the underlying VersionClient | ||||
| func (c *Client) VersionService() versionservice.VersionClient { | ||||
| 	c.connMu.Lock() | ||||
|   | ||||
| @@ -23,6 +23,7 @@ import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"github.com/containerd/containerd/runtime/v2/runc/manager" | ||||
| 	_ "github.com/containerd/containerd/runtime/v2/runc/pause" | ||||
| 	_ "github.com/containerd/containerd/runtime/v2/runc/task/plugin" | ||||
| 	"github.com/containerd/containerd/runtime/v2/shim" | ||||
| ) | ||||
|   | ||||
| @@ -34,6 +34,7 @@ import ( | ||||
| 	_ "github.com/containerd/containerd/services/leases" | ||||
| 	_ "github.com/containerd/containerd/services/namespaces" | ||||
| 	_ "github.com/containerd/containerd/services/opt" | ||||
| 	_ "github.com/containerd/containerd/services/sandbox" | ||||
| 	_ "github.com/containerd/containerd/services/snapshots" | ||||
| 	_ "github.com/containerd/containerd/services/tasks" | ||||
| 	_ "github.com/containerd/containerd/services/version" | ||||
|   | ||||
| @@ -31,6 +31,7 @@ import ( | ||||
| 	"github.com/containerd/containerd/cmd/ctr/commands/plugins" | ||||
| 	"github.com/containerd/containerd/cmd/ctr/commands/pprof" | ||||
| 	"github.com/containerd/containerd/cmd/ctr/commands/run" | ||||
| 	"github.com/containerd/containerd/cmd/ctr/commands/sandboxes" | ||||
| 	"github.com/containerd/containerd/cmd/ctr/commands/snapshots" | ||||
| 	"github.com/containerd/containerd/cmd/ctr/commands/tasks" | ||||
| 	versionCmd "github.com/containerd/containerd/cmd/ctr/commands/version" | ||||
| @@ -114,6 +115,7 @@ containerd CLI | ||||
| 		tasks.Command, | ||||
| 		install.Command, | ||||
| 		ociCmd.Command, | ||||
| 		sandboxes.Command, | ||||
| 	}, extraCmds...) | ||||
| 	app.Before = func(context *cli.Context) error { | ||||
| 		if context.GlobalBool("debug") { | ||||
|   | ||||
							
								
								
									
										178
									
								
								cmd/ctr/commands/sandboxes/sandboxes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								cmd/ctr/commands/sandboxes/sandboxes.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandboxes | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"text/tabwriter" | ||||
|  | ||||
| 	"github.com/containerd/containerd" | ||||
| 	"github.com/containerd/containerd/cmd/ctr/commands" | ||||
| 	"github.com/containerd/containerd/defaults" | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	"github.com/containerd/containerd/oci" | ||||
| 	"github.com/urfave/cli" | ||||
| ) | ||||
|  | ||||
| // Command is a set of subcommands to manage runtimes with sandbox support | ||||
| var Command = cli.Command{ | ||||
| 	Name:    "sandboxes", | ||||
| 	Aliases: []string{"sandbox", "sb", "s"}, | ||||
| 	Usage:   "manage sandboxes", | ||||
| 	Subcommands: cli.Commands{ | ||||
| 		runCommand, | ||||
| 		listCommand, | ||||
| 		removeCommand, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| var runCommand = cli.Command{ | ||||
| 	Name:      "run", | ||||
| 	Aliases:   []string{"create", "c", "r"}, | ||||
| 	Usage:     "run a new sandbox", | ||||
| 	ArgsUsage: "[flags] <pod-config.json> <sandbox-id>", | ||||
| 	Flags: []cli.Flag{ | ||||
| 		cli.StringFlag{ | ||||
| 			Name:  "runtime", | ||||
| 			Usage: "runtime name", | ||||
| 			Value: defaults.DefaultRuntime, | ||||
| 		}, | ||||
| 	}, | ||||
| 	Action: func(context *cli.Context) error { | ||||
| 		var ( | ||||
| 			id      = context.Args().Get(0) | ||||
| 			runtime = context.String("runtime") | ||||
| 		) | ||||
|  | ||||
| 		client, ctx, cancel, err := commands.NewClient(context) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer cancel() | ||||
|  | ||||
| 		sandbox, err := client.NewSandbox(ctx, id, | ||||
| 			containerd.WithSandboxRuntime(runtime, nil), | ||||
| 			containerd.WithSandboxSpec(&oci.Spec{}), | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to create new sandbox: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		err = sandbox.Start(ctx) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to start: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		fmt.Println(sandbox.ID()) | ||||
| 		return nil | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| var listCommand = cli.Command{ | ||||
| 	Name:    "list", | ||||
| 	Aliases: []string{"ls"}, | ||||
| 	Usage:   "list sandboxes", | ||||
| 	Flags: []cli.Flag{ | ||||
| 		cli.StringSliceFlag{ | ||||
| 			Name:  "filters", | ||||
| 			Usage: "the list of filters to apply when querying sandboxes from the store", | ||||
| 		}, | ||||
| 	}, | ||||
| 	Action: func(context *cli.Context) error { | ||||
| 		client, ctx, cancel, err := commands.NewClient(context) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer cancel() | ||||
|  | ||||
| 		var ( | ||||
| 			writer  = tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0) | ||||
| 			filters = context.StringSlice("filters") | ||||
| 		) | ||||
|  | ||||
| 		defer func() { | ||||
| 			_ = writer.Flush() | ||||
| 		}() | ||||
|  | ||||
| 		list, err := client.SandboxStore().List(ctx, filters...) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to list sandboxes: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		if _, err := fmt.Fprintln(writer, "ID\tCREATED\tRUNTIME\t"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		for _, sandbox := range list { | ||||
| 			_, err := fmt.Fprintf(writer, "%s\t%s\t%s\t\n", sandbox.ID, sandbox.CreatedAt, sandbox.Runtime.Name) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| var removeCommand = cli.Command{ | ||||
| 	Name:      "remove", | ||||
| 	Aliases:   []string{"rm"}, | ||||
| 	ArgsUsage: "<id> [<id>, ...]", | ||||
| 	Usage:     "remove sandboxes", | ||||
| 	Flags: []cli.Flag{ | ||||
| 		cli.BoolFlag{ | ||||
| 			Name:  "force, f", | ||||
| 			Usage: "ignore shutdown errors when removing sandbox", | ||||
| 		}, | ||||
| 	}, | ||||
| 	Action: func(context *cli.Context) error { | ||||
| 		client, ctx, cancel, err := commands.NewClient(context) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer cancel() | ||||
|  | ||||
| 		force := context.Bool("force") | ||||
|  | ||||
| 		for _, id := range context.Args() { | ||||
| 			sandbox, err := client.LoadSandbox(ctx, id) | ||||
| 			if err != nil { | ||||
| 				log.G(ctx).WithError(err).Errorf("failed to load sandbox %s", id) | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			err = sandbox.Stop(ctx) | ||||
| 			if err != nil { | ||||
| 				log.G(ctx).WithError(err).Errorf("failed to stop sandbox %s", id) | ||||
| 				if !force { | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			err = sandbox.Delete(ctx) | ||||
| 			if err != nil { | ||||
| 				log.G(ctx).WithError(err).Errorf("failed to shutdown sandbox %s", id) | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			log.G(ctx).Infof("deleted: %s", id) | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	}, | ||||
| } | ||||
| @@ -74,6 +74,15 @@ func WithRuntime(name string, options interface{}) NewContainerOpts { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithSandbox joins the container to a container group (aka sandbox) from the given ID | ||||
| // Note: shim runtime must support sandboxes environments. | ||||
| func WithSandbox(sandboxID string) NewContainerOpts { | ||||
| 	return func(ctx context.Context, client *Client, c *containers.Container) error { | ||||
| 		c.SandboxID = sandboxID | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithImage sets the provided image as the base for the container | ||||
| func WithImage(i Image) NewContainerOpts { | ||||
| 	return func(ctx context.Context, client *Client, c *containers.Container) error { | ||||
|   | ||||
| @@ -76,6 +76,11 @@ type Container struct { | ||||
|  | ||||
| 	// Extensions stores client-specified metadata | ||||
| 	Extensions map[string]typeurl.Any | ||||
|  | ||||
| 	// SandboxID is an identifier of sandbox this container belongs to. | ||||
| 	// | ||||
| 	// This property is optional, but can't be changed after creation. | ||||
| 	SandboxID string | ||||
| } | ||||
|  | ||||
| // RuntimeInfo holds runtime specific information | ||||
|   | ||||
| @@ -166,6 +166,7 @@ func containerToProto(container *containers.Container) containersapi.Container { | ||||
| 		Snapshotter: container.Snapshotter, | ||||
| 		SnapshotKey: container.SnapshotKey, | ||||
| 		Extensions:  extensions, | ||||
| 		Sandbox:     container.SandboxID, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -193,6 +194,7 @@ func containerFromProto(containerpb *containersapi.Container) containers.Contain | ||||
| 		CreatedAt:   containerpb.CreatedAt, | ||||
| 		UpdatedAt:   containerpb.UpdatedAt, | ||||
| 		Extensions:  extensions, | ||||
| 		SandboxID:   containerpb.Sandbox, | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @@ -48,6 +48,7 @@ require ( | ||||
| 	github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 | ||||
| 	github.com/opencontainers/selinux v1.10.0 | ||||
| 	github.com/pelletier/go-toml v1.9.3 | ||||
| 	github.com/pkg/errors v0.9.1 | ||||
| 	github.com/prometheus/client_golang v1.11.1 | ||||
| 	github.com/sirupsen/logrus v1.8.1 | ||||
| 	github.com/stretchr/testify v1.7.0 | ||||
| @@ -103,7 +104,6 @@ require ( | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e // indirect | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
| 	github.com/prometheus/client_model v0.2.0 // indirect | ||||
| 	github.com/prometheus/common v0.30.0 // indirect | ||||
|   | ||||
| @@ -24,6 +24,7 @@ import ( | ||||
| 	"github.com/containerd/containerd/filters" | ||||
| 	"github.com/containerd/containerd/images" | ||||
| 	"github.com/containerd/containerd/leases" | ||||
| 	"github.com/containerd/containerd/sandbox" | ||||
| 	"github.com/containerd/containerd/snapshots" | ||||
| ) | ||||
|  | ||||
| @@ -149,6 +150,23 @@ func adaptSnapshot(info snapshots.Info) filters.Adaptor { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func adaptSandbox(instance *sandbox.Sandbox) filters.Adaptor { | ||||
| 	return filters.AdapterFunc(func(fieldpath []string) (string, bool) { | ||||
| 		if len(fieldpath) == 0 { | ||||
| 			return "", false | ||||
| 		} | ||||
|  | ||||
| 		switch fieldpath[0] { | ||||
| 		case "id": | ||||
| 			return instance.ID, true | ||||
| 		case "labels": | ||||
| 			return checkMap(fieldpath[1:], instance.Labels) | ||||
| 		default: | ||||
| 			return "", false | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func checkMap(fieldpath []string, m map[string]string) (string, bool) { | ||||
| 	if len(m) == 0 { | ||||
| 		return "", false | ||||
|   | ||||
| @@ -213,11 +213,11 @@ func WriteAny(bkt *bolt.Bucket, name []byte, any typeurl.Any) error { | ||||
|  | ||||
| 	data, err := proto.Marshal(pbany) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 		return fmt.Errorf("failed to marshal: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := bkt.Put(name, data); err != nil { | ||||
| 		return err | ||||
| 		return fmt.Errorf("put failed: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
|   | ||||
| @@ -130,6 +130,7 @@ var ( | ||||
| 	bucketKeyObjectBlob       = []byte("blob")       // stores content links | ||||
| 	bucketKeyObjectIngests    = []byte("ingests")    // stores ingest objects | ||||
| 	bucketKeyObjectLeases     = []byte("leases")     // stores leases | ||||
| 	bucketKeyObjectSandboxes  = []byte("sandboxes")  // stores sandboxes | ||||
|  | ||||
| 	bucketKeyDigest      = []byte("digest") | ||||
| 	bucketKeyMediaType   = []byte("mediatype") | ||||
| @@ -149,6 +150,7 @@ var ( | ||||
| 	bucketKeyExpected    = []byte("expected") | ||||
| 	bucketKeyRef         = []byte("ref") | ||||
| 	bucketKeyExpireAt    = []byte("expireat") | ||||
| 	bucketKeySandboxID   = []byte("sandboxid") | ||||
|  | ||||
| 	deprecatedBucketKeyObjectIngest = []byte("ingest") // stores ingest links, deprecated in v1.2 | ||||
| ) | ||||
| @@ -270,3 +272,19 @@ func createIngestBucket(tx *bolt.Tx, namespace, ref string) (*bolt.Bucket, error | ||||
| func getIngestBucket(tx *bolt.Tx, namespace, ref string) *bolt.Bucket { | ||||
| 	return getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectContent, bucketKeyObjectIngests, []byte(ref)) | ||||
| } | ||||
|  | ||||
| func createSandboxBucket(tx *bolt.Tx, namespace string) (*bolt.Bucket, error) { | ||||
| 	return createBucketIfNotExists( | ||||
| 		tx, | ||||
| 		[]byte(namespace), | ||||
| 		bucketKeyObjectSandboxes, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func getSandboxBucket(tx *bolt.Tx, namespace string) *bolt.Bucket { | ||||
| 	return getBucket( | ||||
| 		tx, | ||||
| 		[]byte(namespace), | ||||
| 		bucketKeyObjectSandboxes, | ||||
| 	) | ||||
| } | ||||
|   | ||||
| @@ -359,6 +359,8 @@ func readContainer(container *containers.Container, bkt *bolt.Bucket) error { | ||||
| 			} | ||||
|  | ||||
| 			container.Extensions = extensions | ||||
| 		case string(bucketKeySandboxID): | ||||
| 			container.SandboxID = string(v) | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| @@ -407,5 +409,9 @@ func writeContainer(bkt *bolt.Bucket, container *containers.Container) error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := bkt.Put(bucketKeySandboxID, []byte(container.SandboxID)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return boltutil.WriteLabels(bkt, container.Labels) | ||||
| } | ||||
|   | ||||
							
								
								
									
										377
									
								
								metadata/sandbox.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								metadata/sandbox.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,377 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package metadata | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/filters" | ||||
| 	"github.com/containerd/containerd/identifiers" | ||||
| 	"github.com/containerd/containerd/metadata/boltutil" | ||||
| 	"github.com/containerd/containerd/namespaces" | ||||
| 	api "github.com/containerd/containerd/sandbox" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"go.etcd.io/bbolt" | ||||
| ) | ||||
|  | ||||
| type sandboxStore struct { | ||||
| 	db *DB | ||||
| } | ||||
|  | ||||
| var _ api.Store = (*sandboxStore)(nil) | ||||
|  | ||||
| // NewSandboxStore creates a datababase client for sandboxes | ||||
| func NewSandboxStore(db *DB) api.Store { | ||||
| 	return &sandboxStore{db: db} | ||||
| } | ||||
|  | ||||
| // Create a sandbox record in the store | ||||
| func (s *sandboxStore) Create(ctx context.Context, sandbox api.Sandbox) (api.Sandbox, error) { | ||||
| 	ns, err := namespaces.NamespaceRequired(ctx) | ||||
| 	if err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	sandbox.CreatedAt = time.Now().UTC() | ||||
| 	sandbox.UpdatedAt = sandbox.CreatedAt | ||||
|  | ||||
| 	if err := s.validate(&sandbox); err != nil { | ||||
| 		return api.Sandbox{}, errors.Wrap(err, "failed to validate sandbox") | ||||
| 	} | ||||
|  | ||||
| 	if err := s.db.Update(func(tx *bbolt.Tx) error { | ||||
| 		parent, err := createSandboxBucket(tx, ns) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("create error: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		if err := s.write(parent, &sandbox, false); err != nil { | ||||
| 			return fmt.Errorf("write error: %w", err) | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	return sandbox, nil | ||||
| } | ||||
|  | ||||
| // Update the sandbox with the provided sandbox object and fields | ||||
| func (s *sandboxStore) Update(ctx context.Context, sandbox api.Sandbox, fieldpaths ...string) (api.Sandbox, error) { | ||||
| 	ns, err := namespaces.NamespaceRequired(ctx) | ||||
| 	if err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	ret := api.Sandbox{} | ||||
| 	if err := update(ctx, s.db, func(tx *bbolt.Tx) error { | ||||
| 		parent := getSandboxBucket(tx, ns) | ||||
| 		if parent == nil { | ||||
| 			return errors.Wrap(errdefs.ErrNotFound, "no sandbox buckets") | ||||
| 		} | ||||
|  | ||||
| 		updated, err := s.read(parent, []byte(sandbox.ID)) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if len(fieldpaths) == 0 { | ||||
| 			fieldpaths = []string{"labels", "extensions", "spec", "runtime"} | ||||
|  | ||||
| 			if updated.Runtime.Name != sandbox.Runtime.Name { | ||||
| 				return errors.Wrapf(errdefs.ErrInvalidArgument, "sandbox.Runtime.Name field is immutable") | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for _, path := range fieldpaths { | ||||
| 			if strings.HasPrefix(path, "labels.") { | ||||
| 				if updated.Labels == nil { | ||||
| 					updated.Labels = map[string]string{} | ||||
| 				} | ||||
|  | ||||
| 				key := strings.TrimPrefix(path, "labels.") | ||||
| 				updated.Labels[key] = sandbox.Labels[key] | ||||
| 				continue | ||||
| 			} else if strings.HasPrefix(path, "extensions.") { | ||||
| 				if updated.Extensions == nil { | ||||
| 					updated.Extensions = map[string]typeurl.Any{} | ||||
| 				} | ||||
|  | ||||
| 				key := strings.TrimPrefix(path, "extensions.") | ||||
| 				updated.Extensions[key] = sandbox.Extensions[key] | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			switch path { | ||||
| 			case "labels": | ||||
| 				updated.Labels = sandbox.Labels | ||||
| 			case "extensions": | ||||
| 				updated.Extensions = sandbox.Extensions | ||||
| 			case "runtime": | ||||
| 				updated.Runtime = sandbox.Runtime | ||||
| 			case "spec": | ||||
| 				updated.Spec = sandbox.Spec | ||||
| 			default: | ||||
| 				return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on sandbox %q", path, sandbox.ID) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		updated.UpdatedAt = time.Now().UTC() | ||||
|  | ||||
| 		if err := s.validate(&updated); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if err := s.write(parent, &updated, true); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		ret = updated | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	return ret, nil | ||||
| } | ||||
|  | ||||
| // Get sandbox metadata using the id | ||||
| func (s *sandboxStore) Get(ctx context.Context, id string) (api.Sandbox, error) { | ||||
| 	ns, err := namespaces.NamespaceRequired(ctx) | ||||
| 	if err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	ret := api.Sandbox{} | ||||
| 	if err := view(ctx, s.db, func(tx *bbolt.Tx) error { | ||||
| 		bucket := getSandboxBucket(tx, ns) | ||||
| 		if bucket == nil { | ||||
| 			return errors.Wrap(errdefs.ErrNotFound, "no sandbox buckets") | ||||
| 		} | ||||
|  | ||||
| 		out, err := s.read(bucket, []byte(id)) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		ret = out | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	return ret, nil | ||||
| } | ||||
|  | ||||
| // List returns sandboxes that match one or more of the provided filters | ||||
| func (s *sandboxStore) List(ctx context.Context, fields ...string) ([]api.Sandbox, error) { | ||||
| 	ns, err := namespaces.NamespaceRequired(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	filter, err := filters.ParseAll(fields...) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	var ( | ||||
| 		list []api.Sandbox | ||||
| 	) | ||||
|  | ||||
| 	if err := view(ctx, s.db, func(tx *bbolt.Tx) error { | ||||
| 		bucket := getSandboxBucket(tx, ns) | ||||
| 		if bucket == nil { | ||||
| 			// We haven't created any sandboxes yet, just return empty list | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		if err := bucket.ForEach(func(k, v []byte) error { | ||||
| 			info, err := s.read(bucket, k) | ||||
| 			if err != nil { | ||||
| 				return errors.Wrapf(err, "failed to read bucket %q", string(k)) | ||||
| 			} | ||||
|  | ||||
| 			if filter.Match(adaptSandbox(&info)) { | ||||
| 				list = append(list, info) | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return list, nil | ||||
| } | ||||
|  | ||||
| // Delete a sandbox from metadata store using the id | ||||
| func (s *sandboxStore) Delete(ctx context.Context, id string) error { | ||||
| 	ns, err := namespaces.NamespaceRequired(ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := update(ctx, s.db, func(tx *bbolt.Tx) error { | ||||
| 		buckets := getSandboxBucket(tx, ns) | ||||
| 		if buckets == nil { | ||||
| 			return errors.Wrap(errdefs.ErrNotFound, "no sandbox buckets") | ||||
| 		} | ||||
|  | ||||
| 		if err := buckets.DeleteBucket([]byte(id)); err != nil { | ||||
| 			return errors.Wrapf(err, "failed to delete sandbox %q", id) | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	}); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxStore) write(parent *bbolt.Bucket, instance *api.Sandbox, overwrite bool) error { | ||||
| 	var ( | ||||
| 		bucket *bbolt.Bucket | ||||
| 		err    error | ||||
| 		id     = []byte(instance.ID) | ||||
| 	) | ||||
|  | ||||
| 	if overwrite { | ||||
| 		bucket, err = parent.CreateBucketIfNotExists(id) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} else { | ||||
| 		bucket = parent.Bucket(id) | ||||
| 		if bucket != nil { | ||||
| 			return errors.Wrapf(errdefs.ErrAlreadyExists, "sandbox bucket %q already exists", instance.ID) | ||||
| 		} | ||||
|  | ||||
| 		bucket, err = parent.CreateBucket(id) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err := boltutil.WriteTimestamps(bucket, instance.CreatedAt, instance.UpdatedAt); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := boltutil.WriteLabels(bucket, instance.Labels); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := boltutil.WriteExtensions(bucket, instance.Extensions); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := boltutil.WriteAny(bucket, bucketKeySpec, instance.Spec); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	runtimeBucket, err := bucket.CreateBucketIfNotExists(bucketKeyRuntime) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := runtimeBucket.Put(bucketKeyName, []byte(instance.Runtime.Name)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := boltutil.WriteAny(runtimeBucket, bucketKeyOptions, instance.Runtime.Options); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxStore) read(parent *bbolt.Bucket, id []byte) (api.Sandbox, error) { | ||||
| 	var ( | ||||
| 		inst api.Sandbox | ||||
| 		err  error | ||||
| 	) | ||||
|  | ||||
| 	bucket := parent.Bucket(id) | ||||
| 	if bucket == nil { | ||||
| 		return api.Sandbox{}, errors.Wrapf(errdefs.ErrNotFound, "bucket %q not found", id) | ||||
| 	} | ||||
|  | ||||
| 	inst.ID = string(id) | ||||
|  | ||||
| 	inst.Labels, err = boltutil.ReadLabels(bucket) | ||||
| 	if err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	if err := boltutil.ReadTimestamps(bucket, &inst.CreatedAt, &inst.UpdatedAt); err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	inst.Spec, err = boltutil.ReadAny(bucket, bucketKeySpec) | ||||
| 	if err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	runtimeBucket := bucket.Bucket(bucketKeyRuntime) | ||||
| 	if runtimeBucket == nil { | ||||
| 		return api.Sandbox{}, errors.New("no runtime bucket") | ||||
| 	} | ||||
|  | ||||
| 	inst.Runtime.Name = string(runtimeBucket.Get(bucketKeyName)) | ||||
| 	inst.Runtime.Options, err = boltutil.ReadAny(runtimeBucket, bucketKeyOptions) | ||||
| 	if err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	inst.Extensions, err = boltutil.ReadExtensions(bucket) | ||||
| 	if err != nil { | ||||
| 		return api.Sandbox{}, err | ||||
| 	} | ||||
|  | ||||
| 	return inst, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxStore) validate(new *api.Sandbox) error { | ||||
| 	if err := identifiers.Validate(new.ID); err != nil { | ||||
| 		return errors.Wrap(err, "invalid sandbox ID") | ||||
| 	} | ||||
|  | ||||
| 	if new.CreatedAt.IsZero() { | ||||
| 		return errors.Wrap(errdefs.ErrInvalidArgument, "creation date must not be zero") | ||||
| 	} | ||||
|  | ||||
| 	if new.UpdatedAt.IsZero() { | ||||
| 		return errors.Wrap(errdefs.ErrInvalidArgument, "updated date must not be zero") | ||||
| 	} | ||||
|  | ||||
| 	if new.Runtime.Name == "" { | ||||
| 		return errors.Wrapf(errdefs.ErrInvalidArgument, "sandbox.Runtime.Name must be set") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										292
									
								
								metadata/sandbox_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								metadata/sandbox_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,292 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package metadata | ||||
|  | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/protobuf" | ||||
| 	api "github.com/containerd/containerd/sandbox" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/gogo/protobuf/types" | ||||
| ) | ||||
|  | ||||
| func TestSandboxCreate(t *testing.T) { | ||||
| 	ctx, db, done := testDB(t) | ||||
| 	defer done() | ||||
|  | ||||
| 	store := NewSandboxStore(db) | ||||
|  | ||||
| 	in := api.Sandbox{ | ||||
| 		ID:     "1", | ||||
| 		Labels: map[string]string{"a": "1", "b": "2"}, | ||||
| 		Spec:   &types.Any{TypeUrl: "1", Value: []byte{1, 2, 3}}, | ||||
| 		Extensions: map[string]typeurl.Any{ | ||||
| 			"ext1": &types.Any{TypeUrl: "url/1", Value: []byte{1, 2, 3}}, | ||||
| 			"ext2": &types.Any{TypeUrl: "url/2", Value: []byte{3, 2, 1}}, | ||||
| 		}, | ||||
| 		Runtime: api.RuntimeOpts{ | ||||
| 			Name:    "test", | ||||
| 			Options: &types.Any{TypeUrl: "url/3", Value: []byte{4, 5, 6}}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	_, err := store.Create(ctx, in) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	out, err := store.Get(ctx, "1") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	assertEqualInstances(t, in, out) | ||||
| } | ||||
|  | ||||
| func TestSandboxCreateDup(t *testing.T) { | ||||
| 	ctx, db, done := testDB(t) | ||||
| 	defer done() | ||||
|  | ||||
| 	store := NewSandboxStore(db) | ||||
|  | ||||
| 	in := api.Sandbox{ | ||||
| 		ID:      "1", | ||||
| 		Spec:    &types.Any{TypeUrl: "1", Value: []byte{1, 2, 3}}, | ||||
| 		Runtime: api.RuntimeOpts{Name: "test"}, | ||||
| 	} | ||||
|  | ||||
| 	_, err := store.Create(ctx, in) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	_, err = store.Create(ctx, in) | ||||
| 	if !errdefs.IsAlreadyExists(err) { | ||||
| 		t.Fatalf("expected %+v, got %+v", errdefs.ErrAlreadyExists, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSandboxUpdate(t *testing.T) { | ||||
| 	ctx, db, done := testDB(t) | ||||
| 	defer done() | ||||
|  | ||||
| 	store := NewSandboxStore(db) | ||||
|  | ||||
| 	if _, err := store.Create(ctx, api.Sandbox{ | ||||
| 		ID:     "2", | ||||
| 		Labels: map[string]string{"lbl1": "existing"}, | ||||
| 		Spec:   &types.Any{TypeUrl: "1", Value: []byte{1}}, // will replace | ||||
| 		Extensions: map[string]typeurl.Any{ | ||||
| 			"ext2": &types.Any{TypeUrl: "url2", Value: []byte{4, 5, 6}}, // will append `ext1` | ||||
| 		}, | ||||
| 		Runtime: api.RuntimeOpts{Name: "test"}, // no change | ||||
| 	}); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	expectedSpec := types.Any{TypeUrl: "2", Value: []byte{3, 2, 1}} | ||||
|  | ||||
| 	out, err := store.Update(ctx, api.Sandbox{ | ||||
| 		ID:     "2", | ||||
| 		Labels: map[string]string{"lbl1": "new"}, | ||||
| 		Spec:   &expectedSpec, | ||||
| 		Extensions: map[string]typeurl.Any{ | ||||
| 			"ext1": &types.Any{TypeUrl: "url1", Value: []byte{1, 2}}, | ||||
| 		}, | ||||
| 	}, "labels.lbl1", "extensions.ext1", "spec") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	expected := api.Sandbox{ | ||||
| 		ID:   "2", | ||||
| 		Spec: &expectedSpec, | ||||
| 		Labels: map[string]string{ | ||||
| 			"lbl1": "new", | ||||
| 		}, | ||||
| 		Extensions: map[string]typeurl.Any{ | ||||
| 			"ext1": &types.Any{TypeUrl: "url1", Value: []byte{1, 2}}, | ||||
| 			"ext2": &types.Any{TypeUrl: "url2", Value: []byte{4, 5, 6}}, | ||||
| 		}, | ||||
| 		Runtime: api.RuntimeOpts{Name: "test"}, | ||||
| 	} | ||||
|  | ||||
| 	assertEqualInstances(t, out, expected) | ||||
| } | ||||
|  | ||||
| func TestSandboxGetInvalid(t *testing.T) { | ||||
| 	ctx, db, done := testDB(t) | ||||
| 	defer done() | ||||
|  | ||||
| 	store := NewSandboxStore(db) | ||||
|  | ||||
| 	_, err := store.Get(ctx, "invalid_id") | ||||
| 	if err == nil { | ||||
| 		t.Fatalf("expected %+v error for invalid ID", errdefs.ErrNotFound) | ||||
| 	} else if !errdefs.IsNotFound(err) { | ||||
| 		t.Fatalf("unexpected error %T type", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSandboxList(t *testing.T) { | ||||
| 	ctx, db, done := testDB(t) | ||||
| 	defer done() | ||||
|  | ||||
| 	store := NewSandboxStore(db) | ||||
|  | ||||
| 	in := []api.Sandbox{ | ||||
| 		{ | ||||
| 			ID:         "1", | ||||
| 			Labels:     map[string]string{"test": "1"}, | ||||
| 			Spec:       &types.Any{TypeUrl: "1", Value: []byte{1, 2, 3}}, | ||||
| 			Extensions: map[string]typeurl.Any{"ext": &types.Any{}}, | ||||
| 			Runtime:    api.RuntimeOpts{Name: "test"}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			ID:     "2", | ||||
| 			Labels: map[string]string{"test": "2"}, | ||||
| 			Spec:   &types.Any{TypeUrl: "2", Value: []byte{3, 2, 1}}, | ||||
| 			Extensions: map[string]typeurl.Any{"ext": &types.Any{ | ||||
| 				TypeUrl: "test", | ||||
| 				Value:   []byte{9}, | ||||
| 			}}, | ||||
| 			Runtime: api.RuntimeOpts{Name: "test"}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, inst := range in { | ||||
| 		_, err := store.Create(ctx, inst) | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	out, err := store.List(ctx) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if len(in) != len(out) { | ||||
| 		t.Fatalf("expected list size: %d != %d", len(in), len(out)) | ||||
| 	} | ||||
|  | ||||
| 	for i := range out { | ||||
| 		assertEqualInstances(t, out[i], in[i]) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSandboxListWithFilter(t *testing.T) { | ||||
| 	ctx, db, done := testDB(t) | ||||
| 	defer done() | ||||
|  | ||||
| 	store := NewSandboxStore(db) | ||||
|  | ||||
| 	in := []api.Sandbox{ | ||||
| 		{ | ||||
| 			ID:         "1", | ||||
| 			Labels:     map[string]string{"test": "1"}, | ||||
| 			Spec:       &types.Any{TypeUrl: "1", Value: []byte{1, 2, 3}}, | ||||
| 			Extensions: map[string]typeurl.Any{"ext": &types.Any{}}, | ||||
| 			Runtime:    api.RuntimeOpts{Name: "test"}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			ID:     "2", | ||||
| 			Labels: map[string]string{"test": "2"}, | ||||
| 			Spec:   &types.Any{TypeUrl: "2", Value: []byte{3, 2, 1}}, | ||||
| 			Extensions: map[string]typeurl.Any{"ext": &types.Any{ | ||||
| 				TypeUrl: "test", | ||||
| 				Value:   []byte{9}, | ||||
| 			}}, | ||||
| 			Runtime: api.RuntimeOpts{Name: "test"}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, inst := range in { | ||||
| 		_, err := store.Create(ctx, inst) | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	out, err := store.List(ctx, "id==1") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if len(out) != 1 { | ||||
| 		t.Fatalf("expected list to contain 1 element, got %d", len(out)) | ||||
| 	} | ||||
|  | ||||
| 	assertEqualInstances(t, out[0], in[0]) | ||||
| } | ||||
|  | ||||
| func TestSandboxDelete(t *testing.T) { | ||||
| 	ctx, db, done := testDB(t) | ||||
| 	defer done() | ||||
|  | ||||
| 	store := NewSandboxStore(db) | ||||
|  | ||||
| 	in := api.Sandbox{ | ||||
| 		ID:      "2", | ||||
| 		Spec:    &types.Any{TypeUrl: "1", Value: []byte{1, 2, 3}}, | ||||
| 		Runtime: api.RuntimeOpts{Name: "test"}, | ||||
| 	} | ||||
|  | ||||
| 	_, err := store.Create(ctx, in) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	err = store.Delete(ctx, "2") | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("deleted failed %+v", err) | ||||
| 	} | ||||
|  | ||||
| 	_, err = store.Get(ctx, "2") | ||||
| 	if !errdefs.IsNotFound(err) { | ||||
| 		t.Fatalf("unexpected err result: %+v != %+v", err, errdefs.ErrNotFound) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func assertEqualInstances(t *testing.T, x, y api.Sandbox) { | ||||
| 	if x.ID != y.ID { | ||||
| 		t.Fatalf("ids are not equal: %q != %q", x.ID, y.ID) | ||||
| 	} | ||||
|  | ||||
| 	if !reflect.DeepEqual(x.Labels, y.Labels) { | ||||
| 		t.Fatalf("labels are not equal: %+v != %+v", x.Labels, y.Labels) | ||||
| 	} | ||||
|  | ||||
| 	if !reflect.DeepEqual(x.Spec, y.Spec) { | ||||
| 		t.Fatalf("specs are not equal: %+v != %+v", x.Spec, y.Spec) | ||||
| 	} | ||||
|  | ||||
| 	if !reflect.DeepEqual(x.Extensions, y.Extensions) { | ||||
| 		t.Fatalf("extensions are not equal: %+v != %+v", x.Extensions, y.Extensions) | ||||
| 	} | ||||
|  | ||||
| 	if x.Runtime.Name != y.Runtime.Name { | ||||
| 		t.Fatalf("runtime names are not equal: %q != %q", x.Runtime.Name, y.Runtime.Name) | ||||
| 	} | ||||
|  | ||||
| 	if !reflect.DeepEqual(protobuf.FromAny(x.Runtime.Options), protobuf.FromAny(y.Runtime.Options)) { | ||||
| 		t.Fatalf("runtime options are not equal: %+v != %+v", x.Runtime.Options, y.Runtime.Options) | ||||
| 	} | ||||
| } | ||||
| @@ -49,6 +49,8 @@ type CreateOpts struct { | ||||
| 	// Runtime name to use (e.g. `io.containerd.NAME.VERSION`). | ||||
| 	// As an alternative full abs path to binary may be specified instead. | ||||
| 	Runtime string | ||||
| 	// SandboxID is an optional ID of sandbox this container belongs to | ||||
| 	SandboxID string | ||||
| } | ||||
|  | ||||
| // Exit information for a process | ||||
|   | ||||
| @@ -25,6 +25,8 @@ import ( | ||||
| 	"github.com/containerd/containerd/identifiers" | ||||
| 	"github.com/containerd/containerd/mount" | ||||
| 	"github.com/containerd/containerd/namespaces" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/opencontainers/runtime-spec/specs-go" | ||||
| ) | ||||
|  | ||||
| const configFilename = "config.json" | ||||
| @@ -43,7 +45,7 @@ func LoadBundle(ctx context.Context, root, id string) (*Bundle, error) { | ||||
| } | ||||
|  | ||||
| // NewBundle returns a new bundle on disk | ||||
| func NewBundle(ctx context.Context, root, state, id string, spec []byte) (b *Bundle, err error) { | ||||
| func NewBundle(ctx context.Context, root, state, id string, spec typeurl.Any) (b *Bundle, err error) { | ||||
| 	if err := identifiers.Validate(id); err != nil { | ||||
| 		return nil, fmt.Errorf("invalid task id %s: %w", id, err) | ||||
| 	} | ||||
| @@ -73,8 +75,10 @@ func NewBundle(ctx context.Context, root, state, id string, spec []byte) (b *Bun | ||||
| 	if err := os.Mkdir(b.Path, 0700); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := prepareBundleDirectoryPermissions(b.Path, spec); err != nil { | ||||
| 		return nil, err | ||||
| 	if typeurl.Is(spec, &specs.Spec{}) { | ||||
| 		if err := prepareBundleDirectoryPermissions(b.Path, spec.GetValue()); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	paths = append(paths, b.Path) | ||||
| 	// create working directory for the bundle | ||||
| @@ -100,9 +104,14 @@ func NewBundle(ctx context.Context, root, state, id string, spec []byte) (b *Bun | ||||
| 	if err := os.Symlink(work, filepath.Join(b.Path, "work")); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	// write the spec to the bundle | ||||
| 	err = os.WriteFile(filepath.Join(b.Path, configFilename), spec, 0666) | ||||
| 	return b, err | ||||
| 	if spec := spec.GetValue(); spec != nil { | ||||
| 		// write the spec to the bundle | ||||
| 		err = os.WriteFile(filepath.Join(b.Path, configFilename), spec, 0666) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to write %s", configFilename) | ||||
| 		} | ||||
| 	} | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| // Bundle represents an OCI bundle | ||||
|   | ||||
| @@ -29,6 +29,7 @@ import ( | ||||
| 	"github.com/containerd/containerd/namespaces" | ||||
| 	"github.com/containerd/containerd/oci" | ||||
| 	"github.com/containerd/containerd/pkg/testutil" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/opencontainers/runtime-spec/specs-go" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| @@ -57,11 +58,11 @@ func TestNewBundle(t *testing.T) { | ||||
| 					GIDMappings: []specs.LinuxIDMapping{{ContainerID: 0, HostID: usernsGID}}, | ||||
| 				} | ||||
| 			} | ||||
| 			specBytes, err := json.Marshal(&spec) | ||||
| 			specAny, err := typeurl.MarshalAny(&spec) | ||||
| 			require.NoError(t, err, "failed to marshal spec") | ||||
|  | ||||
| 			ctx := namespaces.WithNamespace(context.TODO(), namespaces.Default) | ||||
| 			b, err := NewBundle(ctx, work, state, id, specBytes) | ||||
| 			b, err := NewBundle(ctx, work, state, id, specAny) | ||||
| 			require.NoError(t, err, "NewBundle should succeed") | ||||
| 			require.NotNil(t, b, "bundle should not be nil") | ||||
|  | ||||
|   | ||||
| @@ -96,6 +96,24 @@ func init() { | ||||
| 			return NewTaskManager(shimManager), nil | ||||
| 		}, | ||||
| 	}) | ||||
|  | ||||
| 	// Task manager uses shim manager as a dependency to manage shim instances. | ||||
| 	// However, due to time limits and to avoid migration steps in 1.6 release, | ||||
| 	// use the following workaround. | ||||
| 	// This expected to be removed in 1.7. | ||||
| 	plugin.Register(&plugin.Registration{ | ||||
| 		Type: plugin.RuntimePluginV2, | ||||
| 		ID:   "shim", | ||||
| 		InitFn: func(ic *plugin.InitContext) (interface{}, error) { | ||||
| 			taskManagerI, err := ic.GetByID(plugin.RuntimePluginV2, "task") | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			taskManager := taskManagerI.(*TaskManager) | ||||
| 			return taskManager.manager, nil | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| type ManagerConfig struct { | ||||
| @@ -158,7 +176,7 @@ func (m *ShimManager) ID() string { | ||||
|  | ||||
| // Start launches a new shim instance | ||||
| func (m *ShimManager) Start(ctx context.Context, id string, opts runtime.CreateOpts) (_ ShimProcess, retErr error) { | ||||
| 	bundle, err := NewBundle(ctx, m.root, m.state, id, opts.Spec.GetValue()) | ||||
| 	bundle, err := NewBundle(ctx, m.root, m.state, id, opts.Spec) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -168,6 +186,40 @@ func (m *ShimManager) Start(ctx context.Context, id string, opts runtime.CreateO | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// This container belongs to sandbox which supposed to be already started via sandbox API. | ||||
| 	if opts.SandboxID != "" { | ||||
| 		process, err := m.Get(ctx, opts.SandboxID) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("can't find sandbox %s", opts.SandboxID) | ||||
| 		} | ||||
|  | ||||
| 		// Write sandbox ID this task belongs to. | ||||
| 		if err := os.WriteFile(filepath.Join(bundle.Path, "sandbox"), []byte(opts.SandboxID), 0600); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		address, err := shimbinary.ReadAddress(filepath.Join(m.state, process.Namespace(), opts.SandboxID, "address")) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to get socket address for sandbox %q: %w", opts.SandboxID, err) | ||||
| 		} | ||||
|  | ||||
| 		// Use sandbox's socket address to handle task requests for this container. | ||||
| 		if err := shimbinary.WriteAddress(filepath.Join(bundle.Path, "address"), address); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		shim, err := loadShim(ctx, bundle, func() {}) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("failed to load sandbox task %q: %w", opts.SandboxID, err) | ||||
| 		} | ||||
|  | ||||
| 		if err := m.shims.Add(ctx, shim); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		return shim, nil | ||||
| 	} | ||||
|  | ||||
| 	shim, err := m.startShim(ctx, bundle, id, opts) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| @@ -324,7 +376,8 @@ func (m *ShimManager) Get(ctx context.Context, id string) (ShimProcess, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return proc, nil | ||||
| 	shimTask := proc.(*shimTask) | ||||
| 	return shimTask, nil | ||||
| } | ||||
|  | ||||
| // Delete a runtime task | ||||
| @@ -388,7 +441,8 @@ func (m *TaskManager) Create(ctx context.Context, taskID string, opts runtime.Cr | ||||
| 		dctx, cancel := timeout.WithContext(context.Background(), cleanupTimeout) | ||||
| 		defer cancel() | ||||
|  | ||||
| 		_, errShim := shim.delete(dctx, func(context.Context, string) {}) | ||||
| 		sandboxed := opts.SandboxID != "" | ||||
| 		_, errShim := shim.delete(dctx, sandboxed, func(context.Context, string) {}) | ||||
| 		if errShim != nil { | ||||
| 			if errdefs.IsDeadlineExceeded(errShim) { | ||||
| 				dctx, cancel = timeout.WithContext(context.Background(), cleanupTimeout) | ||||
| @@ -422,8 +476,14 @@ func (m *TaskManager) Delete(ctx context.Context, taskID string) (*runtime.Exit, | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	container, err := m.manager.containers.Get(ctx, taskID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	sandboxed := container.SandboxID != "" | ||||
| 	shimTask := item.(*shimTask) | ||||
| 	exit, err := shimTask.delete(ctx, func(ctx context.Context, id string) { | ||||
| 	exit, err := shimTask.delete(ctx, sandboxed, func(ctx context.Context, id string) { | ||||
| 		m.manager.shims.Delete(ctx, id) | ||||
| 	}) | ||||
|  | ||||
|   | ||||
							
								
								
									
										104
									
								
								runtime/v2/runc/pause/sandbox.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								runtime/v2/runc/pause/sandbox.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| //go:build linux | ||||
| // +build linux | ||||
|  | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package pause | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"github.com/containerd/containerd/pkg/shutdown" | ||||
| 	"github.com/containerd/ttrpc" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
|  | ||||
| 	"github.com/containerd/containerd/plugin" | ||||
| 	api "github.com/containerd/containerd/runtime/v2/task" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	plugin.Register(&plugin.Registration{ | ||||
| 		Type: plugin.TTRPCPlugin, | ||||
| 		ID:   "pause", | ||||
| 		Requires: []plugin.Type{ | ||||
| 			plugin.InternalPlugin, | ||||
| 		}, | ||||
| 		InitFn: func(ic *plugin.InitContext) (interface{}, error) { | ||||
| 			ss, err := ic.GetByID(plugin.InternalPlugin, "shutdown") | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			return &pauseService{ | ||||
| 				shutdown: ss.(shutdown.Service), | ||||
| 			}, nil | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // pauseService is an extension for task v2 runtime to support Pod "pause" containers via sandbox API. | ||||
| type pauseService struct { | ||||
| 	shutdown shutdown.Service | ||||
| } | ||||
|  | ||||
| var _ api.SandboxService = (*pauseService)(nil) | ||||
|  | ||||
| func (p *pauseService) RegisterTTRPC(server *ttrpc.Server) error { | ||||
| 	api.RegisterSandboxService(server, p) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (p *pauseService) StartSandbox(ctx context.Context, req *api.StartSandboxRequest) (*api.StartSandboxResponse, error) { | ||||
| 	log.Debugf("start sandbox request: %+v", req) | ||||
| 	return &api.StartSandboxResponse{}, nil | ||||
| } | ||||
|  | ||||
| func (p *pauseService) StopSandbox(ctx context.Context, req *api.StopSandboxRequest) (*api.StopSandboxResponse, error) { | ||||
| 	log.Debugf("stop sandbox request: %+v", req) | ||||
| 	p.shutdown.Shutdown() | ||||
| 	return &api.StopSandboxResponse{}, nil | ||||
| } | ||||
|  | ||||
| func (p *pauseService) WaitSandbox(ctx context.Context, req *api.WaitSandboxRequest) (*api.WaitSandboxResponse, error) { | ||||
| 	return &api.WaitSandboxResponse{ | ||||
| 		ExitStatus: 0, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (p *pauseService) UpdateSandbox(ctx context.Context, req *api.UpdateSandboxRequest) (*api.UpdateSandboxResponse, error) { | ||||
| 	log.Debugf("update sandbox request: %+v", req) | ||||
| 	return &api.UpdateSandboxResponse{}, nil | ||||
| } | ||||
|  | ||||
| func (p *pauseService) PauseSandbox(ctx context.Context, req *api.PauseSandboxRequest) (*api.PauseSandboxResponse, error) { | ||||
| 	log.Debugf("pause sandbox request: %+v", req) | ||||
| 	return &api.PauseSandboxResponse{}, nil | ||||
| } | ||||
|  | ||||
| func (p *pauseService) ResumeSandbox(ctx context.Context, req *api.ResumeSandboxRequest) (*api.ResumeSandboxResponse, error) { | ||||
| 	log.Debugf("resume sandbox request: %+v", req) | ||||
| 	return &api.ResumeSandboxResponse{}, nil | ||||
| } | ||||
|  | ||||
| func (p *pauseService) SandboxStatus(ctx context.Context, req *api.SandboxStatusRequest) (*api.SandboxStatusResponse, error) { | ||||
| 	log.Debugf("sandbox status request: %+v", req) | ||||
| 	return &api.SandboxStatusResponse{}, nil | ||||
| } | ||||
|  | ||||
| func (p *pauseService) PingSandbox(ctx context.Context, req *api.PingRequest) (*api.PingResponse, error) { | ||||
| 	return &api.PingResponse{}, nil | ||||
| } | ||||
| @@ -194,6 +194,10 @@ type ShimProcess interface { | ||||
| 	ID() string | ||||
| 	// Namespace of this shim. | ||||
| 	Namespace() string | ||||
| 	// Bundle is a file system path to shim's bundle. | ||||
| 	Bundle() string | ||||
| 	// Client returns the underlying TTRPC client for this shim. | ||||
| 	Client() *ttrpc.Client | ||||
| } | ||||
|  | ||||
| type shim struct { | ||||
| @@ -210,6 +214,10 @@ func (s *shim) Namespace() string { | ||||
| 	return s.bundle.Namespace | ||||
| } | ||||
|  | ||||
| func (s *shim) Bundle() string { | ||||
| 	return s.bundle.Path | ||||
| } | ||||
|  | ||||
| func (s *shim) Close() error { | ||||
| 	return s.client.Close() | ||||
| } | ||||
| @@ -243,6 +251,10 @@ type shimTask struct { | ||||
| 	task task.TaskService | ||||
| } | ||||
|  | ||||
| func (s *shimTask) Client() *ttrpc.Client { | ||||
| 	return s.client | ||||
| } | ||||
|  | ||||
| func (s *shimTask) Shutdown(ctx context.Context) error { | ||||
| 	_, err := s.task.Shutdown(ctx, &task.ShutdownRequest{ | ||||
| 		ID: s.ID(), | ||||
| @@ -271,7 +283,7 @@ func (s *shimTask) PID(ctx context.Context) (uint32, error) { | ||||
| 	return response.TaskPid, nil | ||||
| } | ||||
|  | ||||
| func (s *shimTask) delete(ctx context.Context, removeTask func(ctx context.Context, id string)) (*runtime.Exit, error) { | ||||
| func (s *shimTask) delete(ctx context.Context, sandboxed bool, removeTask func(ctx context.Context, id string)) (*runtime.Exit, error) { | ||||
| 	response, shimErr := s.task.Delete(ctx, &task.DeleteRequest{ | ||||
| 		ID: s.ID(), | ||||
| 	}) | ||||
| @@ -299,8 +311,12 @@ func (s *shimTask) delete(ctx context.Context, removeTask func(ctx context.Conte | ||||
| 		removeTask(ctx, s.ID()) | ||||
| 	} | ||||
|  | ||||
| 	if err := s.waitShutdown(ctx); err != nil { | ||||
| 		log.G(ctx).WithField("id", s.ID()).WithError(err).Error("failed to shutdown shim task") | ||||
| 	// Don't shutdown sandbox as there may be other containers running. | ||||
| 	// Let controller decide when to shutdown. | ||||
| 	if !sandboxed { | ||||
| 		if err := s.waitShutdown(ctx); err != nil { | ||||
| 			log.G(ctx).WithField("id", s.ID()).WithError(err).Error("failed to shutdown shim task") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err := s.shim.delete(ctx); err != nil { | ||||
|   | ||||
							
								
								
									
										3694
									
								
								runtime/v2/task/sandbox.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3694
									
								
								runtime/v2/task/sandbox.pb.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										121
									
								
								runtime/v2/task/sandbox.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								runtime/v2/task/sandbox.proto
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| /* | ||||
| 	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.task.v2; | ||||
|  | ||||
| import "google/protobuf/any.proto"; | ||||
| import "google/protobuf/timestamp.proto"; | ||||
| import weak "gogoproto/gogo.proto"; | ||||
|  | ||||
| import "github.com/containerd/containerd/api/types/mount.proto"; | ||||
|  | ||||
| // Sandbox is an optional interface that shim may implement to support sandboxes environments. | ||||
| // A typical example of sandbox is microVM or pause container - an entity that groups containers and/or | ||||
| // holds resources relevant for this group. | ||||
| service Sandbox { | ||||
| 	// StartSandbox will create/start a new sandbox instance | ||||
| 	rpc StartSandbox(StartSandboxRequest) returns (StartSandboxResponse); | ||||
|  | ||||
| 	// StopSandbox will stop existing sandbox instance | ||||
| 	rpc StopSandbox(StopSandboxRequest) returns (StopSandboxResponse); | ||||
|  | ||||
| 	// WaitSandbox blocks until sanbox exits. | ||||
| 	rpc WaitSandbox(WaitSandboxRequest) returns (WaitSandboxResponse); | ||||
|  | ||||
| 	// Update can be used to amend the state of currently running sandbox instance (depending on | ||||
| 	// implementation this can be used to resize/reacquire needed resources like RAM/CPU). | ||||
| 	rpc UpdateSandbox(UpdateSandboxRequest) returns (UpdateSandboxResponse); | ||||
|  | ||||
| 	// PauseSandbox will suspend currently running sandbox instance. | ||||
| 	rpc PauseSandbox(PauseSandboxRequest) returns (PauseSandboxResponse); | ||||
|  | ||||
| 	// ResumeSandbox will resuyme previously suspended sandbox instance. | ||||
| 	rpc ResumeSandbox(ResumeSandboxRequest) returns (ResumeSandboxResponse); | ||||
|  | ||||
| 	// SandboxStatus will return current status of the running sandbox instance | ||||
| 	rpc SandboxStatus(SandboxStatusRequest) returns (SandboxStatusResponse); | ||||
|  | ||||
| 	// PingSandbox is a lightweight API call to check whether sandbox alive. | ||||
| 	rpc PingSandbox(PingRequest) returns (PingResponse); | ||||
| } | ||||
|  | ||||
| message StartSandboxRequest { | ||||
| 	string sandbox_id = 1; | ||||
| 	string bundle_path = 2; | ||||
| 	repeated containerd.types.Mount rootfs = 3; | ||||
| 	google.protobuf.Any options = 4; | ||||
| } | ||||
|  | ||||
| message StartSandboxResponse { | ||||
| 	uint32 pid = 1; | ||||
| } | ||||
|  | ||||
| message StopSandboxRequest { | ||||
| 	string sandbox_id = 1; | ||||
| 	uint32 timeout_secs = 2; | ||||
| } | ||||
|  | ||||
| message StopSandboxResponse {} | ||||
|  | ||||
| message UpdateSandboxRequest { | ||||
| 	string sandbox_id = 1; | ||||
| 	google.protobuf.Any resources = 2; | ||||
| 	map<string, string> annotations = 3; | ||||
| } | ||||
|  | ||||
| message WaitSandboxRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message WaitSandboxResponse { | ||||
| 	uint32 exit_status = 1; | ||||
| 	google.protobuf.Timestamp exited_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | ||||
| } | ||||
|  | ||||
| message UpdateSandboxResponse {} | ||||
|  | ||||
| message SandboxStatusRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message PauseSandboxRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message PauseSandboxResponse {} | ||||
|  | ||||
| message ResumeSandboxRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message ResumeSandboxResponse {} | ||||
|  | ||||
| message SandboxStatusResponse { | ||||
| 	string id = 1; | ||||
| 	uint32 pid = 2; | ||||
| 	string state = 3; | ||||
| 	uint32 exit_status = 4; | ||||
| 	google.protobuf.Timestamp exited_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; | ||||
| 	google.protobuf.Any extra = 6; | ||||
| } | ||||
|  | ||||
| message PingRequest { | ||||
| 	string sandbox_id = 1; | ||||
| } | ||||
|  | ||||
| message PingResponse {} | ||||
							
								
								
									
										239
									
								
								sandbox.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								sandbox.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,239 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package containerd | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/containerd/containers" | ||||
| 	"github.com/containerd/containerd/oci" | ||||
| 	api "github.com/containerd/containerd/sandbox" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	"github.com/gogo/protobuf/types" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| // Sandbox is a high level client to containerd's sandboxes. | ||||
| type Sandbox interface { | ||||
| 	// ID is a sandbox identifier | ||||
| 	ID() string | ||||
| 	// PID returns sandbox's process PID or error if its not yet started. | ||||
| 	PID() (uint32, error) | ||||
| 	// NewContainer creates new container that will belong to this sandbox | ||||
| 	NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) | ||||
| 	// Labels returns the labels set on the sandbox | ||||
| 	Labels(ctx context.Context) (map[string]string, error) | ||||
| 	// Start starts new sandbox instance | ||||
| 	Start(ctx context.Context) error | ||||
| 	// Stop sends stop request to the shim instance. | ||||
| 	Stop(ctx context.Context) error | ||||
| 	// Wait blocks until sandbox process exits. | ||||
| 	Wait(ctx context.Context) (<-chan ExitStatus, error) | ||||
| 	// Delete removes sandbox from the metadata store. | ||||
| 	Delete(ctx context.Context) error | ||||
| } | ||||
|  | ||||
| type sandboxClient struct { | ||||
| 	pid      *uint32 | ||||
| 	client   *Client | ||||
| 	metadata api.Sandbox | ||||
| } | ||||
|  | ||||
| func (s *sandboxClient) ID() string { | ||||
| 	return s.metadata.ID | ||||
| } | ||||
|  | ||||
| func (s *sandboxClient) PID() (uint32, error) { | ||||
| 	if s.pid == nil { | ||||
| 		return 0, fmt.Errorf("sandbox not started") | ||||
| 	} | ||||
|  | ||||
| 	return *s.pid, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxClient) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error) { | ||||
| 	return s.client.NewContainer(ctx, id, append(opts, WithSandbox(s.ID()))...) | ||||
| } | ||||
|  | ||||
| func (s *sandboxClient) Labels(ctx context.Context) (map[string]string, error) { | ||||
| 	sandbox, err := s.client.SandboxStore().Get(ctx, s.ID()) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return sandbox.Labels, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxClient) Start(ctx context.Context) error { | ||||
| 	pid, err := s.client.SandboxController().Start(ctx, s.ID()) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	s.pid = &pid | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxClient) Wait(ctx context.Context) (<-chan ExitStatus, error) { | ||||
| 	c := make(chan ExitStatus, 1) | ||||
| 	go func() { | ||||
| 		defer close(c) | ||||
|  | ||||
| 		resp, err := s.client.SandboxController().Wait(ctx, s.ID()) | ||||
| 		if err != nil { | ||||
| 			c <- ExitStatus{ | ||||
| 				code: UnknownExitStatus, | ||||
| 				err:  err, | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		c <- ExitStatus{ | ||||
| 			code:     resp.ExitStatus, | ||||
| 			exitedAt: resp.ExitedAt, | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return c, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxClient) Stop(ctx context.Context) error { | ||||
| 	return s.client.SandboxController().Shutdown(ctx, s.ID()) | ||||
| } | ||||
|  | ||||
| func (s *sandboxClient) Delete(ctx context.Context) error { | ||||
| 	return s.client.SandboxStore().Delete(ctx, s.ID()) | ||||
| } | ||||
|  | ||||
| // NewSandbox creates new sandbox client | ||||
| func (c *Client) NewSandbox(ctx context.Context, sandboxID string, opts ...NewSandboxOpts) (Sandbox, error) { | ||||
| 	if sandboxID == "" { | ||||
| 		return nil, errors.New("sandbox ID must be specified") | ||||
| 	} | ||||
|  | ||||
| 	newSandbox := api.Sandbox{ | ||||
| 		ID:        sandboxID, | ||||
| 		CreatedAt: time.Now().UTC(), | ||||
| 		UpdatedAt: time.Now().UTC(), | ||||
| 	} | ||||
|  | ||||
| 	for _, opt := range opts { | ||||
| 		if err := opt(ctx, c, &newSandbox); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	metadata, err := c.SandboxStore().Create(ctx, newSandbox) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &sandboxClient{ | ||||
| 		pid:      nil, // Not yet started | ||||
| 		client:   c, | ||||
| 		metadata: metadata, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // LoadSandbox laods existing sandbox metadata object using the id | ||||
| func (c *Client) LoadSandbox(ctx context.Context, id string) (Sandbox, error) { | ||||
| 	sandbox, err := c.SandboxStore().Get(ctx, id) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	status, err := c.SandboxController().Status(ctx, id) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to load sandbox %s, status request failed: %w", id, err) | ||||
| 	} | ||||
|  | ||||
| 	return &sandboxClient{ | ||||
| 		pid:      &status.Pid, | ||||
| 		client:   c, | ||||
| 		metadata: sandbox, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // NewSandboxOpts is a sandbox options and extensions to be provided by client | ||||
| type NewSandboxOpts func(ctx context.Context, client *Client, sandbox *api.Sandbox) error | ||||
|  | ||||
| // WithSandboxRuntime allows a user to specify the runtime to be used to run a sandbox | ||||
| func WithSandboxRuntime(name string, options interface{}) NewSandboxOpts { | ||||
| 	return func(ctx context.Context, client *Client, s *api.Sandbox) error { | ||||
| 		if options == nil { | ||||
| 			options = &types.Empty{} | ||||
| 		} | ||||
|  | ||||
| 		opts, err := typeurl.MarshalAny(options) | ||||
| 		if err != nil { | ||||
| 			return errors.Wrap(err, "failed to marshal sandbox runtime options") | ||||
| 		} | ||||
|  | ||||
| 		s.Runtime = api.RuntimeOpts{ | ||||
| 			Name:    name, | ||||
| 			Options: opts, | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithSandboxSpec will provide the sandbox runtime spec | ||||
| func WithSandboxSpec(s *oci.Spec, opts ...oci.SpecOpts) NewSandboxOpts { | ||||
| 	return func(ctx context.Context, client *Client, sandbox *api.Sandbox) error { | ||||
| 		c := &containers.Container{ID: sandbox.ID} | ||||
|  | ||||
| 		if err := oci.ApplyOpts(ctx, client, c, s, opts...); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		spec, err := typeurl.MarshalAny(s) | ||||
| 		if err != nil { | ||||
| 			return errors.Wrap(err, "failed to marshal spec") | ||||
| 		} | ||||
|  | ||||
| 		sandbox.Spec = spec | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithSandboxExtension attaches an extension to sandbox | ||||
| func WithSandboxExtension(name string, ext interface{}) NewSandboxOpts { | ||||
| 	return func(ctx context.Context, client *Client, s *api.Sandbox) error { | ||||
| 		if s.Extensions == nil { | ||||
| 			s.Extensions = make(map[string]typeurl.Any) | ||||
| 		} | ||||
|  | ||||
| 		any, err := typeurl.MarshalAny(ext) | ||||
| 		if err != nil { | ||||
| 			return errors.Wrap(err, "failed to marshal sandbox extension") | ||||
| 		} | ||||
|  | ||||
| 		s.Extensions[name] = any | ||||
| 		return err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithSandboxLabels attaches map of labels to sandbox | ||||
| func WithSandboxLabels(labels map[string]string) NewSandboxOpts { | ||||
| 	return func(ctx context.Context, client *Client, sandbox *api.Sandbox) error { | ||||
| 		sandbox.Labels = labels | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										41
									
								
								sandbox/controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								sandbox/controller.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandbox | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| ) | ||||
|  | ||||
| // Controller is an interface to manage sandboxes at runtime. | ||||
| // When running in sandbox mode, shim expected to implement `SandboxService`. | ||||
| // Shim lifetimes are now managed manually via sandbox API by the containerd's client. | ||||
| type Controller interface { | ||||
| 	// Start will start new sandbox instance. | ||||
| 	// containerd will run new shim runtime instance and will invoke Start to create a sandbox process. | ||||
| 	// This routine must be invoked before scheduling containers on this instance. | ||||
| 	// Once started clients may run containers via Task service (additionally specifying sandbox id the container will belong to). | ||||
| 	Start(ctx context.Context, sandboxID string) (uint32, error) | ||||
| 	// Shutdown deletes and cleans all tasks and sandbox instance. | ||||
| 	Shutdown(ctx context.Context, sandboxID string) error | ||||
| 	// Wait blocks until sandbox process exits. | ||||
| 	Wait(ctx context.Context, sandboxID string) (*sandbox.ControllerWaitResponse, error) | ||||
| 	// Status will query sandbox process status. It is heavier than Ping call and must be used whenever you need to | ||||
| 	// gather metadata about current sandbox state (status, uptime, resource use, etc). | ||||
| 	Status(ctx context.Context, sandboxID string) (*sandbox.ControllerStatusResponse, error) | ||||
| } | ||||
							
								
								
									
										68
									
								
								sandbox/helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								sandbox/helpers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandbox | ||||
|  | ||||
| import ( | ||||
| 	"github.com/containerd/containerd/api/types" | ||||
| 	"github.com/containerd/containerd/protobuf" | ||||
| 	"github.com/containerd/typeurl" | ||||
| 	gogo_types "github.com/gogo/protobuf/types" | ||||
| ) | ||||
|  | ||||
| // ToProto will map Sandbox struct to it's protobuf definition | ||||
| func ToProto(sandbox *Sandbox) types.Sandbox { | ||||
| 	extensions := make(map[string]gogo_types.Any) | ||||
| 	for k, v := range sandbox.Extensions { | ||||
| 		extensions[k] = *protobuf.FromAny(v) | ||||
| 	} | ||||
| 	return types.Sandbox{ | ||||
| 		SandboxID: sandbox.ID, | ||||
| 		Runtime: types.Sandbox_Runtime{ | ||||
| 			Name:    sandbox.Runtime.Name, | ||||
| 			Options: protobuf.FromAny(sandbox.Runtime.Options), | ||||
| 		}, | ||||
| 		Labels:     sandbox.Labels, | ||||
| 		CreatedAt:  sandbox.CreatedAt, | ||||
| 		UpdatedAt:  sandbox.UpdatedAt, | ||||
| 		Extensions: extensions, | ||||
| 		Spec:       protobuf.FromAny(sandbox.Spec), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // FromProto map protobuf sandbox definition to Sandbox struct | ||||
| func FromProto(sandboxpb *types.Sandbox) Sandbox { | ||||
| 	runtime := RuntimeOpts{ | ||||
| 		Name:    sandboxpb.Runtime.Name, | ||||
| 		Options: sandboxpb.Runtime.Options, | ||||
| 	} | ||||
|  | ||||
| 	extensions := make(map[string]typeurl.Any) | ||||
| 	for k, v := range sandboxpb.Extensions { | ||||
| 		v := v | ||||
| 		extensions[k] = &v | ||||
| 	} | ||||
|  | ||||
| 	return Sandbox{ | ||||
| 		ID:         sandboxpb.SandboxID, | ||||
| 		Labels:     sandboxpb.Labels, | ||||
| 		Runtime:    runtime, | ||||
| 		Spec:       sandboxpb.Spec, | ||||
| 		CreatedAt:  sandboxpb.CreatedAt, | ||||
| 		UpdatedAt:  sandboxpb.UpdatedAt, | ||||
| 		Extensions: extensions, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										66
									
								
								sandbox/store.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								sandbox/store.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandbox | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/containerd/typeurl" | ||||
| ) | ||||
|  | ||||
| // Sandbox is an object stored in metadata database | ||||
| type Sandbox struct { | ||||
| 	// ID uniquely identifies the sandbox in a namespace | ||||
| 	ID string | ||||
| 	// Labels provide metadata extension for a sandbox | ||||
| 	Labels map[string]string | ||||
| 	// Runtime shim to use for this sandbox | ||||
| 	Runtime RuntimeOpts | ||||
| 	// Spec carries the runtime specification used to implement the sandbox | ||||
| 	Spec typeurl.Any | ||||
| 	// CreatedAt is the time at which the sandbox was created | ||||
| 	CreatedAt time.Time | ||||
| 	// UpdatedAt is the time at which the sandbox was updated | ||||
| 	UpdatedAt time.Time | ||||
| 	// Extensions stores client-specified metadata | ||||
| 	Extensions map[string]typeurl.Any | ||||
| } | ||||
|  | ||||
| // RuntimeOpts holds runtime specific information | ||||
| type RuntimeOpts struct { | ||||
| 	Name    string | ||||
| 	Options typeurl.Any | ||||
| } | ||||
|  | ||||
| // Store is a storage interface for sandbox metadata objects | ||||
| type Store interface { | ||||
| 	// Create a sandbox record in the store | ||||
| 	Create(ctx context.Context, sandbox Sandbox) (Sandbox, error) | ||||
|  | ||||
| 	// Update the sandbox with the provided sandbox object and fields | ||||
| 	Update(ctx context.Context, sandbox Sandbox, fieldpaths ...string) (Sandbox, error) | ||||
|  | ||||
| 	// Get sandbox metadata using the id | ||||
| 	Get(ctx context.Context, id string) (Sandbox, error) | ||||
|  | ||||
| 	// List returns sandboxes that match one or more of the provided filters | ||||
| 	List(ctx context.Context, filters ...string) ([]Sandbox, error) | ||||
|  | ||||
| 	// Delete a sandbox from metadata store using the id | ||||
| 	Delete(ctx context.Context, id string) error | ||||
| } | ||||
							
								
								
									
										73
									
								
								sandbox_controller.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								sandbox_controller.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package containerd | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	api "github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	sb "github.com/containerd/containerd/sandbox" | ||||
| ) | ||||
|  | ||||
| // sandboxRemoteController is a low level GRPC client for containerd's sandbox controller service | ||||
| type sandboxRemoteController struct { | ||||
| 	client api.ControllerClient | ||||
| } | ||||
|  | ||||
| var _ sb.Controller = (*sandboxRemoteController)(nil) | ||||
|  | ||||
| // NewSandboxRemoteController creates client for sandbox controller | ||||
| func NewSandboxRemoteController(client api.ControllerClient) sb.Controller { | ||||
| 	return &sandboxRemoteController{client: client} | ||||
| } | ||||
|  | ||||
| func (s *sandboxRemoteController) Start(ctx context.Context, sandboxID string) (uint32, error) { | ||||
| 	resp, err := s.client.Start(ctx, &api.ControllerStartRequest{SandboxID: sandboxID}) | ||||
| 	if err != nil { | ||||
| 		return 0, errdefs.FromGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return resp.Pid, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxRemoteController) Shutdown(ctx context.Context, sandboxID string) error { | ||||
| 	_, err := s.client.Shutdown(ctx, &api.ControllerShutdownRequest{SandboxID: sandboxID}) | ||||
| 	if err != nil { | ||||
| 		return errdefs.FromGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxRemoteController) Wait(ctx context.Context, sandboxID string) (*api.ControllerWaitResponse, error) { | ||||
| 	resp, err := s.client.Wait(ctx, &api.ControllerWaitRequest{SandboxID: sandboxID}) | ||||
| 	if err != nil { | ||||
| 		return nil, errdefs.FromGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxRemoteController) Status(ctx context.Context, sandboxID string) (*api.ControllerStatusResponse, error) { | ||||
| 	resp, err := s.client.Status(ctx, &api.ControllerStatusRequest{SandboxID: sandboxID}) | ||||
| 	if err != nil { | ||||
| 		return nil, errdefs.FromGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return resp, nil | ||||
| } | ||||
							
								
								
									
										95
									
								
								sandbox_store.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								sandbox_store.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package containerd | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	api "github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	sb "github.com/containerd/containerd/sandbox" | ||||
| ) | ||||
|  | ||||
| // remoteSandboxStore is a low-level containerd client to manage sandbox environments metadata | ||||
| type remoteSandboxStore struct { | ||||
| 	client api.StoreClient | ||||
| } | ||||
|  | ||||
| var _ sb.Store = (*remoteSandboxStore)(nil) | ||||
|  | ||||
| // NewRemoteSandboxStore create client for sandbox store | ||||
| func NewRemoteSandboxStore(client api.StoreClient) sb.Store { | ||||
| 	return &remoteSandboxStore{client: client} | ||||
| } | ||||
|  | ||||
| func (s *remoteSandboxStore) Create(ctx context.Context, sandbox sb.Sandbox) (sb.Sandbox, error) { | ||||
| 	resp, err := s.client.Create(ctx, &api.StoreCreateRequest{ | ||||
| 		Sandbox: sb.ToProto(&sandbox), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return sb.Sandbox{}, errdefs.FromGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return sb.FromProto(&resp.Sandbox), nil | ||||
| } | ||||
|  | ||||
| func (s *remoteSandboxStore) Update(ctx context.Context, sandbox sb.Sandbox, fieldpaths ...string) (sb.Sandbox, error) { | ||||
| 	resp, err := s.client.Update(ctx, &api.StoreUpdateRequest{ | ||||
| 		Sandbox: sb.ToProto(&sandbox), | ||||
| 		Fields:  fieldpaths, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return sb.Sandbox{}, errdefs.FromGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return sb.FromProto(&resp.Sandbox), nil | ||||
| } | ||||
|  | ||||
| func (s *remoteSandboxStore) Get(ctx context.Context, id string) (sb.Sandbox, error) { | ||||
| 	resp, err := s.client.Get(ctx, &api.StoreGetRequest{ | ||||
| 		SandboxID: id, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return sb.Sandbox{}, errdefs.FromGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return sb.FromProto(resp.Sandbox), nil | ||||
| } | ||||
|  | ||||
| func (s *remoteSandboxStore) List(ctx context.Context, filters ...string) ([]sb.Sandbox, error) { | ||||
| 	resp, err := s.client.List(ctx, &api.StoreListRequest{ | ||||
| 		Filters: filters, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, errdefs.FromGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	out := make([]sb.Sandbox, len(resp.List)) | ||||
| 	for i := range resp.List { | ||||
| 		out[i] = sb.FromProto(&resp.List[i]) | ||||
| 	} | ||||
|  | ||||
| 	return out, nil | ||||
| } | ||||
|  | ||||
| func (s *remoteSandboxStore) Delete(ctx context.Context, id string) error { | ||||
| 	_, err := s.client.Delete(ctx, &api.StoreDeleteRequest{ | ||||
| 		SandboxID: id, | ||||
| 	}) | ||||
|  | ||||
| 	return errdefs.FromGRPC(err) | ||||
| } | ||||
							
								
								
									
										18
									
								
								services.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								services.go
									
									
									
									
									
								
							| @@ -22,12 +22,14 @@ import ( | ||||
| 	imagesapi "github.com/containerd/containerd/api/services/images/v1" | ||||
| 	introspectionapi "github.com/containerd/containerd/api/services/introspection/v1" | ||||
| 	namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1" | ||||
| 	sandboxsapi "github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| 	"github.com/containerd/containerd/api/services/tasks/v1" | ||||
| 	"github.com/containerd/containerd/containers" | ||||
| 	"github.com/containerd/containerd/content" | ||||
| 	"github.com/containerd/containerd/images" | ||||
| 	"github.com/containerd/containerd/leases" | ||||
| 	"github.com/containerd/containerd/namespaces" | ||||
| 	"github.com/containerd/containerd/sandbox" | ||||
| 	"github.com/containerd/containerd/services/introspection" | ||||
| 	"github.com/containerd/containerd/snapshots" | ||||
| ) | ||||
| @@ -43,6 +45,8 @@ type services struct { | ||||
| 	eventService         EventService | ||||
| 	leasesService        leases.Manager | ||||
| 	introspectionService introspection.Service | ||||
| 	sandboxStore         sandbox.Store | ||||
| 	sandboxController    sandbox.Controller | ||||
| } | ||||
|  | ||||
| // ServicesOpt allows callers to set options on the services | ||||
| @@ -155,3 +159,17 @@ func WithIntrospectionService(in introspection.Service) ServicesOpt { | ||||
| 		s.introspectionService = in | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithSandboxStore sets the sandbox store. | ||||
| func WithSandboxStore(client sandboxsapi.StoreClient) ServicesOpt { | ||||
| 	return func(s *services) { | ||||
| 		s.sandboxStore = NewRemoteSandboxStore(client) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithSandboxController sets the sandbox controller. | ||||
| func WithSandboxController(client sandboxsapi.ControllerClient) ServicesOpt { | ||||
| 	return func(s *services) { | ||||
| 		s.sandboxController = NewSandboxRemoteController(client) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -54,6 +54,7 @@ func containerToProto(container *containers.Container) api.Container { | ||||
| 		CreatedAt:   container.CreatedAt, | ||||
| 		UpdatedAt:   container.UpdatedAt, | ||||
| 		Extensions:  extensions, | ||||
| 		Sandbox:     container.SandboxID, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -79,5 +80,6 @@ func containerFromProto(containerpb *api.Container) containers.Container { | ||||
| 		Snapshotter: containerpb.Snapshotter, | ||||
| 		SnapshotKey: containerpb.SnapshotKey, | ||||
| 		Extensions:  extensions, | ||||
| 		SandboxID:   containerpb.Sandbox, | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										196
									
								
								services/sandbox/controller_local.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								services/sandbox/controller_local.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandbox | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
|  | ||||
| 	api "github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/events" | ||||
| 	"github.com/containerd/containerd/events/exchange" | ||||
| 	"github.com/containerd/containerd/metadata" | ||||
| 	"github.com/containerd/containerd/plugin" | ||||
| 	"github.com/containerd/containerd/runtime" | ||||
| 	v2 "github.com/containerd/containerd/runtime/v2" | ||||
| 	"github.com/containerd/containerd/runtime/v2/task" | ||||
| 	proto "github.com/containerd/containerd/runtime/v2/task" | ||||
| 	"github.com/containerd/containerd/sandbox" | ||||
| 	"github.com/containerd/containerd/services" | ||||
| 	"google.golang.org/grpc" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	plugin.Register(&plugin.Registration{ | ||||
| 		Type: plugin.ServicePlugin, | ||||
| 		ID:   services.SandboxControllerService, | ||||
| 		Requires: []plugin.Type{ | ||||
| 			plugin.RuntimePluginV2, | ||||
| 			plugin.MetadataPlugin, | ||||
| 			plugin.EventPlugin, | ||||
| 		}, | ||||
| 		InitFn: func(ic *plugin.InitContext) (interface{}, error) { | ||||
| 			shimPlugin, err := ic.GetByID(plugin.RuntimePluginV2, "shim") | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			metadataPlugin, err := ic.Get(plugin.MetadataPlugin) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			exchangePlugin, err := ic.GetByID(plugin.EventPlugin, "exchange") | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			var ( | ||||
| 				shims     = shimPlugin.(*v2.ShimManager) | ||||
| 				publisher = exchangePlugin.(*exchange.Exchange) | ||||
| 				db        = metadataPlugin.(*metadata.DB) | ||||
| 				store     = metadata.NewSandboxStore(db) | ||||
| 			) | ||||
|  | ||||
| 			return &controllerLocal{ | ||||
| 				shims:     shims, | ||||
| 				store:     store, | ||||
| 				publisher: publisher, | ||||
| 			}, nil | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| type controllerLocal struct { | ||||
| 	shims     *v2.ShimManager | ||||
| 	store     sandbox.Store | ||||
| 	publisher events.Publisher | ||||
| } | ||||
|  | ||||
| var _ api.ControllerClient = (*controllerLocal)(nil) | ||||
|  | ||||
| func (c *controllerLocal) Start(ctx context.Context, in *api.ControllerStartRequest, opts ...grpc.CallOption) (*api.ControllerStartResponse, error) { | ||||
| 	if _, err := c.shims.Get(ctx, in.SandboxID); err == nil { | ||||
| 		return nil, fmt.Errorf("sandbox %s already running: %w", in.SandboxID, errdefs.ErrAlreadyExists) | ||||
| 	} | ||||
|  | ||||
| 	info, err := c.store.Get(ctx, in.SandboxID) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to query sandbox metadata from store: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	shim, err := c.shims.Start(ctx, in.SandboxID, runtime.CreateOpts{ | ||||
| 		Spec:           info.Spec, | ||||
| 		RuntimeOptions: info.Runtime.Options, | ||||
| 		Runtime:        info.Runtime.Name, | ||||
| 		TaskOptions:    nil, | ||||
| 	}) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to start new sandbox: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	svc := task.NewSandboxClient(shim.Client()) | ||||
|  | ||||
| 	resp, err := svc.StartSandbox(ctx, &proto.StartSandboxRequest{ | ||||
| 		SandboxID:  in.SandboxID, | ||||
| 		BundlePath: shim.Bundle(), | ||||
| 		Rootfs:     in.Rootfs, | ||||
| 		Options:    in.Options, | ||||
| 	}) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to start sandbox %s: %w", in.SandboxID, err) | ||||
| 	} | ||||
|  | ||||
| 	return &api.ControllerStartResponse{ | ||||
| 		SandboxID: in.SandboxID, | ||||
| 		Pid:       resp.Pid, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (c *controllerLocal) Shutdown(ctx context.Context, in *api.ControllerShutdownRequest, opts ...grpc.CallOption) (*api.ControllerShutdownResponse, error) { | ||||
| 	svc, err := c.getSandbox(ctx, in.SandboxID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if _, err := svc.StopSandbox(ctx, &proto.StopSandboxRequest{ | ||||
| 		SandboxID:   in.SandboxID, | ||||
| 		TimeoutSecs: in.TimeoutSecs, | ||||
| 	}); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to stop sandbox: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := c.shims.Delete(ctx, in.SandboxID); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to delete sandbox shim: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return &api.ControllerShutdownResponse{}, nil | ||||
| } | ||||
|  | ||||
| func (c *controllerLocal) Wait(ctx context.Context, in *api.ControllerWaitRequest, opts ...grpc.CallOption) (*api.ControllerWaitResponse, error) { | ||||
| 	svc, err := c.getSandbox(ctx, in.SandboxID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	resp, err := svc.WaitSandbox(ctx, &proto.WaitSandboxRequest{ | ||||
| 		SandboxID: in.SandboxID, | ||||
| 	}) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to wait sandbox %s: %w", in.SandboxID, err) | ||||
| 	} | ||||
|  | ||||
| 	return &api.ControllerWaitResponse{ | ||||
| 		ExitStatus: resp.ExitStatus, | ||||
| 		ExitedAt:   resp.ExitedAt, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (c *controllerLocal) Status(ctx context.Context, in *api.ControllerStatusRequest, opts ...grpc.CallOption) (*api.ControllerStatusResponse, error) { | ||||
| 	svc, err := c.getSandbox(ctx, in.SandboxID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	resp, err := svc.SandboxStatus(ctx, &proto.SandboxStatusRequest{SandboxID: in.SandboxID}) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to query sandbox %s status: %w", in.SandboxID, err) | ||||
| 	} | ||||
|  | ||||
| 	return &api.ControllerStatusResponse{ | ||||
| 		ID:         resp.ID, | ||||
| 		Pid:        resp.Pid, | ||||
| 		State:      resp.State, | ||||
| 		ExitStatus: resp.ExitStatus, | ||||
| 		ExitedAt:   resp.ExitedAt, | ||||
| 		Extra:      resp.Extra, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (c *controllerLocal) getSandbox(ctx context.Context, id string) (task.SandboxService, error) { | ||||
| 	shim, err := c.shims.Get(ctx, id) | ||||
| 	if err != nil { | ||||
| 		return nil, errdefs.ErrNotFound | ||||
| 	} | ||||
|  | ||||
| 	svc := task.NewSandboxClient(shim.Client()) | ||||
| 	return svc, nil | ||||
| } | ||||
							
								
								
									
										89
									
								
								services/sandbox/controller_service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								services/sandbox/controller_service.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandbox | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
|  | ||||
| 	api "github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	"github.com/containerd/containerd/plugin" | ||||
| 	"github.com/containerd/containerd/services" | ||||
| 	"google.golang.org/grpc" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	plugin.Register(&plugin.Registration{ | ||||
| 		Type: plugin.GRPCPlugin, | ||||
| 		ID:   "sandbox-controllers", | ||||
| 		Requires: []plugin.Type{ | ||||
| 			plugin.ServicePlugin, | ||||
| 		}, | ||||
| 		InitFn: func(ic *plugin.InitContext) (interface{}, error) { | ||||
| 			plugins, err := ic.GetByType(plugin.ServicePlugin) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			p, ok := plugins[services.SandboxControllerService] | ||||
| 			if !ok { | ||||
| 				return nil, errors.New("sandbox service not found") | ||||
| 			} | ||||
|  | ||||
| 			i, err := p.Instance() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			return &controllerService{ | ||||
| 				local: i.(api.ControllerClient), | ||||
| 			}, nil | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| type controllerService struct { | ||||
| 	local api.ControllerClient | ||||
| } | ||||
|  | ||||
| var _ api.ControllerServer = (*controllerService)(nil) | ||||
|  | ||||
| func (s *controllerService) Register(server *grpc.Server) error { | ||||
| 	api.RegisterControllerServer(server, s) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *controllerService) Start(ctx context.Context, req *api.ControllerStartRequest) (*api.ControllerStartResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("start sandbox") | ||||
| 	return s.local.Start(ctx, req) | ||||
| } | ||||
|  | ||||
| func (s *controllerService) Shutdown(ctx context.Context, req *api.ControllerShutdownRequest) (*api.ControllerShutdownResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("delete sandbox") | ||||
| 	return s.local.Shutdown(ctx, req) | ||||
| } | ||||
|  | ||||
| func (s *controllerService) Wait(ctx context.Context, req *api.ControllerWaitRequest) (*api.ControllerWaitResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("wait sandbox") | ||||
| 	return s.local.Wait(ctx, req) | ||||
| } | ||||
|  | ||||
| func (s *controllerService) Status(ctx context.Context, req *api.ControllerStatusRequest) (*api.ControllerStatusResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("sandbox status") | ||||
| 	return s.local.Status(ctx, req) | ||||
| } | ||||
							
								
								
									
										111
									
								
								services/sandbox/store_local.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								services/sandbox/store_local.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandbox | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
|  | ||||
| 	"github.com/containerd/containerd/services" | ||||
| 	"google.golang.org/grpc" | ||||
|  | ||||
| 	api "github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| 	"github.com/containerd/containerd/api/types" | ||||
| 	"github.com/containerd/containerd/errdefs" | ||||
| 	"github.com/containerd/containerd/events" | ||||
| 	"github.com/containerd/containerd/metadata" | ||||
| 	"github.com/containerd/containerd/plugin" | ||||
| 	"github.com/containerd/containerd/sandbox" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	plugin.Register(&plugin.Registration{ | ||||
| 		Type: plugin.ServicePlugin, | ||||
| 		ID:   services.SandboxStoreService, | ||||
| 		Requires: []plugin.Type{ | ||||
| 			plugin.MetadataPlugin, | ||||
| 		}, | ||||
| 		InitFn: func(ic *plugin.InitContext) (interface{}, error) { | ||||
| 			m, err := ic.Get(plugin.MetadataPlugin) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			db := m.(*metadata.DB) | ||||
| 			return &sandboxLocal{ | ||||
| 				store:     metadata.NewSandboxStore(db), | ||||
| 				publisher: ic.Events, | ||||
| 			}, nil | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| type sandboxLocal struct { | ||||
| 	store     sandbox.Store | ||||
| 	publisher events.Publisher | ||||
| } | ||||
|  | ||||
| var _ = (api.StoreClient)(&sandboxLocal{}) | ||||
|  | ||||
| func (s *sandboxLocal) Create(ctx context.Context, in *api.StoreCreateRequest, _ ...grpc.CallOption) (*api.StoreCreateResponse, error) { | ||||
| 	sb, err := s.store.Create(ctx, sandbox.FromProto(&in.Sandbox)) | ||||
| 	if err != nil { | ||||
| 		return nil, errdefs.ToGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return &api.StoreCreateResponse{Sandbox: sandbox.ToProto(&sb)}, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxLocal) Update(ctx context.Context, in *api.StoreUpdateRequest, _ ...grpc.CallOption) (*api.StoreUpdateResponse, error) { | ||||
| 	sb, err := s.store.Update(ctx, sandbox.FromProto(&in.Sandbox), in.Fields...) | ||||
| 	if err != nil { | ||||
| 		return nil, errdefs.ToGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return &api.StoreUpdateResponse{Sandbox: sandbox.ToProto(&sb)}, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxLocal) Get(ctx context.Context, in *api.StoreGetRequest, _ ...grpc.CallOption) (*api.StoreGetResponse, error) { | ||||
| 	resp, err := s.store.Get(ctx, in.SandboxID) | ||||
| 	if err != nil { | ||||
| 		return nil, errdefs.ToGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	desc := sandbox.ToProto(&resp) | ||||
| 	return &api.StoreGetResponse{Sandbox: &desc}, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxLocal) List(ctx context.Context, in *api.StoreListRequest, _ ...grpc.CallOption) (*api.StoreListResponse, error) { | ||||
| 	resp, err := s.store.List(ctx, in.Filters...) | ||||
| 	if err != nil { | ||||
| 		return nil, errdefs.ToGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	list := make([]types.Sandbox, len(resp)) | ||||
| 	for i := range resp { | ||||
| 		list[i] = sandbox.ToProto(&resp[i]) | ||||
| 	} | ||||
|  | ||||
| 	return &api.StoreListResponse{List: list}, nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxLocal) Delete(ctx context.Context, in *api.StoreDeleteRequest, _ ...grpc.CallOption) (*api.StoreDeleteResponse, error) { | ||||
| 	if err := s.store.Delete(ctx, in.SandboxID); err != nil { | ||||
| 		return nil, errdefs.ToGRPC(err) | ||||
| 	} | ||||
|  | ||||
| 	return &api.StoreDeleteResponse{}, nil | ||||
| } | ||||
							
								
								
									
										90
									
								
								services/sandbox/store_service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								services/sandbox/store_service.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| /* | ||||
|    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. | ||||
| */ | ||||
|  | ||||
| package sandbox | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
|  | ||||
| 	"google.golang.org/grpc" | ||||
|  | ||||
| 	api "github.com/containerd/containerd/api/services/sandbox/v1" | ||||
| 	"github.com/containerd/containerd/log" | ||||
| 	"github.com/containerd/containerd/plugin" | ||||
| 	"github.com/containerd/containerd/services" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	plugin.Register(&plugin.Registration{ | ||||
| 		Type: plugin.GRPCPlugin, | ||||
| 		ID:   "sandboxes", | ||||
| 		Requires: []plugin.Type{ | ||||
| 			plugin.ServicePlugin, | ||||
| 		}, | ||||
| 		InitFn: func(ic *plugin.InitContext) (interface{}, error) { | ||||
| 			plugins, err := ic.GetByType(plugin.ServicePlugin) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			p, ok := plugins[services.SandboxStoreService] | ||||
| 			if !ok { | ||||
| 				return nil, errors.New("sandbox store service not found") | ||||
| 			} | ||||
| 			i, err := p.Instance() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			return &sandboxService{local: i.(api.StoreClient)}, nil | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| type sandboxService struct { | ||||
| 	local api.StoreClient | ||||
| } | ||||
|  | ||||
| var _ api.StoreServer = (*sandboxService)(nil) | ||||
|  | ||||
| func (s *sandboxService) Register(server *grpc.Server) error { | ||||
| 	api.RegisterStoreServer(server, s) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *sandboxService) Create(ctx context.Context, req *api.StoreCreateRequest) (*api.StoreCreateResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("create sandbox") | ||||
| 	return s.local.Create(ctx, req) | ||||
| } | ||||
|  | ||||
| func (s *sandboxService) Update(ctx context.Context, req *api.StoreUpdateRequest) (*api.StoreUpdateResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("update sandbox") | ||||
| 	return s.local.Update(ctx, req) | ||||
| } | ||||
|  | ||||
| func (s *sandboxService) List(ctx context.Context, req *api.StoreListRequest) (*api.StoreListResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("list sandboxes") | ||||
| 	return s.local.List(ctx, req) | ||||
| } | ||||
|  | ||||
| func (s *sandboxService) Get(ctx context.Context, req *api.StoreGetRequest) (*api.StoreGetResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("get sandbox") | ||||
| 	return s.local.Get(ctx, req) | ||||
| } | ||||
|  | ||||
| func (s *sandboxService) Delete(ctx context.Context, req *api.StoreDeleteRequest) (*api.StoreDeleteResponse, error) { | ||||
| 	log.G(ctx).WithField("req", req).Debug("delete sandbox") | ||||
| 	return s.local.Delete(ctx, req) | ||||
| } | ||||
| @@ -33,4 +33,8 @@ const ( | ||||
| 	DiffService = "diff-service" | ||||
| 	// IntrospectionService is the id of introspection service | ||||
| 	IntrospectionService = "introspection-service" | ||||
| 	// SandboxStoreService is the id of Sandbox's store service | ||||
| 	SandboxStoreService = "sandbox-store-service" | ||||
| 	// SandboxControllerService is the id of Sandbox's controller service | ||||
| 	SandboxControllerService = "sandbox-controller-service" | ||||
| ) | ||||
|   | ||||
| @@ -200,6 +200,7 @@ func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc. | ||||
| 		Runtime:        container.Runtime.Name, | ||||
| 		RuntimeOptions: container.Runtime.Options, | ||||
| 		TaskOptions:    r.Options, | ||||
| 		SandboxID:      container.SandboxID, | ||||
| 	} | ||||
| 	if r.RuntimePath != "" { | ||||
| 		opts.Runtime = r.RuntimePath | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Derek McGowan
					Derek McGowan