podman

Форк
0
/
util.go 
290 строк · 9.1 Кб
1
//go:build !remote
2

3
package libpod
4

5
import (
6
	"bufio"
7
	"encoding/binary"
8
	"errors"
9
	"fmt"
10
	"io"
11
	"net/http"
12
	"os"
13
	"path/filepath"
14
	"sort"
15
	"strconv"
16
	"strings"
17
	"time"
18

19
	"github.com/containers/common/libnetwork/types"
20
	"github.com/containers/common/pkg/config"
21
	"github.com/containers/podman/v5/libpod/define"
22
	"github.com/containers/podman/v5/pkg/api/handlers/utils/apiutil"
23
	"github.com/containers/storage/pkg/fileutils"
24
	spec "github.com/opencontainers/runtime-spec/specs-go"
25
	"github.com/opencontainers/selinux/go-selinux/label"
26
	"github.com/sirupsen/logrus"
27
	"golang.org/x/sys/unix"
28
)
29

30
// FuncTimer helps measure the execution time of a function
31
// For debug purposes, do not leave in code
32
// used like defer FuncTimer("foo")
33
func FuncTimer(funcName string) {
34
	elapsed := time.Since(time.Now())
35
	fmt.Printf("%s executed in %d ms\n", funcName, elapsed)
36
}
37

38
// MountExists returns true if dest exists in the list of mounts
39
func MountExists(specMounts []spec.Mount, dest string) bool {
40
	for _, m := range specMounts {
41
		if m.Destination == dest {
42
			return true
43
		}
44
	}
45
	return false
46
}
47

48
type byDestination []spec.Mount
49

50
func (m byDestination) Len() int {
51
	return len(m)
52
}
53

54
func (m byDestination) Less(i, j int) bool {
55
	return m.parts(i) < m.parts(j)
56
}
57

58
func (m byDestination) Swap(i, j int) {
59
	m[i], m[j] = m[j], m[i]
60
}
61

62
func (m byDestination) parts(i int) int {
63
	return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
64
}
65

66
func sortMounts(m []spec.Mount) []spec.Mount {
67
	sort.Sort(byDestination(m))
68
	return m
69
}
70

71
func validPodNSOption(p *Pod, ctrPod string) error {
72
	if p == nil {
73
		return fmt.Errorf("pod passed in was nil. Container may not be associated with a pod: %w", define.ErrInvalidArg)
74
	}
75

76
	if ctrPod == "" {
77
		return fmt.Errorf("container is not a member of any pod: %w", define.ErrInvalidArg)
78
	}
79

80
	if ctrPod != p.ID() {
81
		return fmt.Errorf("pod passed in is not the pod the container is associated with: %w", define.ErrInvalidArg)
82
	}
83
	return nil
84
}
85

86
// JSONDeepCopy performs a deep copy by performing a JSON encode/decode of the
87
// given structures. From and To should be identically typed structs.
88
func JSONDeepCopy(from, to interface{}) error {
89
	tmp, err := json.Marshal(from)
90
	if err != nil {
91
		return err
92
	}
93
	return json.Unmarshal(tmp, to)
94
}
95

96
// DefaultSeccompPath returns the path to the default seccomp.json file
97
// if it exists, first it checks OverrideSeccomp and then default.
98
// If neither exist function returns ""
99
func DefaultSeccompPath() (string, error) {
100
	def, err := config.Default()
101
	if err != nil {
102
		return "", err
103
	}
104
	if def.Containers.SeccompProfile != "" {
105
		return def.Containers.SeccompProfile, nil
106
	}
107

108
	err = fileutils.Exists(config.SeccompOverridePath)
109
	if err == nil {
110
		return config.SeccompOverridePath, nil
111
	}
112
	if !os.IsNotExist(err) {
113
		return "", err
114
	}
115
	if err := fileutils.Exists(config.SeccompDefaultPath); err != nil {
116
		if !os.IsNotExist(err) {
117
			return "", err
118
		}
119
		return "", nil
120
	}
121
	return config.SeccompDefaultPath, nil
122
}
123

