podman

Форк
0
/
runtime_pod_common.go 
309 строк · 8.6 Кб
1
//go:build !remote && (linux || freebsd)
2

3
package libpod
4

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

10
	"github.com/containers/podman/v5/libpod/define"
11
	"github.com/containers/podman/v5/libpod/events"
12
	"github.com/containers/podman/v5/pkg/specgen"
13
	"github.com/hashicorp/go-multierror"
14
	"github.com/sirupsen/logrus"
15
)
16

17
// NewPod makes a new, empty pod
18
func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, options ...PodCreateOption) (_ *Pod, deferredErr error) {
19
	if !r.valid {
20
		return nil, define.ErrRuntimeStopped
21
	}
22

23
	pod := newPod(r)
24

25
	// Set default namespace to runtime's namespace
26
	// Do so before options run so they can override it
27
	if r.config.Engine.Namespace != "" {
28
		pod.config.Namespace = r.config.Engine.Namespace
29
	}
30

31
	for _, option := range options {
32
		if err := option(pod); err != nil {
33
			return nil, fmt.Errorf("running pod create option: %w", err)
34
		}
35
	}
36

37
	// Allocate a lock for the pod
38
	lock, err := r.lockManager.AllocateLock()
39
	if err != nil {
40
		return nil, fmt.Errorf("allocating lock for new pod: %w", err)
41
	}
42
	pod.lock = lock
43
	pod.config.LockID = pod.lock.ID()
44

45
	defer func() {
46
		if deferredErr != nil {
47
			if err := pod.lock.Free(); err != nil {
48
				logrus.Errorf("Freeing pod lock after failed creation: %v", err)
49
			}
50
		}
51
	}()
52

53
	pod.valid = true
54

55
	parentCgroup, err := r.platformMakePod(pod, p.ResourceLimits)
56
	if err != nil {
57
		return nil, err
58
	}
59
	if p.InfraContainerSpec != nil {
60
		p.InfraContainerSpec.CgroupParent = parentCgroup
61
	}
62

63
	if !pod.HasInfraContainer() && pod.SharesNamespaces() {
64
		return nil, errors.New("Pods must have an infra container to share namespaces")
65
	}
66
	if pod.HasInfraContainer() && !pod.SharesNamespaces() {
67
		logrus.Infof("Pod has an infra container, but shares no namespaces")
68
	}
69

70
	// Unless the user has specified a name, use a randomly generated one.
71
	// Note that name conflicts may occur (see #11735), so we need to loop.
72
	generateName := pod.config.Name == ""
73
	var addPodErr error
74
	for {
75
		if generateName {
76
			name, err := r.generateName()
77
			if err != nil {
78
				return nil, err
79
			}
80
			pod.config.Name = name
81
		}
82

83
		if p.InfraContainerSpec != nil && p.InfraContainerSpec.Hostname == "" {
84
			p.InfraContainerSpec.Hostname = pod.config.Name
85
		}
86
		if addPodErr = r.state.AddPod(pod); addPodErr == nil {
87
			return pod, nil
88
		}
89
		if !generateName || (!errors.Is(addPodErr, define.ErrPodExists) && !errors.Is(addPodErr, define.ErrCtrExists)) {
90
			break
91
		}
92
	}
93
	if addPodErr != nil {
94
		return nil, fmt.Errorf("adding pod to state: %w", addPodErr)
95
	}
96

97
	return pod, nil
98
}
99

100
// AddInfra adds the created infra container to the pod state
101
func (r *Runtime) AddInfra(ctx context.Context, pod *Pod, infraCtr *Container) (*Pod, error) {
102
	if !r.valid {
103
		return nil, define.ErrRuntimeStopped
104
	}
105
	pod.state.InfraContainerID = infraCtr.ID()
106
	if err := pod.save(); err != nil {
107
		return nil, err
108
	}
109
	pod.newPodEvent(events.Create)
110
	return pod, nil
111
}
112

113
// SavePod is a helper function to save the pod state from outside of libpod
114
func (r *Runtime) SavePod(pod *Pod) error {
115
	if !r.valid {
116
		return define.ErrRuntimeStopped
117
	}
118
	if err := pod.save(); err != nil {
119
		return err
120
	}
121
	pod.newPodEvent(events.Create)
122
	return nil
123
}
124

125
// DO NOT USE THIS FUNCTION DIRECTLY. Use removePod(), below. It will call
126
// removeMalformedPod() if necessary.
127
func (r *Runtime) removeMalformedPod(ctx context.Context, p *Pod, ctrs []*Container, force bool, timeout *uint, ctrNamedVolumes map[string]*ContainerNamedVolume) (map[string]error, error) {
128
	removedCtrs := make(map[string]error)
129
	errored := false
130
	for _, ctr := range ctrs {
131
		err := func() error {
132
			ctrLock := ctr.lock
133
			ctrLock.Lock()
134
			defer func() {
135
				ctrLock.Unlock()
136
			}()
137

138
			if err := ctr.syncContainer(); err != nil {
139
				return err
140
			}
141

142
			for _, vol := range ctr.config.NamedVolumes {
143
				ctrNamedVolumes[vol.Name] = vol
144
			}
145

146
			opts := ctrRmOpts{
147
				Force:      force,
148
				RemovePod:  true,
149
				IgnoreDeps: true,
150
				Timeout:    timeout,
151
			}
152
			_, _, err := r.removeContainer(ctx, ctr, opts)
153
			return err
154
		}()
155
		removedCtrs[ctr.ID()] = err
156
		if err != nil {
157
			errored = true
158
		}
159
	}
160

161
	// So, technically, no containers have been *removed*.
162
	// They're still in the DB.
163
	// So just return nil for removed containers. Squash all the errors into
164
	// a multierror so we don't lose them.
165
	if errored {
166
		var allErrors error
167
		for ctr, err := range removedCtrs {
168
			if err != nil {
169
				allErrors = multierror.Append(allErrors, fmt.Errorf("removing container %s: %w", ctr, err))
170
			}
171
		}
172
		return nil, fmt.Errorf("no containers were removed due to the following errors: %w", allErrors)
173
	}
174

175
	// Clear infra container ID before we remove the infra container.
176
	// There is a potential issue if we don't do that, and removal is
177
	// interrupted between RemoveAllContainers() below and the pod's removal
178
	// later - we end up with a reference to a nonexistent infra container.
179
	p.state.InfraContainerID = ""
180
	if err := p.save(); err != nil {
181
		return nil, err
182
	}
183

184
	// Remove all containers in the pod from the state.
185
	if err := r.state.RemovePodContainers(p); err != nil {
186
		// If this fails, there isn't much more we can do.
187
		// The containers in the pod are unusable, but they still exist,
188
		// so pod removal will fail.
189
		return nil, err
190
	}
191

192
	return removedCtrs, nil
193
}
194

