Browse code

Revendor Microsoft\hcsshim @ v0.5.17

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2017/05/09 02:30:41
Showing 10 changed files
... ...
@@ -1,6 +1,6 @@
1 1
 # the following lines are in sorted order, FYI
2 2
 github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62
3
-github.com/Microsoft/hcsshim v0.5.13
3
+github.com/Microsoft/hcsshim v0.5.17
4 4
 github.com/Microsoft/go-winio v0.3.9
5 5
 github.com/Sirupsen/logrus v0.11.0
6 6
 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
... ...
@@ -18,5 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 18
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 19
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 20
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
-SOFTWARE.
22
-
21
+SOFTWARE.
23 22
\ No newline at end of file
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6 6
 	"os"
7
-	"runtime"
8 7
 	"sync"
9 8
 	"syscall"
10 9
 	"time"
... ...
@@ -105,6 +104,27 @@ type ProcessListItem struct {
105 105
 	UserTime100ns                uint64    `json:",omitempty"`
106 106
 }
107 107
 
108
+// Type of Request Support in ModifySystem
109
+type RequestType string
110
+
111
+// Type of Resource Support in ModifySystem
112
+type ResourceType string
113
+
114
+// RequestType const
115
+const (
116
+	Add     RequestType  = "Add"
117
+	Remove  RequestType  = "Remove"
118
+	Network ResourceType = "Network"
119
+)
120
+
121
+// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system
122
+// Supported resource types are Network and Request Types are Add/Remove
123
+type ResourceModificationRequestResponse struct {
124
+	Resource ResourceType `json:"ResourceType"`
125
+	Data     string       `json:"Settings"`
126
+	Request  RequestType  `json:"RequestType,omitempty"`
127
+}
128
+
108 129
 // createContainerAdditionalJSON is read from the environment at initialisation
109 130
 // time. It allows an environment variable to define additional JSON which
110 131
 // is merged in the CreateContainer call to HCS.
... ...
@@ -185,7 +205,6 @@ func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON strin
185 185
 	}
186 186
 
187 187
 	logrus.Debugf(title+" succeeded id=%s handle=%d", id, container.handle)
188
-	runtime.SetFinalizer(container, closeContainer)
189 188
 	return container, nil
190 189
 }
191 190
 
... ...
@@ -243,7 +262,6 @@ func OpenContainer(id string) (Container, error) {
243 243
 	}
244 244
 
245 245
 	logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle)
246
-	runtime.SetFinalizer(container, closeContainer)
247 246
 	return container, nil
248 247
 }
249 248
 
... ...
@@ -568,7 +586,6 @@ func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
568 568
 	}
569 569
 
570 570
 	logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
571
-	runtime.SetFinalizer(process, closeProcess)
572 571
 	return process, nil
573 572
 }
574 573
 
... ...
@@ -605,7 +622,6 @@ func (container *container) OpenProcess(pid int) (Process, error) {
605 605
 	}
606 606
 
607 607
 	logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
608
-	runtime.SetFinalizer(process, closeProcess)
609 608
 	return process, nil
610 609
 }
611 610
 
... ...
@@ -631,17 +647,11 @@ func (container *container) Close() error {
631 631
 	}
632 632
 
633 633
 	container.handle = 0
634
-	runtime.SetFinalizer(container, nil)
635 634
 
636 635
 	logrus.Debugf(title+" succeeded id=%s", container.id)
637 636
 	return nil
638 637
 }
639 638
 
