podman

Форк
0
/
checkpoint_restore.go 
234 строки · 7.5 Кб
1
//go:build !remote
2

3
package checkpoint
4

5
import (
6
	"context"
7
	"errors"
8
	"fmt"
9
	"os"
10

11
	metadata "github.com/checkpoint-restore/checkpointctl/lib"
12
	"github.com/containers/common/libimage"
13
	"github.com/containers/common/pkg/config"
14
	"github.com/containers/podman/v5/libpod"
15
	ann "github.com/containers/podman/v5/pkg/annotations"
16
	"github.com/containers/podman/v5/pkg/checkpoint/crutils"
17
	"github.com/containers/podman/v5/pkg/criu"
18
	"github.com/containers/podman/v5/pkg/domain/entities"
19
	"github.com/containers/podman/v5/pkg/specgen/generate"
20
	"github.com/containers/podman/v5/pkg/specgenutil"
21
	spec "github.com/opencontainers/runtime-spec/specs-go"
22
	"github.com/sirupsen/logrus"
23
)
24

25
// Prefixing the checkpoint/restore related functions with 'cr'
26

27
func CRImportCheckpointTar(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions) ([]*libpod.Container, error) {
28
	// First get the container definition from the
29
	// tarball to a temporary directory
30
	dir, err := os.MkdirTemp("", "checkpoint")
31
	if err != nil {
32
		return nil, err
33
	}
34
	defer func() {
35
		if err := os.RemoveAll(dir); err != nil {
36
			logrus.Errorf("Could not recursively remove %s: %q", dir, err)
37
		}
38
	}()
39
	if err := crutils.CRImportCheckpointConfigOnly(dir, restoreOptions.Import); err != nil {
40
		return nil, err
41
	}
42
	return CRImportCheckpoint(ctx, runtime, restoreOptions, dir)
43
}
44

