Skip to content

Commit 2138a5d

Browse files
committed
feat: support containers skip restart when apply plugin
Signed-off-by: zwtop <[email protected]>
1 parent c2f2ef3 commit 2138a5d

File tree

7 files changed

+301
-31
lines changed

7 files changed

+301
-31
lines changed

client/clienttest/runtime.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"time"
2424

2525
"github.com/containerd/containerd"
26+
"github.com/containerd/containerd/containers"
2627
"github.com/containerd/containerd/images"
2728
"github.com/containerd/containerd/platforms"
2829

@@ -45,12 +46,16 @@ func NewRuntime(followWaitTime time.Duration) client.Runtime {
4546
}
4647
}
4748

48-
func (r *runtime) Platform() platforms.Matcher { return platforms.All }
49+
func (r *runtime) Platform() platforms.MatchComparer { return platforms.All }
4950
func (r *runtime) ContainerdClient() *containerd.Client { return &containerd.Client{} }
5051
func (r *runtime) Namespace() string { return "unknown" }
5152
func (r *runtime) ConfigRuntime(context.Context) error { return nil }
5253
func (r *runtime) RemoveNamespace(context.Context) error { return nil }
5354

55+
func (r *runtime) RecommendedRuntimeInfo(context.Context, *model.Container) *containers.RuntimeInfo {
56+
return &containers.RuntimeInfo{}
57+
}
58+
5459
func (r *runtime) NodeExecute(ctx context.Context, name string, commands ...string) error {
5560
if len(commands) == 0 {
5661
return nil
@@ -97,6 +102,14 @@ func (r *runtime) CreateContainer(ctx context.Context, container *model.Containe
97102
return nil
98103
}
99104

105+
func (r *runtime) UpdateContainer(_ context.Context, container *model.Container, _ *client.ContainerUpdateOptions) error {
106+
if _, ok := r.containers[container.Name]; !ok {
107+
return fmt.Errorf("container with name %s not exist", container.Name)
108+
}
109+
r.containers[container.Name] = container
110+
return nil
111+
}
112+
100113
func (r *runtime) RemoveContainer(ctx context.Context, containerID string) error {
101114
delete(r.containers, containerID)
102115
return nil

client/interface.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type Runtime interface {
4040
io.Closer
4141

4242
// Platform supported by the runtime
43-
Platform() platforms.Matcher
43+
Platform() platforms.MatchComparer
4444
// Namespace of the current runtime in
4545
Namespace() string
4646
// NodeExecute execute commands on the runtime node
@@ -72,15 +72,26 @@ type ImageManager interface {
7272

7373
type ContainerStatus struct {
7474
containerd.Status
75+
containerd.Task
7576
containers.Container
7677
}
7778

79+
type ContainerUpdateOptions struct {
80+
UpdateSnapshot bool
81+
}
82+
7883
// ContainerManager contains methods to manipulate containers managed by a
7984
// container runtime. The methods are thread-safe.
8085
type ContainerManager interface {
86+
// RecommendedRuntimeInfo of the container.
87+
RecommendedRuntimeInfo(ctx context.Context, container *model.Container) *containers.RuntimeInfo
88+
8189
// CreateContainer creates a new container.
8290
CreateContainer(ctx context.Context, container *model.Container, follow bool) error
8391

92+
// UpdateContainer update the container.
93+
UpdateContainer(ctx context.Context, container *model.Container, opts *ContainerUpdateOptions) error
94+
8495
// RemoveContainer removes the container.
8596
RemoveContainer(ctx context.Context, containerID string) error
8697

client/runtime.go

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func newTCPClient(ctx context.Context, endpoint string, tlsConfig *tls.Config, t
158158
return containerd.NewWithConn(conn, containerd.WithTimeout(timeout))
159159
}
160160

161-
func (r *runtime) Platform() platforms.Matcher { return r.platform }
161+
func (r *runtime) Platform() platforms.MatchComparer { return r.platform }
162162
func (r *runtime) ContainerdClient() *containerd.Client { return r.client }
163163
func (r *runtime) Namespace() string { return r.namespace }
164164

@@ -225,6 +225,12 @@ func (r *runtime) getImage(ctx context.Context, ref string) (containerd.Image, e
225225
return containerd.NewImageWithPlatform(r.client, i, r.platform), nil
226226
}
227227

228+
func (r *runtime) RecommendedRuntimeInfo(ctx context.Context, container *model.Container) *containers.RuntimeInfo {
229+
cc := &containers.Container{}
230+
lo.Must0(withRuntime(r.runcPath, container)(ctx, r.client, cc))
231+
return &cc.Runtime
232+
}
233+
228234
func (r *runtime) CreateContainer(ctx context.Context, container *model.Container, following bool) error {
229235
ctx = namespaces.WithNamespace(ctx, r.namespace)
230236

@@ -273,6 +279,51 @@ func (r *runtime) CreateContainer(ctx context.Context, container *model.Containe
273279
return nil
274280
}
275281

282+
func (r *runtime) UpdateContainer(ctx context.Context, container *model.Container, opts *ContainerUpdateOptions) error {
283+
ctx = namespaces.WithNamespace(ctx, r.namespace)
284+
285+
image, err := r.getImage(ctx, container.Image)
286+
if err != nil {
287+
return fmt.Errorf("get image %s: %w", container.Image, err)
288+
}
289+
290+
updateOptions := []containerd.UpdateContainerOpts{
291+
containerd.UpdateContainerOpts(containerd.WithImageName(container.Image)),
292+
containerd.UpdateContainerOpts(withLogPath(container.Process.LogPath)),
293+
// containerd.UpdateContainerOpts(withRuntime(r.runcPath, container)), fixme: runtime donot support update
294+
containerd.UpdateContainerOpts(containerd.WithNewSpec(containerSpecOpts(r.namespace, image, container)...)),
295+
}
296+
if container.Process.RestartPolicy == model.RestartPolicyAlways {
297+
updateOptions = append(updateOptions, restart.WithStatus(containerd.Running))
298+
}
299+
if opts.UpdateSnapshot {
300+
updateOptions = append(updateOptions, containerd.UpdateContainerOpts(withNewSnapshotAndConfig(image, container.ConfigContent)))
301+
}
302+
303+
c, err := r.client.LoadContainer(ctx, container.Name)
304+
if err != nil {
305+
return fmt.Errorf("load container: %w", err)
306+
}
307+
err = c.Update(ctx, updateOptions...)
308+
if err != nil {
309+
return fmt.Errorf("update container: %w", err)
310+
}
311+
312+
task, err := c.Task(ctx, nil)
313+
if err != nil {
314+
if errdefs.IsNotFound(err) {
315+
return nil
316+
}
317+
return fmt.Errorf("load task: %w", err)
318+
}
319+
320+
spec, err := c.Spec(ctx)
321+
if err != nil {
322+
return fmt.Errorf("get container spec: %w", err)
323+
}
324+
return task.Update(ctx, containerd.WithResources(spec.Linux.Resources))
325+
}
326+
276327
func (r *runtime) RemoveContainer(ctx context.Context, containerID string) error {
277328
ctx = namespaces.WithNamespace(ctx, r.namespace)
278329

@@ -354,6 +405,7 @@ func (r *runtime) GetContainerStatus(ctx context.Context, containerID string) (C
354405

355406
return ContainerStatus{
356407
Status: status,
408+
Task: task,
357409
Container: lo.Must(c.Info(ctx, containerd.WithoutRefreshedMetadata)),
358410
}, nil
359411
}
@@ -399,6 +451,10 @@ func (r *runtime) doConfig(ctx context.Context) error {
399451
return nil
400452
}
401453

454+
func ContainerSpecOpts(namespace string, img containerd.Image, container *model.Container) []oci.SpecOpts {
455+
return containerSpecOpts(namespace, img, container)
456+
}
457+
402458
func containerSpecOpts(namespace string, img containerd.Image, container *model.Container) []oci.SpecOpts {
403459
var specOpts []oci.SpecOpts
404460
specOpts = append(specOpts, oci.WithProcessCwd(container.Process.WorkingDir))
@@ -576,6 +632,16 @@ func withLogPath(logPath string) func(ctx context.Context, client *containerd.Cl
576632
}
577633
}
578634

635+
func GetLogPath(c *containers.Container) string {
636+
if c.Labels[restart.LogURILabel] != "" {
637+
return strings.TrimPrefix(c.Labels[restart.LogURILabel], "file://")
638+
}
639+
if c.Labels[restart.LogPathLabel] != "" {
640+
return c.Labels[restart.LogPathLabel]
641+
}
642+
return ""
643+
}
644+
579645
func withRlimits(rlimits []specs.POSIXRlimit) oci.SpecOpts {
580646
return func(_ context.Context, _ oci.Client, _ *containers.Container, spec *oci.Spec) error {
581647
if spec.Process == nil {

model/plugin.go

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,18 @@ type PluginInstanceDefinition struct {
2929
}
3030

3131
type ContainerDefinition struct {
32-
Name string `yaml:"name"`
33-
Image string `yaml:"image"`
34-
Mounts []MountDefinition `yaml:"mounts,omitempty"`
35-
Process ProcessDefinition `yaml:"process"`
36-
Logging *LoggingDefinition `yaml:"logging,omitempty"`
37-
Metrics *MetricsDefinition `yaml:"metrics,omitempty"`
38-
Resources *ResourceDefinition `yaml:"resources,omitempty"`
39-
Runtime *RuntimeDefinition `yaml:"runtime,omitempty"`
40-
StartupProbe *ContainerProbe `yaml:"startup_probe,omitempty"`
41-
LivenessProbe *ContainerProbe `yaml:"liveness_probe,omitempty"`
42-
SpecPatches []string `yaml:"spec_patches,omitempty"`
32+
Name string `yaml:"name"`
33+
Image string `yaml:"image"`
34+
Mounts []MountDefinition `yaml:"mounts,omitempty"`
35+
Process ProcessDefinition `yaml:"process"`
36+
Logging *LoggingDefinition `yaml:"logging,omitempty"`
37+
Metrics *MetricsDefinition `yaml:"metrics,omitempty"`
38+
Resources *ResourceDefinition `yaml:"resources,omitempty"`
39+
Runtime *RuntimeDefinition `yaml:"runtime,omitempty"`
40+
UpdatePolicy *UpdatePolicyDefinition `yaml:"update_policy,omitempty"`
41+
StartupProbe *ContainerProbe `yaml:"startup_probe,omitempty"`
42+
LivenessProbe *ContainerProbe `yaml:"liveness_probe,omitempty"`
43+
SpecPatches []string `yaml:"spec_patches,omitempty"`
4344
}
4445

4546
type MountDefinition struct {
@@ -93,6 +94,17 @@ type RuntimeDefinition struct {
9394
SystemdCgroup bool `yaml:"systemd_cgroup,omitempty"`
9495
}
9596

97+
type UpdatePolicyDefinition struct {
98+
OnNoChange UpdatePolicyMode `yaml:"on_no_change,omitempty"`
99+
}
100+
101+
type UpdatePolicyMode string
102+
103+
const (
104+
UpdatePolicyModeSkip UpdatePolicyMode = "skip"
105+
UpdatePolicyModeRestart UpdatePolicyMode = "restart"
106+
)
107+
96108
type POSIXRlimit struct {
97109
Type string `yaml:"type"`
98110
Hard uint64 `yaml:"hard"`

plugin/context.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type ContextKey string
2626

2727
const (
2828
ContextSkipNoChange ContextKey = "skip-no-change"
29+
ContextForceUpdate ContextKey = "force-update"
2930
)
3031

3132
func SetSkipNoChange(ctx context.Context, skip bool) context.Context {
@@ -41,3 +42,17 @@ func GetSkipNoChange(ctx context.Context) bool {
4142
func WithSkipNoChange(ctx context.Context) context.Context {
4243
return SetSkipNoChange(ctx, true)
4344
}
45+
46+
func SetForceUpdate(ctx context.Context, force bool) context.Context {
47+
return context.WithValue(ctx, ContextForceUpdate, force)
48+
}
49+
50+
func GetForceUpdate(ctx context.Context) bool {
51+
v := ctx.Value(ContextForceUpdate)
52+
force, ok := v.(bool)
53+
return lo.If(ok, force).Else(false)
54+
}
55+
56+
func WithForceUpdate(ctx context.Context) context.Context {
57+
return SetForceUpdate(ctx, true)
58+
}

plugin/context_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,14 @@ func TestContextSkipNoChange(t *testing.T) {
3535
ctx = plugin.SetSkipNoChange(ctx, false)
3636
Expect(plugin.GetSkipNoChange(ctx)).Should(BeFalse())
3737
}
38+
39+
func TestContextForceUpdate(t *testing.T) {
40+
RegisterTestingT(t)
41+
42+
ctx := context.Background()
43+
Expect(plugin.GetForceUpdate(ctx)).Should(BeFalse())
44+
ctx = plugin.WithForceUpdate(ctx)
45+
Expect(plugin.GetForceUpdate(ctx)).Should(BeTrue())
46+
ctx = plugin.SetForceUpdate(ctx, false)
47+
Expect(plugin.GetForceUpdate(ctx)).Should(BeFalse())
48+
}

0 commit comments

Comments
 (0)