640
-// closeContainer wraps container.Close for use by a finalizer
641
-func closeContainer(container *container) {
642
-	container.Close()
643
-}
644
-
645 639
 func (container *container) registerCallback() error {
646 640
 	context := &notifcationWatcherContext{
647 641
 		channels: newChannels(),
... ...
@@ -698,3 +708,32 @@ func (container *container) unregisterCallback() error {
698 698
 
699 699
 	return nil
700 700
 }
701
+
702
+// Modifies the System by sending a request to HCS
703
+func (container *container) Modify(config *ResourceModificationRequestResponse) error {
704
+	container.handleLock.RLock()
705
+	defer container.handleLock.RUnlock()
706
+	operation := "Modify"
707
+	title := "HCSShim::Container::" + operation
708
+
709
+	if container.handle == 0 {
710
+		return makeContainerError(container, operation, "", ErrAlreadyClosed)
711
+	}
712
+
713
+	requestJSON, err := json.Marshal(config)
714
+	if err != nil {
715
+		return err
716
+	}
717
+
718
+	requestString := string(requestJSON)
719
+	logrus.Debugf(title+" id=%s request=%s", container.id, requestString)
720
+
721
+	var resultp *uint16
722
+	err = hcsModifyComputeSystem(container.handle, requestString, &resultp)
723
+	err = processHcsResult(err, resultp)
724
+	if err != nil {
725
+		return makeContainerError(container, operation, "", err)
726
+	}
727
+	logrus.Debugf(title+" succeeded id=%s", container.id)
728
+	return nil
729
+}
... ...
@@ -13,6 +13,13 @@ var (
13 13
 	// ErrElementNotFound is an error encountered when the object being referenced does not exist
14 14
 	ErrElementNotFound = syscall.Errno(0x490)
15 15
 
16
+	// ErrElementNotFound is an error encountered when the object being referenced does not exist
17
+	ErrNotSupported = syscall.Errno(0x32)
18
+
19
+	// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
20
+	// decimal -2147024883 / hex 0x8007000d
21
+	ErrInvalidData = syscall.Errno(0xd)
22
+
16 23
 	// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
17 24
 	ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed")
18 25
 
... ...
@@ -54,6 +61,15 @@ var (
54 54
 	// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
55 55
 	// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
56 56
 	ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5)
57
+
58
+	// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
59
+	ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d)
60
+
61
+	// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
62
+	ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b)
63
+
64
+	// ErrNotSupported is an error encountered when hcs doesn't support the request
65
+	ErrPlatformNotSupported = errors.New("unsupported platform request")
57 66
 )
58 67
 
59 68
 // ProcessError is an error encountered in HCS during an operation on a Process object
... ...
@@ -196,6 +212,20 @@ func IsAlreadyStopped(err error) bool {
196 196
 		err == ErrProcNotFound
197 197
 }
198 198
 
199
+// IsNotSupported returns a boolean indicating whether the error is caused by
200
+// unsupported platform requests
201
+// Note: Currently Unsupported platform requests can be mean either
202
+// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage
203
+// is thrown from the Platform
204
+func IsNotSupported(err error) bool {
205
+	err = getInnerError(err)
206
+	// If Platform doesn't recognize or support the request sent, below errors are seen
207
+	return err == ErrVmcomputeInvalidJSON ||
208
+		err == ErrInvalidData ||
209
+		err == ErrNotSupported ||
210
+		err == ErrVmcomputeUnknownMessage
211
+}
212
+
199 213
 func getInnerError(err error) error {
200 214
 	switch pe := err.(type) {
201 215
 	case nil:
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"io"
5 5
 	"io/ioutil"
6 6
 	"os"
7
-	"runtime"
8 7
 	"syscall"
9 8
 
10 9
 	"github.com/Microsoft/go-winio"
... ...
@@ -143,7 +142,6 @@ func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string)
143 143
 	if err != nil {
144 144
 		return nil, makeError(err, "ExportLayerBegin", "")
145 145
 	}
146
-	runtime.SetFinalizer(r, func(r *FilterLayerReader) { r.Close() })
147 146
 	return r, err
148 147
 }
149 148
 
