mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
151 lines
4.8 KiB
Go
151 lines
4.8 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// 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"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/containerd/containerd"
|
|
"github.com/containerd/containerd/containers"
|
|
"github.com/containerd/typeurl"
|
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
)
|
|
|
|
// UpdateContainerDevices 通过 containerd API 更新 container spec 中的 devices
|
|
// 用于 cgroup v2 场景
|
|
func UpdateContainerDevices(ctx context.Context, client *containerd.Client, containerID string, devices []*runtimespec.LinuxDeviceCgroup) error {
|
|
if len(devices) == 0 {
|
|
return nil
|
|
}
|
|
|
|
log.Infof("[UpdateContainerDevices] Updating container %s devices via containerd API: %v", containerID, devices)
|
|
|
|
// 获取 container
|
|
container, err := client.LoadContainer(ctx, containerID)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "load container %s", containerID)
|
|
}
|
|
cStatus := ContainerStatus(ctx, container)
|
|
if cStatus == ProcessStatusRestarting {
|
|
return errors.Errorf("container %q is in restarting state", containerID)
|
|
}
|
|
// 注意:我们允许在 created、stopped 等状态下更新 spec
|
|
spec, err := container.Spec(ctx)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "get container %s spec", containerID)
|
|
}
|
|
oldSpec, err := copySpec(spec)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "copy container %s spec", containerID)
|
|
}
|
|
log.Infof("======oldSpec: %s, try update devices: %s", jsonutils.Marshal(oldSpec).PrettyString(), jsonutils.Marshal(devices).PrettyString())
|
|
|
|
// 确保 Linux.Resources 存在
|
|
if spec.Linux == nil {
|
|
spec.Linux = &runtimespec.Linux{}
|
|
}
|
|
if spec.Linux.Resources == nil {
|
|
spec.Linux.Resources = &runtimespec.LinuxResources{}
|
|
}
|
|
if spec.Linux.Resources.BlockIO == nil {
|
|
spec.Linux.Resources.BlockIO = &runtimespec.LinuxBlockIO{}
|
|
}
|
|
if spec.Linux.Resources.CPU == nil {
|
|
spec.Linux.Resources.CPU = &runtimespec.LinuxCPU{}
|
|
}
|
|
if spec.Linux.Resources.Memory == nil {
|
|
spec.Linux.Resources.Memory = &runtimespec.LinuxMemory{}
|
|
}
|
|
if spec.Linux.Resources.Pids == nil {
|
|
spec.Linux.Resources.Pids = &runtimespec.LinuxPids{}
|
|
}
|
|
if spec.Linux.Resources.HugepageLimits == nil {
|
|
spec.Linux.Resources.HugepageLimits = []runtimespec.LinuxHugepageLimit{}
|
|
}
|
|
if spec.Linux.Resources.Network == nil {
|
|
spec.Linux.Resources.Network = &runtimespec.LinuxNetwork{}
|
|
}
|
|
if spec.Linux.Resources.Rdma == nil {
|
|
spec.Linux.Resources.Rdma = map[string]runtimespec.LinuxRdma{}
|
|
}
|
|
if spec.Linux.Resources.Unified == nil {
|
|
spec.Linux.Resources.Unified = map[string]string{}
|
|
}
|
|
if spec.Linux.Resources.Devices == nil {
|
|
spec.Linux.Resources.Devices = []runtimespec.LinuxDeviceCgroup{}
|
|
}
|
|
|
|
// 更新 devices
|
|
// spec.Linux.Resources.Devices = devices
|
|
for i := range devices {
|
|
dev := devices[i]
|
|
spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, *dev)
|
|
}
|
|
|
|
if err := updateContainerSpec(ctx, container, spec); err != nil {
|
|
return errors.Wrapf(err, "update container %s spec", containerID)
|
|
}
|
|
|
|
log.Infof("[UpdateContainerDevices] Successfully updated container %s devices via containerd API", containerID)
|
|
return nil
|
|
}
|
|
|
|
func updateContainerSpec(ctx context.Context, container containerd.Container, spec *runtimespec.Spec) error {
|
|
if err := container.Update(ctx, func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
|
|
a, err := typeurl.MarshalAny(spec)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "marshal container %s spec", container.ID())
|
|
}
|
|
c.Spec = a
|
|
return nil
|
|
}); err != nil {
|
|
return errors.Wrapf(err, "update container %s spec", container.ID())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeepCopy makes a deep copy from src into dst.
|
|
func DeepCopy(dst interface{}, src interface{}) error {
|
|
if dst == nil {
|
|
return errors.Errorf("dst cannot be nil")
|
|
}
|
|
if src == nil {
|
|
return errors.Errorf("src cannot be nil")
|
|
}
|
|
bytes, err := json.Marshal(src)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to marshal src: %w", err)
|
|
}
|
|
err = json.Unmarshal(bytes, dst)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to unmarshal into dst: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func copySpec(spec *runtimespec.Spec) (*runtimespec.Spec, error) {
|
|
var copySpec runtimespec.Spec
|
|
if err := DeepCopy(©Spec, spec); err != nil {
|
|
return nil, fmt.Errorf("failed to deep copy:%w", err)
|
|
}
|
|
return ©Spec, nil
|
|
}
|