124
// CheckDependencyContainer verifies the given container can be used as a
125
// dependency of another container.
126
// Both the dependency to check and the container that will be using the
127
// dependency must be passed in.
128
// It is assumed that ctr is locked, and depCtr is unlocked.
129
func checkDependencyContainer(depCtr, ctr *Container) error {
130
	state, err := depCtr.State()
131
	if err != nil {
132
		return fmt.Errorf("accessing dependency container %s state: %w", depCtr.ID(), err)
133
	}
134
	if state == define.ContainerStateRemoving {
135
		return fmt.Errorf("cannot use container %s as a dependency as it is being removed: %w", depCtr.ID(), define.ErrCtrStateInvalid)
136
	}
137

138
	if depCtr.ID() == ctr.ID() {
139
		return fmt.Errorf("must specify another container: %w", define.ErrInvalidArg)
140
	}
141

142
	if ctr.config.Pod != "" && depCtr.PodID() != ctr.config.Pod {
143
		return fmt.Errorf("container has joined pod %s and dependency container %s is not a member of the pod: %w", ctr.config.Pod, depCtr.ID(), define.ErrInvalidArg)
144
	}
145

146
	return nil
147
}
148

149
// hijackWriteError writes an error to a hijacked HTTP session.
150
func hijackWriteError(toWrite error, cid string, terminal bool, httpBuf *bufio.ReadWriter) {
151
	if toWrite != nil {
152
		errString := []byte(fmt.Sprintf("Error: %v\n", toWrite))
153
		if !terminal {
154
			// We need a header.
155
			header := makeHTTPAttachHeader(2, uint32(len(errString)))
156
			if _, err := httpBuf.Write(header); err != nil {
157
				logrus.Errorf("Writing header for container %s attach connection error: %v", cid, err)
158
			}
159
		}
160
		if _, err := httpBuf.Write(errString); err != nil {
161
			logrus.Errorf("Writing error to container %s HTTP attach connection: %v", cid, err)
162
		}
163
		if err := httpBuf.Flush(); err != nil {
164
			logrus.Errorf("Flushing HTTP buffer for container %s HTTP attach connection: %v", cid, err)
165
		}
166
	}
167
}
168

169
// hijackWriteErrorAndClose writes an error to a hijacked HTTP session and
170
// closes it. Intended to HTTPAttach function.
171
// If error is nil, it will not be written; we'll only close the connection.
172
func hijackWriteErrorAndClose(toWrite error, cid string, terminal bool, httpCon io.Closer, httpBuf *bufio.ReadWriter) {
173
	hijackWriteError(toWrite, cid, terminal, httpBuf)
174

175
	if err := httpCon.Close(); err != nil {
176
		logrus.Errorf("Closing container %s HTTP attach connection: %v", cid, err)
177
	}
178
}
179

180
// makeHTTPAttachHeader makes an 8-byte HTTP header for a buffer of the given
181
// length and stream. Accepts an integer indicating which stream we are sending
182
// to (STDIN = 0, STDOUT = 1, STDERR = 2).
183
func makeHTTPAttachHeader(stream byte, length uint32) []byte {
184
	header := make([]byte, 8)
185
	header[0] = stream
186
	binary.BigEndian.PutUint32(header[4:], length)
187
	return header
188
}
189