... ...
@@ -98,6 +98,8 @@ type hnsResponse struct {
98 98
 
99 99
 func hnsCall(method, path, request string, returnResponse interface{}) error {
100 100
 	var responseBuffer *uint16
101
+	logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request)
102
+
101 103
 	err := _hnsCall(method, path, request, &responseBuffer)
102 104
 	if err != nil {
103 105
 		return makeError(err, "hnsCall ", "")
... ...
@@ -158,3 +160,112 @@ func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) {
158 158
 
159 159
 	return endpoint, nil
160 160
 }
161
+
162
+// HNSListEndpointRequest makes a HNS call to query the list of available endpoints
163
+func HNSListEndpointRequest() ([]HNSEndpoint, error) {
164
+	var endpoint []HNSEndpoint
165
+	err := hnsCall("GET", "/endpoints/", "", &endpoint)
166
+	if err != nil {
167
+		return nil, err
168
+	}
169
+
170
+	return endpoint, nil
171
+}
172
+
173
+// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
174
+func HotAttachEndpoint(containerID string, endpointID string) error {
175
+	return modifyNetworkEndpoint(containerID, endpointID, Add)
176
+}
177
+
178
+// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
179
+func HotDetachEndpoint(containerID string, endpointID string) error {
180
+	return modifyNetworkEndpoint(containerID, endpointID, Remove)
181
+}
182
+
183
+// ModifyContainer corresponding to the container id, by sending a request
184
+func modifyContainer(id string, request *ResourceModificationRequestResponse) error {
185
+	container, err := OpenContainer(id)
186
+	if err != nil {
187
+		if IsNotExist(err) {
188
+			return ErrComputeSystemDoesNotExist
189
+		}
190
+		return getInnerError(err)
191
+	}
192
+	defer container.Close()
193
+	err = container.Modify(request)
194
+	if err != nil {
195
+		if IsNotSupported(err) {
196
+			return ErrPlatformNotSupported
197
+		}
198
+		return getInnerError(err)
199
+	}
200
+
201
+	return nil
202
+}
203
+
204
+func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error {
205
+	requestMessage := &ResourceModificationRequestResponse{
206
+		Resource: Network,
207
+		Request:  request,
208
+		Data:     endpointID,
209
+	}
210
+	err := modifyContainer(containerID, requestMessage)
211
+
212
+	if err != nil {
213
+		return err
214
+	}
215
+
216
+	return nil
217
+}
218
+
219
+// GetHNSNetworkByID
220
+func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) {
221
+	return HNSNetworkRequest("GET", networkID, "")
222
+}
223
+
224
+// GetHNSNetworkName filtered by Name
225
+func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) {
226
+	hsnnetworks, err := HNSListNetworkRequest("GET", "", "")
227
+	if err != nil {
228
+		return nil, err
229
+	}
230
+	for _, hnsnetwork := range hsnnetworks {
231
+		if hnsnetwork.Name == networkName {
232
+			return &hnsnetwork, nil
233
+		}
234
+	}
235
+	return nil, fmt.Errorf("Network %v not found", networkName)
236
+}
237
+
238
+// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods
239
+func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) {
240
+	jsonString, err := json.Marshal(endpoint)
241
+	if err != nil {
242
+		return nil, err
243
+	}
244
+	return HNSEndpointRequest("POST", "", string(jsonString))
245
+}
246
+
247
+// Create Endpoint by sending EndpointRequest to HNS
248
+func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) {
249
+	return HNSEndpointRequest("DELETE", endpoint.Id, "")
250
+}
251
+
252
+// GetHNSEndpointByID
253
+func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) {
254
+	return HNSEndpointRequest("GET", endpointID, "")
255
+}
256
+
257
+// GetHNSNetworkName filtered by Name
258
+func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
259
+	hnsResponse, err := HNSListEndpointRequest()
260
+	if err != nil {
261
+		return nil, err
262
+	}
263
+	for _, hnsEndpoint := range hnsResponse {
264
+		if hnsEndpoint.Name == endpointName {
265
+			return &hnsEndpoint, nil
266
+		}
267
+	}
268
+	return nil, fmt.Errorf("Endpoint %v not found", endpointName)
269
+}
... ...
@@ -5,7 +5,6 @@ import (
5 5
 	"io/ioutil"
6 6
 	"os"
7 7
 	"path/filepath"
8
-	"runtime"
9 8
 
10 9
 	"github.com/Microsoft/go-winio"
11 10
 	"github.com/Sirupsen/logrus"
... ...
@@ -209,6 +208,5 @@ func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string)
209 209
 	if err != nil {
210 210
 		return nil, makeError(err, "ImportLayerStart", "")
211 211
 	}
212
-	runtime.SetFinalizer(w, func(w *FilterLayerWriter) { w.Close() })
213 212
 	return w, nil
214 213
 }
