Skip to content

Commit

Permalink
support create a container with a given id
Browse files Browse the repository at this point in the history
Signed-off-by: allen.wang <[email protected]>
  • Loading branch information
wangforthinker committed Sep 11, 2018
1 parent ff5d11f commit 6822c14
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 3 deletions.
5 changes: 5 additions & 0 deletions apis/server/container_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ func (s *Server) createContainer(ctx context.Context, rw http.ResponseWriter, re
logCreateOptions("container", config)

name := req.FormValue("name")
//consider set specific id by url params
specificID := req.FormValue("recover-id")
if specificID != "" {
config.SpecificID = specificID
}

// to do compensation to potential nil pointer after validation
if config.HostConfig == nil {
Expand Down
7 changes: 7 additions & 0 deletions apis/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2209,6 +2209,13 @@ definitions:
description: "net priority."
type: "integer"
default: 0
SpecificID:
type: "string"
description: |
Create container with given id.
The length of given id should be in [12,64].
The characters of given id should be in 0123456789abcdef.
By default, given id is unnecessary.
ContainerCreateResp:
description: "response returned by daemon when container create successfully"
Expand Down
9 changes: 9 additions & 0 deletions apis/types/container_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/common_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func addCommonFlags(flagSet *pflag.FlagSet) *container {
flagSet.Int64Var(&c.oomScoreAdj, "oom-score-adj", -500, "Tune host's OOM preferences (-1000 to 1000)")

flagSet.StringVar(&c.name, "name", "", "Specify name of container")
flagSet.StringVar(&c.specificID, "specific-id", "", "Specify id of container, length of id should be in [12,64], characters of id should be in '0123456789abcdef'")

flagSet.StringSliceVar(&c.networks, "net", nil, "Set networks to container")
flagSet.StringSliceVarP(&c.ports, "port", "p", nil, "Set container ports mapping")
Expand Down
2 changes: 2 additions & 0 deletions cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type container struct {
hostname string
rm bool
disableNetworkFiles bool
specificID string

blkioWeight uint16
blkioWeightDevice config.WeightDevice
Expand Down Expand Up @@ -194,6 +195,7 @@ func (c *container) config() (*types.ContainerCreateConfig, error) {
QuotaID: c.quotaID,
SpecAnnotation: specAnnotation,
NetPriority: c.netPriority,
SpecificID: c.specificID,
},

HostConfig: &types.HostConfig{
Expand Down
36 changes: 33 additions & 3 deletions daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ func (mgr *ContainerManager) Restore(ctx context.Context) error {

// Create checks passed in parameters and create a Container object whose status is set at Created.
func (mgr *ContainerManager) Create(ctx context.Context, name string, config *types.ContainerCreateConfig) (resp *types.ContainerCreateResp, err error) {
var (
id = ""
)
// cleanup allocated resources when failed
cleanups := []func() error{}
defer func() {
Expand Down Expand Up @@ -291,16 +294,43 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty
return nil, fmt.Errorf("NetworkingConfig cannot be nil")
}

id, err := mgr.generateID()
if err != nil {
return nil, err
if config.SpecificID != "" {
if len(config.SpecificID) < 12 || len(config.SpecificID) > 64 {
return nil, errors.Wrap(errtypes.ErrInvalidParam, "Container id length should be in [12,64]")
}
//characters of containerID should be in "0123456789abcdef"
for _, c := range []byte(config.SpecificID) {
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
return nil, errors.Wrap(errtypes.ErrInvalidParam, "The characters of container id should be in '0123456789abcdef'")
}
}

if mgr.cache.Get(config.SpecificID).Exist() {
return nil, errors.Wrap(errtypes.ErrAlreadyExisted, "container id: "+config.SpecificID)
}
id = config.SpecificID
} else {
tmpId, err := mgr.generateID()
if err != nil {
return nil, err
}
id = tmpId
}
//put container id to cache to prevent concurrent containerCreateReq with same specific id
mgr.cache.Put(id, nil)
defer func() {
//clear cache
if err != nil {
mgr.cache.Remove(id)
}
}()

if name == "" {
name = mgr.generateName(id)
} else if mgr.NameToID.Get(name).Exist() {
return nil, errors.Wrap(errtypes.ErrAlreadyExisted, "container name: "+name)
}
//todo: consider to do as the former ?

// set hostname.
if config.Hostname.String() == "" {
Expand Down
84 changes: 84 additions & 0 deletions test/api_container_create_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"net/http"
"net/url"
"sort"

Expand Down Expand Up @@ -231,3 +232,86 @@ func (suite *APIContainerCreateSuite) TestCreateNvidiaConfig(c *check.C) {

DelContainerForceMultyTime(c, cname)
}

func (suite *APIContainerCreateSuite) TestBasicWithSpecificID(c *check.C) {
{
specificID := "0123456789abcdef"
obj := map[string]interface{}{
"Image": busyboxImage,
"HostConfig": map[string]interface{}{},
"SpecificID": specificID,
}

body := request.WithJSONBody(obj)
resp, err := request.Post("/containers/create", body)
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, 201)

// Decode response
got := types.ContainerCreateResp{}
c.Assert(request.DecodeBody(&got, resp.Body), check.IsNil)
c.Assert(got.ID, check.Equals, specificID)
c.Assert(got.Name, check.NotNil)

DelContainerForceMultyTime(c, got.ID)
}

//transfer specific id by url params
{
specificID := "0123456789fedcba"
q := url.Values{}
q.Add("recover-id", specificID)
query := request.WithQuery(q)
obj := map[string]interface{}{
"Image": busyboxImage,
"HostConfig": map[string]interface{}{},
}

body := request.WithJSONBody(obj)
resp, err := request.Post("/containers/create", query, body)
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, 201)

// Decode response
got := types.ContainerCreateResp{}
c.Assert(request.DecodeBody(&got, resp.Body), check.IsNil)
c.Assert(got.ID, check.Equals, specificID)
c.Assert(got.Name, check.NotNil)

DelContainerForceMultyTime(c, got.ID)
}
}

func (suite *APIContainerCreateSuite) TestInvalidSpecificID(c *check.C) {
obj := map[string]interface{}{
"Image": busyboxImage,
"HostConfig": map[string]interface{}{},
}

//case1: specificID len is less than 12
{
obj["SpecificID"] = "123456789ab"
body := request.WithJSONBody(obj)
resp, err := request.Post("/containers/create", body)
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, http.StatusBadRequest)
}

//case2: specificID len is more than 64
{
obj["SpecificID"] = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0"
body := request.WithJSONBody(obj)
resp, err := request.Post("/containers/create", body)
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, http.StatusBadRequest)
}

//case3: characters of specificID is not in "0123456789abcdef"
{
obj["SpecificID"] = "0123456789abcdefhi"
body := request.WithJSONBody(obj)
resp, err := request.Post("/containers/create", body)
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, http.StatusBadRequest)
}
}

0 comments on commit 6822c14

Please sign in to comment.