190
// writeHijackHeader writes a header appropriate for the type of HTTP Hijack
191
// that occurred in a hijacked HTTP connection used for attach.
192
func writeHijackHeader(r *http.Request, conn io.Writer, tty bool) {
193
	// AttachHeader is the literal header sent for upgraded/hijacked connections for
194
	// attach, sourced from Docker at:
195
	// https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
196
	// Using literally to ensure compatibility with existing clients.
197

198
	// New docker API uses a different header for the non tty case.
199
	// Lets do the same for libpod. Only do this for the new api versions to not break older clients.
200
	header := "application/vnd.docker.raw-stream"
201
	if !tty {
202
		version := "4.7.0"
203
		if !apiutil.IsLibpodRequest(r) {
204
			version = "1.42.0" // docker only used two digest "1.42" but our semver lib needs the extra .0 to work
205
		}
206
		if _, err := apiutil.SupportedVersion(r, ">= "+version); err == nil {
207
			header = "application/vnd.docker.multiplexed-stream"
208
		}
209
	}
210

211
	c := r.Header.Get("Connection")
212
	proto := r.Header.Get("Upgrade")
213
	if len(proto) == 0 || !strings.EqualFold(c, "Upgrade") {
214
		// OK - can't upgrade if not requested or protocol is not specified
215
		fmt.Fprintf(conn,
216
			"HTTP/1.1 200 OK\r\nContent-Type: %s\r\n\r\n", header)
217
	} else {
218
		// Upgraded
219
		fmt.Fprintf(conn,
220
			"HTTP/1.1 101 UPGRADED\r\nContent-Type: %s\r\nConnection: Upgrade\r\nUpgrade: %s\r\n\r\n",
221
			header, proto)
222
	}
223
}
224

225
// Convert OCICNI port bindings into Inspect-formatted port bindings.
226
func makeInspectPortBindings(bindings []types.PortMapping) map[string][]define.InspectHostPort {
227
	return makeInspectPorts(bindings, nil)
228
}
229

230
// Convert OCICNI port bindings into Inspect-formatted port bindings with exposed, but not bound ports set to nil.
231
func makeInspectPorts(bindings []types.PortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort {
232
	portBindings := make(map[string][]define.InspectHostPort)
233
	for _, port := range bindings {
234
		protocols := strings.Split(port.Protocol, ",")
235
		for _, protocol := range protocols {
236
			for i := uint16(0); i < port.Range; i++ {
237
				key := fmt.Sprintf("%d/%s", port.ContainerPort+i, protocol)
238
				hostPorts := portBindings[key]
239
				var hostIP = port.HostIP
240
				if len(port.HostIP) == 0 {
241
					hostIP = "0.0.0.0"
242
				}
243
				hostPorts = append(hostPorts, define.InspectHostPort{
244
					HostIP:   hostIP,
245
					HostPort: strconv.FormatUint(uint64(port.HostPort+i), 10),
246
				})
247
				portBindings[key] = hostPorts
248
			}
249
		}
250
	}
251
	// add exposed ports without host port information to match docker
252
	for port, protocols := range expose {
253
		for _, protocol := range protocols {
254
			key := fmt.Sprintf("%d/%s", port, protocol)
255
			if _, ok := portBindings[key]; !ok {
256
				portBindings[key] = nil
257
			}
258
		}
259
	}
260
	return portBindings
261
}
262

263
// Write a given string to a new file at a given path.
264
// Will error if a file with the given name already exists.
265
// Will be chown'd to the UID/GID provided and have the provided SELinux label
266
// set.
267
func writeStringToPath(path, contents, mountLabel string, uid, gid int) error {
268
	f, err := os.Create(path)
269
	if err != nil {
270
		return fmt.Errorf("unable to create %s: %w", path, err)
271
	}
272
	defer f.Close()
273
	if err := f.Chown(uid, gid); err != nil {
274
		return err
275
	}
276

277
	if _, err := f.WriteString(contents); err != nil {
278
		return fmt.Errorf("unable to write %s: %w", path, err)
279
	}
280
	// Relabel runDirResolv for the container
281
	if err := label.Relabel(path, mountLabel, false); err != nil {
282
		if errors.Is(err, unix.ENOTSUP) {
283
			logrus.Debugf("Labeling not supported on %q", path)
284
			return nil
285
		}
286
		return err
287
	}
288

289
	return nil
290
}
291

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

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

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

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