diff --git a/vendor.conf b/vendor.conf index e7d98d2285..358ad3ea3f 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,6 +1,6 @@ # the following lines are in sorted order, FYI github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62 -github.com/Microsoft/hcsshim v0.5.12 +github.com/Microsoft/hcsshim v0.5.13 # TODO: get rid of this fork once PR https://github.com/Microsoft/go-winio/pull/43 is merged github.com/Microsoft/go-winio 7c7d6b461cb10872c1138a0d7f3acf9a41b5c353 https://github.com/dgageot/go-winio.git github.com/Sirupsen/logrus v0.11.0 diff --git a/vendor/github.com/Microsoft/hcsshim/container.go b/vendor/github.com/Microsoft/hcsshim/container.go index ba3faeb206..efa858dc5e 100644 --- a/vendor/github.com/Microsoft/hcsshim/container.go +++ b/vendor/github.com/Microsoft/hcsshim/container.go @@ -2,6 +2,8 @@ package hcsshim import ( "encoding/json" + "fmt" + "os" "runtime" "sync" "syscall" @@ -103,8 +105,27 @@ type ProcessListItem struct { UserTime100ns uint64 `json:",omitempty"` } +// createContainerAdditionalJSON is read from the environment at initialisation +// time. It allows an environment variable to define additional JSON which +// is merged in the CreateContainer call to HCS. +var createContainerAdditionalJSON string + +func init() { + createContainerAdditionalJSON = 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) { + return createContainerWithJSON(id, c, "") +} + +// CreateContainerWithJSON creates a new container with the given configuration but does not start it. +// It is identical to CreateContainer except that optional additional JSON can be merged before passing to HCS. +func CreateContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) { + return createContainerWithJSON(id, c, additionalJSON) +} + +func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) { operation := "CreateContainer" title := "HCSShim::" + operation @@ -120,6 +141,32 @@ func CreateContainer(id string, c *ContainerConfig) (Container, error) { configuration := string(configurationb) logrus.Debugf(title+" id=%s config=%s", id, configuration) + // Merge any additional JSON. Priority is given to what is passed in explicitly, + // falling back to what's set in the environment. + if additionalJSON == "" && createContainerAdditionalJSON != "" { + additionalJSON = createContainerAdditionalJSON + } + if additionalJSON != "" { + configurationMap := map[string]interface{}{} + if err := json.Unmarshal([]byte(configuration), &configurationMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal %s: %s", configuration, err) + } + + additionalMap := map[string]interface{}{} + if err := json.Unmarshal([]byte(additionalJSON), &additionalMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal %s: %s", additionalJSON, err) + } + + mergedMap := mergeMaps(additionalMap, configurationMap) + mergedJSON, err := json.Marshal(mergedMap) + if err != nil { + return nil, fmt.Errorf("failed to marshal merged configuration map %+v: %s", mergedMap, err) + } + + configuration = string(mergedJSON) + logrus.Debugf(title+" id=%s merged config=%s", id, configuration) + } + var ( resultp *uint16 identity syscall.Handle @@ -142,6 +189,33 @@ func CreateContainer(id string, c *ContainerConfig) (Container, error) { return container, nil } +// mergeMaps recursively merges map `fromMap` into map `ToMap`. Any pre-existing values +// in ToMap are overwritten. Values in fromMap are added to ToMap. +// From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang +func mergeMaps(fromMap, ToMap interface{}) interface{} { + switch fromMap := fromMap.(type) { + case map[string]interface{}: + ToMap, ok := ToMap.(map[string]interface{}) + if !ok { + return fromMap + } + for keyToMap, valueToMap := range ToMap { + if valueFromMap, ok := fromMap[keyToMap]; ok { + fromMap[keyToMap] = mergeMaps(valueFromMap, valueToMap) + } else { + fromMap[keyToMap] = valueToMap + } + } + case nil: + // merge(nil, map[string]interface{...}) -> map[string]interface{...} + ToMap, ok := ToMap.(map[string]interface{}) + if ok { + return ToMap + } + } + return fromMap +} + // OpenContainer opens an existing container by ID. func OpenContainer(id string) (Container, error) { operation := "OpenContainer"