... ...
@@ -117,6 +117,9 @@ type Container interface {
117 117
 
118 118
 	// Close cleans up any state associated with the container but does not terminate or wait for it.
119 119
 	Close() error
120
+
121
+	// Modify the System
122
+	Modify(config *ResourceModificationRequestResponse) error
120 123
 }
121 124
 
122 125
 // Process represents a running or exited process.
... ...
@@ -23,6 +23,13 @@ var mutatedUtilityVMFiles = map[string]bool{
23 23
 	`EFI\Microsoft\Boot\BCD.LOG2`: true,
24 24
 }
25 25
 
26
+const (
27
+	filesPath          = `Files`
28
+	hivesPath          = `Hives`
29
+	utilityVMPath      = `UtilityVM`
30
+	utilityVMFilesPath = `UtilityVM\Files`
31
+)
32
+
26 33
 func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) {
27 34
 	return winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createDisposition)
28 35
 }
... ...
@@ -44,6 +51,10 @@ func makeLongAbsPath(path string) (string, error) {
44 44
 	return `\\?\` + path, nil
45 45
 }
46 46
 
47
+func hasPathPrefix(p, prefix string) bool {
48
+	return strings.HasPrefix(p, prefix) && len(p) > len(prefix) && p[len(prefix)] == '\\'
49
+}
50
+
47 51
 type fileEntry struct {
48 52
 	path string
49 53
 	fi   os.FileInfo
... ...
@@ -83,7 +94,7 @@ func readTombstones(path string) (map[string]([]string), error) {
83 83
 
84 84
 	ts := make(map[string]([]string))
85 85
 	for s.Scan() {
86
-		t := filepath.Join("Files", s.Text()[1:]) // skip leading `\`
86
+		t := filepath.Join(filesPath, s.Text()[1:]) // skip leading `\`
87 87
 		dir := filepath.Dir(t)
88 88
 		ts[dir] = append(ts[dir], t)
89 89
 	}
... ...
@@ -212,7 +223,7 @@ func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.Fil
212 212
 		return
213 213
 	}
214 214
 
215
-	if fe.fi.IsDir() && strings.HasPrefix(path, `Files\`) {
215
+	if fe.fi.IsDir() && hasPathPrefix(path, filesPath) {
216 216
 		fe.path += ".$wcidirs$"
217 217
 	}
218 218
 
... ...
@@ -231,14 +242,14 @@ func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.Fil
231 231
 		return
232 232
 	}
233 233
 
234
-	if !strings.HasPrefix(path, `Files\`) {
234
+	if !hasPathPrefix(path, filesPath) {
235 235
 		size = fe.fi.Size()
236 236
 		r.backupReader = winio.NewBackupFileReader(f, false)
237
-		if path == "Hives" || path == "Files" {
237
+		if path == hivesPath || path == filesPath {
238 238
 			// The Hives directory has a non-deterministic file time because of the
239 239
 			// nature of the import process. Use the times from System_Delta.
240 240
 			var g *os.File
241
-			g, err = os.Open(filepath.Join(r.root, `Hives\System_Delta`))
241
+			g, err = os.Open(filepath.Join(r.root, hivesPath, `System_Delta`))
242 242
 			if err != nil {
243 243
 				return
244 244
 			}
... ...
@@ -357,7 +368,7 @@ func (w *legacyLayerWriter) init() error {
357 357
 
358 358
 func (w *legacyLayerWriter) initUtilityVM() error {
359 359
 	if !w.HasUtilityVM {
360
-		err := os.Mkdir(filepath.Join(w.destRoot, `UtilityVM`), 0)
360
+		err := os.Mkdir(filepath.Join(w.destRoot, utilityVMPath), 0)
361 361
 		if err != nil {
362 362
 			return err
363 363
 		}
... ...
@@ -365,7 +376,7 @@ func (w *legacyLayerWriter) initUtilityVM() error {
365 365
 		// clone the utility VM from the parent layer into this layer. Use hard
366 366
 		// links to avoid unnecessary copying, since most of the files are
367 367
 		// immutable.
368
-		err = cloneTree(filepath.Join(w.parentRoots[0], `UtilityVM\Files`), filepath.Join(w.destRoot, `UtilityVM\Files`), mutatedUtilityVMFiles)
368
+		err = cloneTree(filepath.Join(w.parentRoots[0], utilityVMFilesPath), filepath.Join(w.destRoot, utilityVMFilesPath), mutatedUtilityVMFiles)
369 369
 		if err != nil {
370 370
 			return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
371 371
 		}
... ...
@@ -490,15 +501,15 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
490 490
 		return err
491 491
 	}
492 492
 
493
-	if name == `UtilityVM` {
493
+	if name == utilityVMPath {
494 494
 		return w.initUtilityVM()
495 495
 	}
496 496
 
497
-	if strings.HasPrefix(name, `UtilityVM\`) {
497
+	if hasPathPrefix(name, utilityVMPath) {
498 498
 		if !w.HasUtilityVM {
499 499
 			return errors.New("missing UtilityVM directory")
500 500
 		}
501
-		if !strings.HasPrefix(name, `UtilityVM\Files\`) && name != `UtilityVM\Files` {
501
+		if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
502 502
 			return errors.New("invalid UtilityVM layer")
503 503
 		}
504 504
 		path := filepath.Join(w.destRoot, name)
... ...
@@ -585,7 +596,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
585 585
 		return err
586 586
 	}
587 587
 
588
-	if strings.HasPrefix(name, `Hives\`) {
588
+	if hasPathPrefix(name, hivesPath) {
589 589
 		w.backupWriter = winio.NewBackupFileWriter(f, false)
590 590
 	} else {
591 591
 		// The file attributes are written before the stream.
... ...
@@ -608,22 +619,19 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
608 608
 		return err
609 609
 	}
610 610
 
611
-	var requiredPrefix string
612 611
 	var roots []string
613
-	if prefix := `Files\`; strings.HasPrefix(name, prefix) {
614
-		requiredPrefix = prefix
612
+	if hasPathPrefix(target, filesPath) {
615 613
 		// Look for cross-layer hard link targets in the parent layers, since
616 614
 		// nothing is in the destination path yet.
617 615
 		roots = w.parentRoots
618
-	} else if prefix := `UtilityVM\Files\`; strings.HasPrefix(name, prefix) {
619
-		requiredPrefix = prefix
616
+	} else if hasPathPrefix(target, utilityVMFilesPath) {
620 617
 		// Since the utility VM is fully cloned into the destination path
621 618
 		// already, look for cross-layer hard link targets directly in the
622 619
 		// destination path.
623 620
 		roots = []string{w.destRoot}
624 621
 	}
625 622
 
626
-	if requiredPrefix == "" || !strings.HasPrefix(target, requiredPrefix) {
623
+	if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
627 624
 		return errors.New("invalid hard link in layer")
628 625
 	}
629 626
 
... ...
@@ -657,9 +665,9 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
657 657
 }
658 658
 
659 659
 func (w *legacyLayerWriter) Remove(name string) error {
660
-	if strings.HasPrefix(name, `Files\`) {
661
-		w.tombstones = append(w.tombstones, name[len(`Files\`):])
662
-	} else if strings.HasPrefix(name, `UtilityVM\Files\`) {
660
+	if hasPathPrefix(name, filesPath) {
661
+		w.tombstones = append(w.tombstones, name[len(filesPath)+1:])
662
+	} else if hasPathPrefix(name, utilityVMFilesPath) {
663 663
 		err := w.initUtilityVM()
664 664
 		if err != nil {
665 665
 			return err
... ...
@@ -3,7 +3,6 @@ package hcsshim
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"io"
6
-	"runtime"
7 6
 	"sync"
8 7
 	"syscall"
9 8
 	"time"
... ...
@@ -322,17 +321,11 @@ func (process *process) Close() error {
322 322
 	}
323 323
 
324 324
 	process.handle = 0
325
-	runtime.SetFinalizer(process, nil)
326 325
 
327 326
 	logrus.Debugf(title+" succeeded processid=%d", process.processID)
328 327
 	return nil
329 328
 }
330 329
 
331
-// closeProcess wraps process.Close for use by a finalizer
332
-func closeProcess(process *process) {
333
-	process.Close()
334
-}
335
-
336 330
 func (process *process) registerCallback() error {
337 331
 	context := &notifcationWatcherContext{
338 332
 		channels: newChannels(),