-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
container.go
359 lines (310 loc) · 9.6 KB
/
container.go
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
package oci
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/storage/pkg/idtools"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"k8s.io/apimachinery/pkg/fields"
pb "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"k8s.io/kubernetes/pkg/kubelet/types"
)
const defaultStopSignalInt = 15
var defaultStopSignal = strconv.Itoa(defaultStopSignalInt)
var ErrContainerStopped = errors.New("container is already stopped")
// Container represents a runtime container.
type Container struct {
volumes []ContainerVolume
id string
name string
logPath string
image string
sandbox string
runtimeHandler string
// this is the /var/run/storage/... directory, erased on reboot
bundlePath string
// this is the /var/lib/storage/... directory
dir string
stopSignal string
imageName string
imageRef string
mountPoint string
seccompProfilePath string
conmonCgroupfsPath string
labels fields.Set
annotations fields.Set
crioAnnotations fields.Set
state *ContainerState
metadata *pb.ContainerMetadata
opLock sync.RWMutex
spec *specs.Spec
idMappings *idtools.IDMappings
terminal bool
stdin bool
stdinOnce bool
created bool
}
// ContainerVolume is a bind mount for the container.
type ContainerVolume struct {
ContainerPath string `json:"container_path"`
HostPath string `json:"host_path"`
Readonly bool `json:"readonly"`
}
// ContainerState represents the status of a container.
type ContainerState struct {
specs.State
Created time.Time `json:"created"`
Started time.Time `json:"started,omitempty"`
Finished time.Time `json:"finished,omitempty"`
ExitCode *int32 `json:"exitCode,omitempty"`
OOMKilled bool `json:"oomKilled,omitempty"`
Error string `json:"error,omitempty"`
}
// NewContainer creates a container object.
func NewContainer(id, name, bundlePath, logPath string, labels, crioAnnotations, annotations map[string]string, image, imageName, imageRef string, metadata *pb.ContainerMetadata, sandbox string, terminal, stdin, stdinOnce bool, runtimeHandler, dir string, created time.Time, stopSignal string) (*Container, error) {
state := &ContainerState{}
state.Created = created
c := &Container{
id: id,
name: name,
bundlePath: bundlePath,
logPath: logPath,
labels: labels,
sandbox: sandbox,
terminal: terminal,
stdin: stdin,
stdinOnce: stdinOnce,
runtimeHandler: runtimeHandler,
metadata: metadata,
annotations: annotations,
crioAnnotations: crioAnnotations,
image: image,
imageName: imageName,
imageRef: imageRef,
dir: dir,
state: state,
stopSignal: stopSignal,
}
return c, nil
}
// SetSpec loads the OCI spec in the container struct
func (c *Container) SetSpec(s *specs.Spec) {
c.spec = s
}
// Spec returns a copy of the spec for the container
func (c *Container) Spec() specs.Spec {
return *c.spec
}
// ConmonCgroupfsPath returns the path to conmon's cgroup. This is only set when
// cgroupfs is used as a cgroup manager.
func (c *Container) ConmonCgroupfsPath() string {
return c.conmonCgroupfsPath
}
// GetStopSignal returns the container's own stop signal configured from the
// image configuration or the default one.
func (c *Container) GetStopSignal() string {
if c.stopSignal == "" {
return defaultStopSignal
}
signal := unix.SignalNum(strings.ToUpper(c.stopSignal))
if signal == 0 {
return defaultStopSignal
}
// return the stop signal in the form of its int converted to a string
// i.e stop signal 34 is returned as "34" to avoid back and forth conversion
return strconv.Itoa(int(signal))
}
// StopSignal returns the container's own stop signal configured from
// the image configuration or the default one.
func (c *Container) StopSignal() syscall.Signal {
if c.stopSignal == "" {
return defaultStopSignalInt
}
signal := unix.SignalNum(strings.ToUpper(c.stopSignal))
if signal == 0 {
return defaultStopSignalInt
}
return signal
}
// FromDisk restores container's state from disk
func (c *Container) FromDisk() error {
jsonSource, err := os.Open(c.StatePath())
if err != nil {
return err
}
defer jsonSource.Close()
dec := json.NewDecoder(jsonSource)
return dec.Decode(c.state)
}
// StatePath returns the containers state.json path
func (c *Container) StatePath() string {
return filepath.Join(c.dir, "state.json")
}
// CreatedAt returns the container creation time
func (c *Container) CreatedAt() time.Time {
return c.state.Created
}
// Name returns the name of the container.
func (c *Container) Name() string {
return c.name
}
// ID returns the id of the container.
func (c *Container) ID() string {
if c == nil {
return ""
}
return c.id
}
// CleanupConmonCgroup cleans up conmon's group when using cgroupfs.
func (c *Container) CleanupConmonCgroup() {
path := c.ConmonCgroupfsPath()
if path == "" {
return
}
cg, err := cgroups.Load(path)
if err != nil {
logrus.Infof("error loading conmon cgroup of container %s: %v", c.ID(), err)
return
}
if err := cg.Delete(); err != nil {
logrus.Infof("error deleting conmon cgroup of container %s: %v", c.ID(), err)
}
}
// SetSeccompProfilePath sets the seccomp profile path
func (c *Container) SetSeccompProfilePath(pp string) {
c.seccompProfilePath = pp
}
// SeccompProfilePath returns the seccomp profile path
func (c *Container) SeccompProfilePath() string {
return c.seccompProfilePath
}
// BundlePath returns the bundlePath of the container.
func (c *Container) BundlePath() string {
return c.bundlePath
}
// LogPath returns the log path of the container.
func (c *Container) LogPath() string {
return c.logPath
}
// Labels returns the labels of the container.
func (c *Container) Labels() map[string]string {
return c.labels
}
// Annotations returns the annotations of the container.
func (c *Container) Annotations() map[string]string {
return c.annotations
}
// CrioAnnotations returns the crio annotations of the container.
func (c *Container) CrioAnnotations() map[string]string {
return c.crioAnnotations
}
// Image returns the image of the container.
func (c *Container) Image() string {
return c.image
}
// ImageName returns the image name of the container.
func (c *Container) ImageName() string {
return c.imageName
}
// ImageRef returns the image ref of the container.
func (c *Container) ImageRef() string {
return c.imageRef
}
// Sandbox returns the sandbox name of the container.
func (c *Container) Sandbox() string {
return c.sandbox
}
// Dir returns the dir of the container
func (c *Container) Dir() string {
return c.dir
}
// Metadata returns the metadata of the container.
func (c *Container) Metadata() *pb.ContainerMetadata {
return c.metadata
}
// State returns the state of the running container
func (c *Container) State() *ContainerState {
c.opLock.RLock()
defer c.opLock.RUnlock()
return c.state
}
// StateNoLock returns the state of a container without using a lock.
func (c *Container) StateNoLock() *ContainerState {
return c.state
}
// AddVolume adds a volume to list of container volumes.
func (c *Container) AddVolume(v ContainerVolume) {
c.volumes = append(c.volumes, v)
}
// Volumes returns the list of container volumes.
func (c *Container) Volumes() []ContainerVolume {
return c.volumes
}
// SetMountPoint sets the container mount point
func (c *Container) SetMountPoint(mp string) {
c.mountPoint = mp
}
// MountPoint returns the container mount point
func (c *Container) MountPoint() string {
return c.mountPoint
}
// SetIDMappings sets the ID/GID mappings used for the container
func (c *Container) SetIDMappings(mappings *idtools.IDMappings) {
c.idMappings = mappings
}
// IDMappings returns the ID/GID mappings used for the container
func (c *Container) IDMappings() *idtools.IDMappings {
return c.idMappings
}
// SetCreated sets the created flag to true once container is created
func (c *Container) SetCreated() {
c.created = true
}
// Created returns whether the container was created successfully
func (c *Container) Created() bool {
return c.created
}
// SetStartFailed sets the container state appropriately after a start failure
func (c *Container) SetStartFailed(err error) {
c.opLock.Lock()
defer c.opLock.Unlock()
// adjust finished and started times
c.state.Finished, c.state.Started = c.state.Created, c.state.Created
if err != nil {
c.state.Error = err.Error()
}
}
// Description returns a description for the container
func (c *Container) Description() string {
return fmt.Sprintf("%s/%s/%s", c.Labels()[types.KubernetesPodNamespaceLabel], c.Labels()[types.KubernetesPodNameLabel], c.Labels()[types.KubernetesContainerNameLabel])
}
// StdinOnce returns whether stdin once is set for the container.
func (c *Container) StdinOnce() bool {
return c.stdinOnce
}
func (c *Container) exitFilePath() string {
return filepath.Join(c.dir, "exit")
}
// ShouldBeStopped checks whether the container state is in a place
// where attempting to stop it makes sense
// a container is not stoppable if it's paused or stopped
// if it's paused, that's an error, and is reported as such
func (c *Container) ShouldBeStopped() error {
switch c.state.Status {
case ContainerStateStopped: // no-op
return ErrContainerStopped
case ContainerStatePaused:
return errors.New("cannot stop paused container")
}
return nil
}