Move sandbox to core/sandbox
Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
@@ -23,8 +23,8 @@ import (
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/core/images"
|
||||
"github.com/containerd/containerd/v2/core/leases"
|
||||
"github.com/containerd/containerd/v2/core/sandbox"
|
||||
"github.com/containerd/containerd/v2/filters"
|
||||
"github.com/containerd/containerd/v2/sandbox"
|
||||
"github.com/containerd/containerd/v2/snapshots"
|
||||
)
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/metadata/boltutil"
|
||||
api "github.com/containerd/containerd/v2/core/sandbox"
|
||||
"github.com/containerd/containerd/v2/errdefs"
|
||||
"github.com/containerd/containerd/v2/filters"
|
||||
"github.com/containerd/containerd/v2/identifiers"
|
||||
"github.com/containerd/containerd/v2/namespaces"
|
||||
api "github.com/containerd/containerd/v2/sandbox"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
@@ -19,9 +19,9 @@ package metadata
|
||||
import (
|
||||
"testing"
|
||||
|
||||
api "github.com/containerd/containerd/v2/core/sandbox"
|
||||
"github.com/containerd/containerd/v2/errdefs"
|
||||
"github.com/containerd/containerd/v2/protobuf/types"
|
||||
api "github.com/containerd/containerd/v2/sandbox"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
81
core/sandbox/bridge.go
Normal file
81
core/sandbox/bridge.go
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/containerd/ttrpc"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
api "github.com/containerd/containerd/v2/api/runtime/sandbox/v1"
|
||||
)
|
||||
|
||||
// NewClient returns a new sandbox client that handles both GRPC and TTRPC clients.
|
||||
func NewClient(client interface{}) (api.TTRPCSandboxService, error) {
|
||||
switch c := client.(type) {
|
||||
case *ttrpc.Client:
|
||||
return api.NewTTRPCSandboxClient(c), nil
|
||||
case grpc.ClientConnInterface:
|
||||
return &grpcBridge{api.NewSandboxClient(c)}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported client type %T", client)
|
||||
}
|
||||
}
|
||||
|
||||
type grpcBridge struct {
|
||||
client api.SandboxClient
|
||||
}
|
||||
|
||||
var _ api.TTRPCSandboxService = (*grpcBridge)(nil)
|
||||
|
||||
func (g *grpcBridge) CreateSandbox(ctx context.Context, request *api.CreateSandboxRequest) (*api.CreateSandboxResponse, error) {
|
||||
return g.client.CreateSandbox(ctx, request)
|
||||
}
|
||||
|
||||
func (g *grpcBridge) StartSandbox(ctx context.Context, request *api.StartSandboxRequest) (*api.StartSandboxResponse, error) {
|
||||
return g.client.StartSandbox(ctx, request)
|
||||
}
|
||||
|
||||
func (g *grpcBridge) Platform(ctx context.Context, request *api.PlatformRequest) (*api.PlatformResponse, error) {
|
||||
return g.client.Platform(ctx, request)
|
||||
}
|
||||
|
||||
func (g *grpcBridge) StopSandbox(ctx context.Context, request *api.StopSandboxRequest) (*api.StopSandboxResponse, error) {
|
||||
return g.client.StopSandbox(ctx, request)
|
||||
}
|
||||
|
||||
func (g *grpcBridge) WaitSandbox(ctx context.Context, request *api.WaitSandboxRequest) (*api.WaitSandboxResponse, error) {
|
||||
return g.client.WaitSandbox(ctx, request)
|
||||
}
|
||||
|
||||
func (g *grpcBridge) SandboxStatus(ctx context.Context, request *api.SandboxStatusRequest) (*api.SandboxStatusResponse, error) {
|
||||
return g.client.SandboxStatus(ctx, request)
|
||||
}
|
||||
|
||||
func (g *grpcBridge) PingSandbox(ctx context.Context, request *api.PingRequest) (*api.PingResponse, error) {
|
||||
return g.client.PingSandbox(ctx, request)
|
||||
}
|
||||
|
||||
func (g *grpcBridge) ShutdownSandbox(ctx context.Context, request *api.ShutdownSandboxRequest) (*api.ShutdownSandboxResponse, error) {
|
||||
return g.client.ShutdownSandbox(ctx, request)
|
||||
}
|
||||
|
||||
func (g *grpcBridge) SandboxMetrics(ctx context.Context, request *api.SandboxMetricsRequest) (*api.SandboxMetricsResponse, error) {
|
||||
return g.client.SandboxMetrics(ctx, request)
|
||||
}
|
||||
136
core/sandbox/controller.go
Normal file
136
core/sandbox/controller.go
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
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"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/api/types"
|
||||
"github.com/containerd/containerd/v2/core/mount"
|
||||
"github.com/containerd/containerd/v2/platforms"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
)
|
||||
|
||||
type CreateOptions struct {
|
||||
Rootfs []mount.Mount
|
||||
// Options are used to pass arbitrary options to the shim when creating a new sandbox.
|
||||
// CRI will use this to pass PodSandboxConfig.
|
||||
// Don't confuse this with Runtime options, which are passed at shim instance start
|
||||
// to setup global shim configuration.
|
||||
Options typeurl.Any
|
||||
NetNSPath string
|
||||
Annotations map[string]string
|
||||
}
|
||||
|
||||
type CreateOpt func(*CreateOptions) error
|
||||
|
||||
// WithRootFS is used to create a sandbox with the provided rootfs mount
|
||||
func WithRootFS(m []mount.Mount) CreateOpt {
|
||||
return func(co *CreateOptions) error {
|
||||
co.Rootfs = m
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithOptions allows passing arbitrary options when creating a new sandbox.
|
||||
func WithOptions(options any) CreateOpt {
|
||||
return func(co *CreateOptions) error {
|
||||
var err error
|
||||
co.Options, err = typeurl.MarshalAny(options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal sandbox options: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithNetNSPath used to assign network namespace path of a sandbox.
|
||||
func WithNetNSPath(netNSPath string) CreateOpt {
|
||||
return func(co *CreateOptions) error {
|
||||
co.NetNSPath = netNSPath
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithAnnotations sets the provided annotations for sandbox creation.
|
||||
func WithAnnotations(annotations map[string]string) CreateOpt {
|
||||
return func(co *CreateOptions) error {
|
||||
co.Annotations = annotations
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type StopOptions struct {
|
||||
Timeout *time.Duration
|
||||
}
|
||||
|
||||
type StopOpt func(*StopOptions)
|
||||
|
||||
func WithTimeout(timeout time.Duration) StopOpt {
|
||||
return func(so *StopOptions) {
|
||||
so.Timeout = &timeout
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// Create is used to initialize sandbox environment. (mounts, any)
|
||||
Create(ctx context.Context, sandboxInfo Sandbox, opts ...CreateOpt) error
|
||||
// Start will start previously created sandbox.
|
||||
Start(ctx context.Context, sandboxID string) (ControllerInstance, error)
|
||||
// Platform returns target sandbox OS that will be used by Controller.
|
||||
// containerd will rely on this to generate proper OCI spec.
|
||||
Platform(_ctx context.Context, _sandboxID string) (platforms.Platform, error)
|
||||
// Stop will stop sandbox instance
|
||||
Stop(ctx context.Context, sandboxID string, opts ...StopOpt) error
|
||||
// Wait blocks until sandbox process exits.
|
||||
Wait(ctx context.Context, sandboxID string) (ExitStatus, 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, verbose bool) (ControllerStatus, error)
|
||||
// Shutdown deletes and cleans all tasks and sandbox instance.
|
||||
Shutdown(ctx context.Context, sandboxID string) error
|
||||
// Metrics queries the sandbox for metrics.
|
||||
Metrics(ctx context.Context, sandboxID string) (*types.Metric, error)
|
||||
}
|
||||
|
||||
type ControllerInstance struct {
|
||||
SandboxID string
|
||||
Pid uint32
|
||||
CreatedAt time.Time
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
type ExitStatus struct {
|
||||
ExitStatus uint32
|
||||
ExitedAt time.Time
|
||||
}
|
||||
|
||||
type ControllerStatus struct {
|
||||
SandboxID string
|
||||
Pid uint32
|
||||
State string
|
||||
Info map[string]string
|
||||
CreatedAt time.Time
|
||||
ExitedAt time.Time
|
||||
Extra typeurl.Any
|
||||
}
|
||||
70
core/sandbox/helpers.go
Normal file
70
core/sandbox/helpers.go
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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/v2/api/types"
|
||||
"github.com/containerd/containerd/v2/protobuf"
|
||||
gogo_types "github.com/containerd/containerd/v2/protobuf/types"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
)
|
||||
|
||||
// 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),
|
||||
},
|
||||
Sandboxer: sandbox.Sandboxer,
|
||||
Labels: sandbox.Labels,
|
||||
CreatedAt: protobuf.ToTimestamp(sandbox.CreatedAt),
|
||||
UpdatedAt: protobuf.ToTimestamp(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,
|
||||
Sandboxer: sandboxpb.Sandboxer,
|
||||
CreatedAt: protobuf.FromTimestamp(sandboxpb.CreatedAt),
|
||||
UpdatedAt: protobuf.FromTimestamp(sandboxpb.UpdatedAt),
|
||||
Extensions: extensions,
|
||||
}
|
||||
}
|
||||
153
core/sandbox/proxy/controller.go
Normal file
153
core/sandbox/proxy/controller.go
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
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 proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/containerd/containerd/v2/api/services/sandbox/v1"
|
||||
"github.com/containerd/containerd/v2/api/types"
|
||||
"github.com/containerd/containerd/v2/core/mount"
|
||||
"github.com/containerd/containerd/v2/core/sandbox"
|
||||
"github.com/containerd/containerd/v2/errdefs"
|
||||
"github.com/containerd/containerd/v2/platforms"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
// remoteSandboxController is a low level GRPC client for containerd's sandbox controller service
|
||||
type remoteSandboxController struct {
|
||||
client api.ControllerClient
|
||||
}
|
||||
|
||||
var _ sandbox.Controller = (*remoteSandboxController)(nil)
|
||||
|
||||
// NewSandboxController creates a client for a sandbox controller
|
||||
func NewSandboxController(client api.ControllerClient) sandbox.Controller {
|
||||
return &remoteSandboxController{client: client}
|
||||
}
|
||||
|
||||
func (s *remoteSandboxController) Create(ctx context.Context, sandboxInfo sandbox.Sandbox, opts ...sandbox.CreateOpt) error {
|
||||
var options sandbox.CreateOptions
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
_, err := s.client.Create(ctx, &api.ControllerCreateRequest{
|
||||
SandboxID: sandboxInfo.ID,
|
||||
Rootfs: mount.ToProto(options.Rootfs),
|
||||
Options: &anypb.Any{
|
||||
TypeUrl: options.Options.GetTypeUrl(),
|
||||
Value: options.Options.GetValue(),
|
||||
},
|
||||
NetnsPath: options.NetNSPath,
|
||||
Annotations: options.Annotations,
|
||||
})
|
||||
if err != nil {
|
||||
return errdefs.FromGRPC(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *remoteSandboxController) Start(ctx context.Context, sandboxID string) (sandbox.ControllerInstance, error) {
|
||||
resp, err := s.client.Start(ctx, &api.ControllerStartRequest{SandboxID: sandboxID})
|
||||
if err != nil {
|
||||
return sandbox.ControllerInstance{}, errdefs.FromGRPC(err)
|
||||
}
|
||||
|
||||
return sandbox.ControllerInstance{
|
||||
SandboxID: sandboxID,
|
||||
Pid: resp.GetPid(),
|
||||
CreatedAt: resp.GetCreatedAt().AsTime(),
|
||||
Labels: resp.GetLabels(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *remoteSandboxController) Platform(ctx context.Context, sandboxID string) (platforms.Platform, error) {
|
||||
resp, err := s.client.Platform(ctx, &api.ControllerPlatformRequest{SandboxID: sandboxID})
|
||||
if err != nil {
|
||||
return platforms.Platform{}, errdefs.FromGRPC(err)
|
||||
}
|
||||
|
||||
platform := resp.GetPlatform()
|
||||
return platforms.Platform{
|
||||
Architecture: platform.GetArchitecture(),
|
||||
OS: platform.GetOS(),
|
||||
Variant: platform.GetVariant(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *remoteSandboxController) Stop(ctx context.Context, sandboxID string, opts ...sandbox.StopOpt) error {
|
||||
var soptions sandbox.StopOptions
|
||||
for _, opt := range opts {
|
||||
opt(&soptions)
|
||||
}
|
||||
req := &api.ControllerStopRequest{SandboxID: sandboxID}
|
||||
if soptions.Timeout != nil {
|
||||
req.TimeoutSecs = uint32(soptions.Timeout.Seconds())
|
||||
}
|
||||
_, err := s.client.Stop(ctx, req)
|
||||
if err != nil {
|
||||
return errdefs.FromGRPC(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *remoteSandboxController) 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 *remoteSandboxController) Wait(ctx context.Context, sandboxID string) (sandbox.ExitStatus, error) {
|
||||
resp, err := s.client.Wait(ctx, &api.ControllerWaitRequest{SandboxID: sandboxID})
|
||||
if err != nil {
|
||||
return sandbox.ExitStatus{}, errdefs.FromGRPC(err)
|
||||
}
|
||||
|
||||
return sandbox.ExitStatus{
|
||||
ExitStatus: resp.GetExitStatus(),
|
||||
ExitedAt: resp.GetExitedAt().AsTime(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *remoteSandboxController) Status(ctx context.Context, sandboxID string, verbose bool) (sandbox.ControllerStatus, error) {
|
||||
resp, err := s.client.Status(ctx, &api.ControllerStatusRequest{SandboxID: sandboxID, Verbose: verbose})
|
||||
if err != nil {
|
||||
return sandbox.ControllerStatus{}, errdefs.FromGRPC(err)
|
||||
}
|
||||
return sandbox.ControllerStatus{
|
||||
SandboxID: sandboxID,
|
||||
Pid: resp.GetPid(),
|
||||
State: resp.GetState(),
|
||||
Info: resp.GetInfo(),
|
||||
CreatedAt: resp.GetCreatedAt().AsTime(),
|
||||
ExitedAt: resp.GetExitedAt().AsTime(),
|
||||
Extra: resp.GetExtra(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *remoteSandboxController) Metrics(ctx context.Context, sandboxID string) (*types.Metric, error) {
|
||||
resp, err := s.client.Metrics(ctx, &api.ControllerMetricsRequest{SandboxID: sandboxID})
|
||||
if err != nil {
|
||||
return nil, errdefs.FromGRPC(err)
|
||||
}
|
||||
return resp.Metrics, nil
|
||||
}
|
||||
95
core/sandbox/proxy/store.go
Normal file
95
core/sandbox/proxy/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 proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/containerd/containerd/v2/api/services/sandbox/v1"
|
||||
sb "github.com/containerd/containerd/v2/core/sandbox"
|
||||
"github.com/containerd/containerd/v2/errdefs"
|
||||
)
|
||||
|
||||
// remoteSandboxStore is a low-level containerd client to manage sandbox environments metadata
|
||||
type remoteSandboxStore struct {
|
||||
client api.StoreClient
|
||||
}
|
||||
|
||||
var _ sb.Store = (*remoteSandboxStore)(nil)
|
||||
|
||||
// NewSandboxStore create a client for a sandbox store
|
||||
func NewSandboxStore(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)
|
||||
}
|
||||
118
core/sandbox/store.go
Normal file
118
core/sandbox/store.go
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/errdefs"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
)
|
||||
|
||||
// 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
|
||||
// Sandboxer is the sandbox controller who manages the sandbox
|
||||
Sandboxer string
|
||||
// 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
|
||||
}
|
||||
|
||||
// AddExtension is a helper function to add sandbox metadata extension.
|
||||
func (s *Sandbox) AddExtension(name string, obj interface{}) error {
|
||||
if s.Extensions == nil {
|
||||
s.Extensions = map[string]typeurl.Any{}
|
||||
}
|
||||
|
||||
out, err := typeurl.MarshalAny(obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal sandbox extension %q: %w", name, err)
|
||||
}
|
||||
|
||||
s.Extensions[name] = out
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddLabel adds a label to sandbox's labels.
|
||||
func (s *Sandbox) AddLabel(name string, value string) {
|
||||
if s.Labels == nil {
|
||||
s.Labels = map[string]string{}
|
||||
}
|
||||
|
||||
s.Labels[name] = value
|
||||
}
|
||||
|
||||
// GetExtension retrieves a sandbox extension by name.
|
||||
func (s *Sandbox) GetExtension(name string, obj interface{}) error {
|
||||
out, ok := s.Extensions[name]
|
||||
if !ok {
|
||||
return errdefs.ErrNotFound
|
||||
}
|
||||
|
||||
if err := typeurl.UnmarshalTo(out, obj); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal sandbox extension %q: %w", name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLabel retrieves a sandbox label by name.
|
||||
func (s *Sandbox) GetLabel(name string) (string, error) {
|
||||
out, ok := s.Labels[name]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unable to find label %q in sandbox metadata: %w", name, errdefs.ErrNotFound)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
40
core/sandbox/store_test.go
Normal file
40
core/sandbox/store_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/typeurl/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAddExtension(t *testing.T) {
|
||||
sb := Sandbox{ID: "1"}
|
||||
|
||||
type test struct{ Name string }
|
||||
|
||||
typeurl.Register(&test{})
|
||||
|
||||
var in = test{Name: "test"}
|
||||
assert.NoError(t, sb.AddExtension("test", &in))
|
||||
|
||||
var out test
|
||||
err := sb.GetExtension("test", &out)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", out.Name)
|
||||
}
|
||||
Reference in New Issue
Block a user