summaryrefslogtreecommitdiff
path: root/libpod/oci_missing.go
blob: 6a756757fa0397a04e54b1e0e26d35859c88270f (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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
package libpod

import (
	"fmt"
	"net/http"
	"path/filepath"
	"sync"

	"github.com/containers/podman/v4/libpod/define"
	"github.com/sirupsen/logrus"
)

var (
	// Only create each missing runtime once.
	// Creation makes error messages we don't want to duplicate.
	missingRuntimes map[string]*MissingRuntime
	// We need a lock for this
	missingRuntimesLock sync.Mutex
)

// MissingRuntime is used when the OCI runtime requested by the container is
// missing (not installed or not in the configuration file).
type MissingRuntime struct {
	// Name is the name of the missing runtime. Will be used in errors.
	name string
	// exitsDir is the directory for exit files.
	exitsDir string
}

// Get a new MissingRuntime for the given name.
// Requires a libpod Runtime so we can make a sane path for the exits dir.
func getMissingRuntime(name string, r *Runtime) OCIRuntime {
	missingRuntimesLock.Lock()
	defer missingRuntimesLock.Unlock()

	if missingRuntimes == nil {
		missingRuntimes = make(map[string]*MissingRuntime)
	}

	runtime, ok := missingRuntimes[name]
	if ok {
		return runtime
	}

	// Once for each missing runtime, we want to error.
	logrus.Errorf("OCI Runtime %s is in use by a container, but is not available (not in configuration file or not installed)", name)

	newRuntime := new(MissingRuntime)
	newRuntime.name = name
	newRuntime.exitsDir = filepath.Join(r.config.Engine.TmpDir, "exits")

	missingRuntimes[name] = newRuntime

	return newRuntime
}

// Name is the name of the missing runtime
func (r *MissingRuntime) Name() string {
	return fmt.Sprintf("%s (missing/not available)", r.name)
}

// Path is not available as the runtime is missing
func (r *MissingRuntime) Path() string {
	return "(missing/not available)"
}

// CreateContainer is not available as the runtime is missing
func (r *MissingRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (int64, error) {
	return 0, r.printError()
}

// UpdateContainerStatus is not available as the runtime is missing
func (r *MissingRuntime) UpdateContainerStatus(ctr *Container) error {
	return r.printError()
}

// StartContainer is not available as the runtime is missing
func (r *MissingRuntime) StartContainer(ctr *Container) error {
	return r.printError()
}

// KillContainer is not available as the runtime is missing
// TODO: We could attempt to unix.Kill() the PID as recorded in the state if we
// really want to smooth things out? Won't be perfect, but if the container has
// a PID namespace it could be enough?
func (r *MissingRuntime) KillContainer(ctr *Container, signal uint, all bool) error {
	return r.printError()
}

// StopContainer is not available as the runtime is missing
func (r *MissingRuntime) StopContainer(ctr *Container, timeout uint, all bool) error {
	return r.printError()
}

// DeleteContainer is not available as the runtime is missing
func (r *MissingRuntime) DeleteContainer(ctr *Container) error {
	return r.printError()
}

// PauseContainer is not available as the runtime is missing
func (r *MissingRuntime) PauseContainer(ctr *Container) error {
	return r.printError()
}

// UnpauseContainer is not available as the runtime is missing
func (r *MissingRuntime) UnpauseContainer(ctr *Container) error {
	return r.printError()
}

// Attach is not available as the runtime is missing
func (r *MissingRuntime) Attach(ctr *Container, params *AttachOptions) error {
	return r.printError()
}

// HTTPAttach is not available as the runtime is missing
func (r *MissingRuntime) HTTPAttach(ctr *Container, req *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, streamAttach, streamLogs bool) error {
	return r.printError()
}

// AttachResize is not available as the runtime is missing
func (r *MissingRuntime) AttachResize(ctr *Container, newSize define.TerminalSize) error {
	return r.printError()
}

// ExecContainer is not available as the runtime is missing
func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) {
	return -1, nil, r.printError()
}

// ExecContainerHTTP is not available as the runtime is missing
func (r *MissingRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter,
	streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) {
	return -1, nil, r.printError()
}

// ExecContainerDetached is not available as the runtime is missing
func (r *MissingRuntime) ExecContainerDetached(ctr *Container, sessionID string, options *ExecOptions, stdin bool) (int, error) {
	return -1, r.printError()
}

// ExecAttachResize is not available as the runtime is missing.
func (r *MissingRuntime) ExecAttachResize(ctr *Container, sessionID string, newSize define.TerminalSize) error {
	return r.printError()
}

// ExecStopContainer is not available as the runtime is missing.
// TODO: We can also investigate using unix.Kill() on the PID of the exec
// session here if we want to make stopping containers possible. Won't be
// perfect, though.
func (r *MissingRuntime) ExecStopContainer(ctr *Container, sessionID string, timeout uint) error {
	return r.printError()
}

// ExecUpdateStatus is not available as the runtime is missing.
func (r *MissingRuntime) ExecUpdateStatus(ctr *Container, sessionID string) (bool, error) {
	return false, r.printError()
}

// CheckpointContainer is not available as the runtime is missing
func (r *MissingRuntime) CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) (int64, error) {
	return 0, r.printError()
}

// CheckConmonRunning is not available as the runtime is missing
func (r *MissingRuntime) CheckConmonRunning(ctr *Container) (bool, error) {
	return false, r.printError()
}

// SupportsCheckpoint returns false as checkpointing requires a working runtime
func (r *MissingRuntime) SupportsCheckpoint() bool {
	return false
}

// SupportsJSONErrors returns false as there is no runtime to give errors
func (r *MissingRuntime) SupportsJSONErrors() bool {
	return false
}

// SupportsNoCgroups returns false as there is no runtime to create containers
func (r *MissingRuntime) SupportsNoCgroups() bool {
	return false
}

// SupportsKVM checks if the OCI runtime supports running containers
// without KVM separation
func (r *MissingRuntime) SupportsKVM() bool {
	return false
}

// AttachSocketPath does not work as there is no runtime to attach to.
// (Theoretically we could follow ExitFilePath but there is no guarantee the
// container is running and thus has an attach socket...)
func (r *MissingRuntime) AttachSocketPath(ctr *Container) (string, error) {
	return "", r.printError()
}

// ExecAttachSocketPath does not work as there is no runtime to attach to.
// (Again, we could follow ExitFilePath, but no guarantee there is an existing
// and running exec session)
func (r *MissingRuntime) ExecAttachSocketPath(ctr *Container, sessionID string) (string, error) {
	return "", r.printError()
}

// ExitFilePath returns the exit file path for containers.
// Here, we mimic what ConmonOCIRuntime does, because there is a chance that the
// container in question is still running happily (config file modified to
// remove a runtime, for example). We can't find the runtime to do anything to
// the container, but Conmon should still place an exit file for it.
func (r *MissingRuntime) ExitFilePath(ctr *Container) (string, error) {
	if ctr == nil {
		return "", fmt.Errorf("must provide a valid container to get exit file path: %w", define.ErrInvalidArg)
	}
	return filepath.Join(r.exitsDir, ctr.ID()), nil
}

// RuntimeInfo returns information on the missing runtime
func (r *MissingRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
	ocirt := define.OCIRuntimeInfo{
		Name:    r.name,
		Path:    "missing",
		Package: "missing",
		Version: "missing",
	}
	return nil, &ocirt, nil
}

// Return an error indicating the runtime is missing
func (r *MissingRuntime) printError() error {
	return fmt.Errorf("runtime %s is missing: %w", r.name, define.ErrOCIRuntimeNotFound)
}