summaryrefslogtreecommitdiff
path: root/libpod/runtime_img.go
blob: d8e88ca5037189c3a0d8ef128b40cb414986ed96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package libpod

import (
	"context"
	"errors"
	"fmt"
	"io"
	"os"

	buildahDefine "github.com/containers/buildah/define"
	"github.com/containers/buildah/imagebuildah"
	"github.com/containers/common/libimage"
	"github.com/containers/image/v5/docker/reference"
	"github.com/containers/podman/v4/libpod/define"
	"github.com/containers/podman/v4/libpod/events"
	"github.com/containers/podman/v4/pkg/util"
	"github.com/sirupsen/logrus"
)

// Runtime API

// RemoveContainersForImageCallback returns a callback that can be used in
// `libimage`.  When forcefully removing images, containers using the image
// should be removed as well.  The callback allows for more graceful removal as
// we can use the libpod-internal removal logic.
func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage.RemoveContainerFunc {
	return func(imageID string) error {
		if !r.valid {
			return define.ErrRuntimeStopped
		}
		ctrs, err := r.state.AllContainers()
		if err != nil {
			return err
		}
		for _, ctr := range ctrs {
			if ctr.config.RootfsImageID != imageID {
				continue
			}
			var timeout *uint
			if ctr.config.IsInfra {
				pod, err := r.state.Pod(ctr.config.Pod)
				if err != nil {
					return fmt.Errorf("container %s is in pod %s, but pod cannot be retrieved: %w", ctr.ID(), ctr.config.Pod, err)
				}
				if err := r.removePod(ctx, pod, true, true, timeout); err != nil {
					return fmt.Errorf("removing image %s: container %s using image could not be removed: %w", imageID, ctr.ID(), err)
				}
			} else {
				if err := r.removeContainer(ctx, ctr, true, false, false, false, timeout); err != nil {
					return fmt.Errorf("removing image %s: container %s using image could not be removed: %w", imageID, ctr.ID(), err)
				}
			}
		}

		// Need to handle volumes with the image driver
		vols, err := r.state.AllVolumes()
		if err != nil {
			return err
		}
		for _, vol := range vols {
			if vol.config.Driver != define.VolumeDriverImage || vol.config.StorageImageID != imageID {
				continue
			}
			// Do a force removal of the volume, and all containers
			// using it.
			if err := r.RemoveVolume(ctx, vol, true, nil); err != nil {
				return fmt.Errorf("removing image %s: volume %s backed by image could not be removed: %w", imageID, vol.Name(), err)
			}
		}

		// Note that `libimage` will take care of removing any leftover
		// containers from the storage.
		return nil
	}
}

// IsExternalContainerCallback returns a callback that be used in `libimage` to
// figure out whether a given container is an external one.  A container is
// considered external if it is not present in libpod's database.
func (r *Runtime) IsExternalContainerCallback(_ context.Context) libimage.IsExternalContainerFunc {
	// NOTE: pruning external containers is subject to race conditions
	// (e.g., when a container gets removed). To address this and similar
	// races, pruning had to happen inside c/storage.  Containers has to be
	// labelled with "podman/libpod" along with callbacks similar to
	// libimage.
	return func(idOrName string) (bool, error) {
		_, err := r.LookupContainer(idOrName)
		if err == nil {
			return false, nil
		}
		if errors.Is(err, define.ErrNoSuchCtr) {
			return true, nil
		}
		isVol, err := r.state.ContainerIDIsVolume(idOrName)
		if err == nil && !isVol {
			return true, nil
		}
		return false, nil
	}
}

// newBuildEvent creates a new event based on completion of a built image
func (r *Runtime) newImageBuildCompleteEvent(idOrName string) {
	e := events.NewEvent(events.Build)
	e.Type = events.Image
	e.Name = idOrName
	if err := r.eventer.Write(e); err != nil {
		logrus.Errorf("Unable to write build event: %q", err)
	}
}

// Build adds the runtime to the imagebuildah call
func (r *Runtime) Build(ctx context.Context, options buildahDefine.BuildOptions, dockerfiles ...string) (string, reference.Canonical, error) {
	if options.Runtime == "" {
		options.Runtime = r.GetOCIRuntimePath()
	}
	// share the network interface between podman and buildah
	options.NetworkInterface = r.network
	id, ref, err := imagebuildah.BuildDockerfiles(ctx, r.store, options, dockerfiles...)
	// Write event for build completion
	r.newImageBuildCompleteEvent(id)
	return id, ref, err
}

// DownloadFromFile reads all of the content from the reader and temporarily
// saves in it $TMPDIR/importxyz, which is deleted after the image is imported
func DownloadFromFile(reader *os.File) (string, error) {
	outFile, err := os.CreateTemp(util.Tmpdir(), "import")
	if err != nil {
		return "", fmt.Errorf("creating file: %w", err)
	}
	defer outFile.Close()

	logrus.Debugf("saving %s to %s", reader.Name(), outFile.Name())

	_, err = io.Copy(outFile, reader)
	if err != nil {
		return "", fmt.Errorf("saving %s to %s: %w", reader.Name(), outFile.Name(), err)
	}

	return outFile.Name(), nil
}