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 Oct 31, 2018
1 parent 7006be4 commit 25c25fb
Show file tree
Hide file tree
Showing 8 changed files with 166 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 @@ -2233,6 +2233,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 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
7 changes: 7 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.

4 changes: 2 additions & 2 deletions apis/types/registry_service_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 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
31 changes: 30 additions & 1 deletion daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,18 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty
return nil, errors.Wrapf(errtypes.ErrInvalidParam, "NetworkingConfig cannot be empty")
}

id, err := mgr.generateID()
id, err := mgr.generateContainerID(config.SpecificID)
if err != nil {
return nil, err
}
//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)
Expand Down Expand Up @@ -1874,3 +1882,24 @@ func (mgr *ContainerManager) execProcessGC() {
func (mgr *ContainerManager) NewSnapshotsSyncer(snapshotStore *SnapshotStore, duration time.Duration) *SnapshotsSyncer {
return newSnapshotsSyncer(snapshotStore, mgr.Client, duration)
}

func (mgr *ContainerManager) generateContainerID(specificID string) (string, error) {
if specificID != "" {
if len(specificID) != 64 {
return "", errors.Wrap(errtypes.ErrInvalidParam, "Container id length should be 64")
}
//characters of containerID should be in "0123456789abcdef"
for _, c := range []byte(specificID) {
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
return "", errors.Wrap(errtypes.ErrInvalidParam, "The characters of container id should be in '0123456789abcdef'")
}
}

if mgr.cache.Get(specificID).Exist() {
return "", errors.Wrap(errtypes.ErrAlreadyExisted, "container id: "+specificID)
}
return specificID, nil
}

return mgr.generateID()
}
112 changes: 112 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"
"strings"
Expand Down Expand Up @@ -279,3 +280,114 @@ func (suite *APIContainerCreateSuite) TestCreateWithMacAddress(c *check.C) {

DelContainerForceMultyTime(c, cname)
}

func (suite *APIContainerCreateSuite) TestBasicWithSpecificID(c *check.C) {
{
specificID := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
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 := "0123456789fedcbaab0123456789abcdef0123456789abcdef0123456789abcd"
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 64
{
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"] = "0123456789abcdefhi0123456789abcdef0123456789abcdef0123456789abcd"
body := request.WithJSONBody(obj)
resp, err := request.Post("/containers/create", body)
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, http.StatusBadRequest)
}

//case4: specificID is conflict with existing one
{
specificID := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
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)

defer DelContainerForceMultyTime(c, got.ID)

//create container with existing id
resp, err = request.Post("/containers/create", body)
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, http.StatusConflict)
}
}

0 comments on commit 25c25fb

Please sign in to comment.