summaryrefslogtreecommitdiff
path: root/vendor/github.com/Microsoft/hcsshim/container.go
blob: 53c0a3854a481c4504b72877ea771ce6ade6078b (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
package hcsshim

import (
	"context"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/Microsoft/hcsshim/internal/hcs"
	"github.com/Microsoft/hcsshim/internal/mergemaps"
	"github.com/Microsoft/hcsshim/internal/schema1"
)

// ContainerProperties holds the properties for a container and the processes running in that container
type ContainerProperties = schema1.ContainerProperties

// MemoryStats holds the memory statistics for a container
type MemoryStats = schema1.MemoryStats

// ProcessorStats holds the processor statistics for a container
type ProcessorStats = schema1.ProcessorStats

// StorageStats holds the storage statistics for a container
type StorageStats = schema1.StorageStats

// NetworkStats holds the network statistics for a container
type NetworkStats = schema1.NetworkStats

// Statistics is the structure returned by a statistics call on a container
type Statistics = schema1.Statistics

// ProcessList is the structure of an item returned by a ProcessList call on a container
type ProcessListItem = schema1.ProcessListItem

// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container
type MappedVirtualDiskController = schema1.MappedVirtualDiskController

// Type of Request Support in ModifySystem
type RequestType = schema1.RequestType

// Type of Resource Support in ModifySystem
type ResourceType = schema1.ResourceType

// RequestType const
const (
	Add     = schema1.Add
	Remove  = schema1.Remove
	Network = schema1.Network
)

// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system
// Supported resource types are Network and Request Types are Add/Remove
type ResourceModificationRequestResponse = schema1.ResourceModificationRequestResponse

type container struct {
	system   *hcs.System
	waitOnce sync.Once
	waitErr  error
	waitCh   chan struct{}
}

// createComputeSystemAdditionalJSON is read from the environment at initialisation
// time. It allows an environment variable to define additional JSON which
// is merged in the CreateComputeSystem call to HCS.
var createContainerAdditionalJSON []byte

func init() {
	createContainerAdditionalJSON = ([]byte)(os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON"))
}

// CreateContainer creates a new container with the given configuration but does not start it.
func CreateContainer(id string, c *ContainerConfig) (Container, error) {
	fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON)
	if err != nil {
		return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err)
	}

	system, err := hcs.CreateComputeSystem(context.Background(), id, fullConfig)
	if err != nil {
		return nil, err
	}
	return &container{system: system}, err
}

// OpenContainer opens an existing container by ID.
func OpenContainer(id string) (Container, error) {
	system, err := hcs.OpenComputeSystem(context.Background(), id)
	if err != nil {
		return nil, err
	}
	return &container{system: system}, err
}

// GetContainers gets a list of the containers on the system that match the query
func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) {
	return hcs.GetComputeSystems(context.Background(), q)
}

// Start synchronously starts the container.
func (container *container) Start() error {
	return convertSystemError(container.system.Start(context.Background()), container)
}

// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
func (container *container) Shutdown() error {
	err := container.system.Shutdown(context.Background())
	if err != nil {
		return convertSystemError(err, container)
	}
	return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Shutdown"}
}

// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
func (container *container) Terminate() error {
	err := container.system.Terminate(context.Background())
	if err != nil {
		return convertSystemError(err, container)
	}
	return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Terminate"}
}

// Waits synchronously waits for the container to shutdown or terminate.
func (container *container) Wait() error {
	err := container.system.Wait()
	if err == nil {
		err = container.system.ExitError()
	}
	return convertSystemError(err, container)
}

// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
// returns false if timeout occurs.
func (container *container) WaitTimeout(timeout time.Duration) error {
	container.waitOnce.Do(func() {
		container.waitCh = make(chan struct{})
		go func() {
			container.waitErr = container.Wait()
			close(container.waitCh)
		}()
	})
	t := time.NewTimer(timeout)
	defer t.Stop()
	select {
	case <-t.C:
		return &ContainerError{Container: container, Err: ErrTimeout, Operation: "hcsshim::ComputeSystem::Wait"}
	case <-container.waitCh:
		return container.waitErr
	}
}

// Pause pauses the execution of a container.
func (container *container) Pause() error {
	return convertSystemError(container.system.Pause(context.Background()), container)
}

// Resume resumes the execution of a container.
func (container *container) Resume() error {
	return convertSystemError(container.system.Resume(context.Background()), container)
}

// HasPendingUpdates returns true if the container has updates pending to install
func (container *container) HasPendingUpdates() (bool, error) {
	return false, nil
}

// Statistics returns statistics for the container. This is a legacy v1 call
func (container *container) Statistics() (Statistics, error) {
	properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeStatistics)
	if err != nil {
		return Statistics{}, convertSystemError(err, container)
	}

	return properties.Statistics, nil
}

// ProcessList returns an array of ProcessListItems for the container. This is a legacy v1 call
func (container *container) ProcessList() ([]ProcessListItem, error) {
	properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeProcessList)
	if err != nil {
		return nil, convertSystemError(err, container)
	}

	return properties.ProcessList, nil
}

// This is a legacy v1 call
func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) {
	properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeMappedVirtualDisk)
	if err != nil {
		return nil, convertSystemError(err, container)
	}

	return properties.MappedVirtualDiskControllers, nil
}

// CreateProcess launches a new process within the container.
func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
	p, err := container.system.CreateProcessNoStdio(c)
	if err != nil {
		return nil, convertSystemError(err, container)
	}
	return &process{p: p.(*hcs.Process)}, nil
}

// OpenProcess gets an interface to an existing process within the container.
func (container *container) OpenProcess(pid int) (Process, error) {
	p, err := container.system.OpenProcess(context.Background(), pid)
	if err != nil {
		return nil, convertSystemError(err, container)
	}
	return &process{p: p}, nil
}

// Close cleans up any state associated with the container but does not terminate or wait for it.
func (container *container) Close() error {
	return convertSystemError(container.system.Close(), container)
}

// Modify the System
func (container *container) Modify(config *ResourceModificationRequestResponse) error {
	return convertSystemError(container.system.Modify(context.Background(), config), container)
}