195
func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, timeout *uint) (map[string]error, error) {
196
	removedCtrs := make(map[string]error)
197

198
	if err := p.updatePod(); err != nil {
199
		return nil, err
200
	}
201

202
	ctrs, err := r.state.PodContainers(p)
203
	if err != nil {
204
		return nil, err
205
	}
206
	numCtrs := len(ctrs)
207

208
	// If the only running container in the pod is the pause container, remove the pod and container unconditionally.
209
	pauseCtrID := p.state.InfraContainerID
210
	if numCtrs == 1 && ctrs[0].ID() == pauseCtrID {
211
		removeCtrs = true
212
		force = true
213
	}
214
	if !removeCtrs && numCtrs > 0 {
215
		return nil, fmt.Errorf("pod %s contains containers and cannot be removed: %w", p.ID(), define.ErrCtrExists)
216
	}
217

218
	var removalErr error
219
	ctrNamedVolumes := make(map[string]*ContainerNamedVolume)
220

221
	// Build a graph of all containers in the pod.
222
	graph, err := BuildContainerGraph(ctrs)
223
	if err != nil {
224
		// We have to allow the pod to be removed.
225
		// But let's only do it if force is set.
226
		if !force {
227
			return nil, fmt.Errorf("cannot create container graph for pod %s: %w", p.ID(), err)
228
		}
229

230
		removalErr = fmt.Errorf("creating container graph for pod %s failed, fell back to loop removal: %w", p.ID(), err)
231

232
		removedCtrs, err = r.removeMalformedPod(ctx, p, ctrs, force, timeout, ctrNamedVolumes)
233
		if err != nil {
234
			logrus.Errorf("Error creating container graph for pod %s: %v. Falling back to loop removal.", p.ID(), err)
235
			return removedCtrs, err
236
		}
237
	} else {
238
		ctrErrors := make(map[string]error)
239
		ctrsVisited := make(map[string]bool)
240

241
		for _, node := range graph.notDependedOnNodes {
242
			removeNode(ctx, node, p, force, timeout, false, ctrErrors, ctrsVisited, ctrNamedVolumes)
243
		}
244

245
		// Finalize the removed containers list
246
		for ctr := range ctrsVisited {
247
			removedCtrs[ctr] = ctrErrors[ctr]
248
		}
249

250
		if len(ctrErrors) > 0 {
251
			return removedCtrs, fmt.Errorf("not all containers could be removed from pod %s: %w", p.ID(), define.ErrRemovingCtrs)
252
		}
253
	}
254

255
	for volName := range ctrNamedVolumes {
256
		volume, err := r.state.Volume(volName)
257
		if err != nil && !errors.Is(err, define.ErrNoSuchVolume) {
258
			logrus.Errorf("Retrieving volume %s: %v", volName, err)
259
			continue
260
		}
261
		if !volume.Anonymous() {
262
			continue
263
		}
264
		if err := r.removeVolume(ctx, volume, false, timeout, false); err != nil {
265
			// If the anonymous volume is still being used that means it was likely transferred
266
			// to another container via --volumes-from so no need to log this as real error.
267
			if errors.Is(err, define.ErrNoSuchVolume) || errors.Is(err, define.ErrVolumeRemoved) || errors.Is(err, define.ErrVolumeBeingUsed) {
268
				continue
269
			}
270
			logrus.Errorf("Removing volume %s: %v", volName, err)
271
		}
272
	}
273

274
	// Remove pod cgroup
275
	if err := p.removePodCgroup(); err != nil {
276
		if removalErr == nil {
277
			removalErr = fmt.Errorf("removing pod %s cgroup: %w", p.ID(), err)
278
		} else {
279
			logrus.Errorf("Deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err)
280
		}
281
	}
282

283
	if err := p.maybeRemoveServiceContainer(); err != nil {
284
		return removedCtrs, err
285
	}
286

287
	// Remove pod from state
288
	if err := r.state.RemovePod(p); err != nil {
289
		if removalErr != nil {
290
			logrus.Errorf("%v", removalErr)
291
		}
292
		return removedCtrs, err
293
	}
294

295
	// Mark pod invalid
296
	p.valid = false
297
	p.newPodEvent(events.Remove)
298

299
	// Deallocate the pod lock
300
	if err := p.lock.Free(); err != nil {
301
		if removalErr == nil {
302
			removalErr = fmt.Errorf("freeing pod %s lock: %w", p.ID(), err)
303
		} else {
304
			logrus.Errorf("Freeing pod %s lock: %v", p.ID(), err)
305
		}
306
	}
307

308
	return removedCtrs, removalErr
309
}
310

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

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

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

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