45
// CRImportCheckpoint it the function which imports the information
46
// from checkpoint tarball and re-creates the container from that information
47
func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions, dir string) ([]*libpod.Container, error) {
48
	// Load spec.dump from temporary directory
49
	dumpSpec := new(spec.Spec)
50
	if _, err := metadata.ReadJSONFile(dumpSpec, dir, metadata.SpecDumpFile); err != nil {
51
		return nil, err
52
	}
53

54
	// Load config.dump from temporary directory
55
	ctrConfig := new(libpod.ContainerConfig)
56
	if _, err := metadata.ReadJSONFile(ctrConfig, dir, metadata.ConfigDumpFile); err != nil {
57
		return nil, err
58
	}
59

60
	if ctrConfig.Pod != "" && restoreOptions.Pod == "" {
61
		return nil, errors.New("cannot restore pod container without --pod")
62
	}
63

64
	if ctrConfig.Pod == "" && restoreOptions.Pod != "" {
65
		return nil, errors.New("cannot restore non pod container into pod")
66
	}
67

68
	// This should not happen as checkpoints with these options are not exported.
69
	if len(ctrConfig.Dependencies) > 0 {
70
		return nil, errors.New("cannot import checkpoints of containers with dependencies")
71
	}
72

73
	// Volumes included in the checkpoint should not exist
74
	if !restoreOptions.IgnoreVolumes {
75
		for _, vol := range ctrConfig.NamedVolumes {
76
			exists, err := runtime.HasVolume(vol.Name)
77
			if err != nil {
78
				return nil, err
79
			}
80
			if exists {
81
				return nil, fmt.Errorf("volume with name %s already exists. Use --ignore-volumes to not restore content of volumes", vol.Name)
82
			}
83
		}
84
	}
85

86
	ctrID := ctrConfig.ID
87
	newName := false
88

89
	// Check if the restored container gets a new name
90
	if restoreOptions.Name != "" {
91
		ctrConfig.ID = ""
92
		ctrConfig.Name = restoreOptions.Name
93
		newName = true
94
	}
95

96
	if restoreOptions.Pod != "" {
97
		// Restoring into a Pod requires much newer versions of CRIU
98
		if err := criu.CheckForCriu(criu.PodCriuVersion); err != nil {
99
			return nil, fmt.Errorf("restoring containers into pod: %w", err)
100
		}
101
		// The runtime also has to support it
102
		if !crutils.CRRuntimeSupportsPodCheckpointRestore(runtime.GetOCIRuntimePath()) {
103
			return nil, fmt.Errorf("runtime %s does not support pod restore", runtime.GetOCIRuntimePath())
104
		}
105
		// Restoring into an existing Pod
106
		ctrConfig.Pod = restoreOptions.Pod
107

108
		// According to podman pod create a pod can share the following namespaces:
109
		// cgroup, ipc, net, pid, uts
110
		// Let's make sure we are restoring into a pod with the same shared namespaces.
111
		pod, err := runtime.LookupPod(ctrConfig.Pod)
112
		if err != nil {
113
			return nil, fmt.Errorf("pod %q cannot be retrieved: %w", ctrConfig.Pod, err)
114
		}
115

116
		infraContainer, err := pod.InfraContainer()
117
		if err != nil {
118
			return nil, fmt.Errorf("cannot retrieve infra container from pod %q: %w", ctrConfig.Pod, err)
119
		}
120

121
		// If a namespace was shared (!= "") it needs to be set to the new infrastructure container.
122
		// If the infrastructure container does not share the same namespaces as the to be restored
123
		// container we abort.
124
		if ctrConfig.IPCNsCtr != "" {
125
			if !pod.SharesIPC() {
126
				return nil, fmt.Errorf("pod %s does not share the IPC namespace", ctrConfig.Pod)
127
			}
128
			ctrConfig.IPCNsCtr = infraContainer.ID()
129
		}
130

131
		if ctrConfig.NetNsCtr != "" {
132
			if !pod.SharesNet() {
133
				return nil, fmt.Errorf("pod %s does not share the network namespace", ctrConfig.Pod)
134
			}
135
			ctrConfig.NetNsCtr = infraContainer.ID()
136
			for net, opts := range ctrConfig.Networks {
137
				opts.StaticIPs = nil
138
				opts.StaticMAC = nil
139
				ctrConfig.Networks[net] = opts
140
			}
141
			ctrConfig.StaticIP = nil
142
			ctrConfig.StaticMAC = nil
143
		}
144

145
		if ctrConfig.PIDNsCtr != "" {
146
			if !pod.SharesPID() {
147
				return nil, fmt.Errorf("pod %s does not share the PID namespace", ctrConfig.Pod)
148
			}
149
			ctrConfig.PIDNsCtr = infraContainer.ID()
150
		}
151

152
		if ctrConfig.UTSNsCtr != "" {
153
			if !pod.SharesUTS() {
154
				return nil, fmt.Errorf("pod %s does not share the UTS namespace", ctrConfig.Pod)
155
			}
156
			ctrConfig.UTSNsCtr = infraContainer.ID()
157
		}
158

159
		if ctrConfig.CgroupNsCtr != "" {
160
			if !pod.SharesCgroup() {
161
				return nil, fmt.Errorf("pod %s does not share the cgroup namespace", ctrConfig.Pod)
162
			}
163
			ctrConfig.CgroupNsCtr = infraContainer.ID()
164
		}
165

166
		// Change SELinux labels to infrastructure container labels
167
		ctrConfig.MountLabel = infraContainer.MountLabel()
168
		ctrConfig.ProcessLabel = infraContainer.ProcessLabel()
169

170
		// Fix parent cgroup
171
		cgroupPath, err := pod.CgroupPath()
172
		if err != nil {
173
			return nil, fmt.Errorf("cannot retrieve cgroup path from pod %q: %w", ctrConfig.Pod, err)
174
		}
175
		ctrConfig.CgroupParent = cgroupPath
176

177
		oldPodID := dumpSpec.Annotations[ann.SandboxID]
178
		// Fix up SandboxID in the annotations
179
		dumpSpec.Annotations[ann.SandboxID] = ctrConfig.Pod
180
		// Fix up CreateCommand
181
		for i, c := range ctrConfig.CreateCommand {
182
			if c == oldPodID {
183
				ctrConfig.CreateCommand[i] = ctrConfig.Pod
184
			}
185
		}
186
	}
187

188
	if len(restoreOptions.PublishPorts) > 0 {
189
		pubPorts, err := specgenutil.CreatePortBindings(restoreOptions.PublishPorts)
190
		if err != nil {
191
			return nil, err
192
		}
193

194
		ports, err := generate.ParsePortMapping(pubPorts, nil)
195
		if err != nil {
196
			return nil, err
197
		}
198
		ctrConfig.PortMappings = ports
199
	}
200

201
	pullOptions := &libimage.PullOptions{}
202
	pullOptions.Writer = os.Stderr
203
	if _, err := runtime.LibimageRuntime().Pull(ctx, ctrConfig.RootfsImageName, config.PullPolicyMissing, pullOptions); err != nil {
204
		return nil, err
205
	}
206

207
	// Now create a new container from the just loaded information
208
	container, err := runtime.RestoreContainer(ctx, dumpSpec, ctrConfig)
209
	if err != nil {
210
		return nil, err
211
	}
212

213
	var containers []*libpod.Container
214
	if container == nil {
215
		return nil, nil
216
	}
217

218
	containerConfig := container.Config()
219
	ctrName := ctrConfig.Name
220
	if containerConfig.Name != ctrName {
221
		return nil, fmt.Errorf("name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName)
222
	}
223

224
	if !newName {
225
		// Only check ID for a restore with the same name.
226
		// Using -n to request a new name for the restored container, will also create a new ID
227
		if containerConfig.ID != ctrID {
228
			return nil, fmt.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID)
229
		}
230
	}
231

232
	containers = append(containers, container)
233
	return containers, nil
234
}
235

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.