@@ -3,6 +3,7 @@ package mgr
33import (
44 "context"
55 "fmt"
6+ "io"
67 "io/ioutil"
78 "os"
89 "os/exec"
@@ -34,6 +35,7 @@ import (
3435 containerdtypes "github.com/containerd/containerd/api/types"
3536 "github.com/containerd/containerd/errdefs"
3637 "github.com/containerd/containerd/mount"
38+ "github.com/docker/docker/pkg/stdcopy"
3739 "github.com/docker/libnetwork"
3840 "github.com/go-openapi/strfmt"
3941 "github.com/imdario/mergo"
@@ -263,6 +265,9 @@ func (mgr *ContainerManager) Restore(ctx context.Context) error {
263265
264266// Create checks passed in parameters and create a Container object whose status is set at Created.
265267func (mgr * ContainerManager ) Create (ctx context.Context , name string , config * types.ContainerCreateConfig ) (resp * types.ContainerCreateResp , err error ) {
268+ var (
269+ id = ""
270+ )
266271 // cleanup allocated resources when failed
267272 cleanups := []func () error {}
268273 defer func () {
@@ -291,16 +296,43 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty
291296 return nil , errors .Wrapf (errtypes .ErrInvalidParam , "NetworkingConfig cannot be empty" )
292297 }
293298
294- id , err := mgr .generateID ()
295- if err != nil {
296- return nil , err
299+ if config .SpecificID != "" {
300+ if len (config .SpecificID ) < 12 || len (config .SpecificID ) > 64 {
301+ return nil , errors .Wrap (errtypes .ErrInvalidParam , "Container id length should be in [12,64]" )
302+ }
303+ //characters of containerID should be in "0123456789abcdef"
304+ for _ , c := range []byte (config .SpecificID ) {
305+ if ! ((c >= '0' && c <= '9' ) || (c >= 'a' && c <= 'f' )) {
306+ return nil , errors .Wrap (errtypes .ErrInvalidParam , "The characters of container id should be in '0123456789abcdef'" )
307+ }
308+ }
309+
310+ if mgr .cache .Get (config .SpecificID ).Exist () {
311+ return nil , errors .Wrap (errtypes .ErrAlreadyExisted , "container id: " + config .SpecificID )
312+ }
313+ id = config .SpecificID
314+ } else {
315+ tmpID , err := mgr .generateID ()
316+ if err != nil {
317+ return nil , err
318+ }
319+ id = tmpID
297320 }
321+ //put container id to cache to prevent concurrent containerCreateReq with same specific id
322+ mgr .cache .Put (id , nil )
323+ defer func () {
324+ //clear cache
325+ if err != nil {
326+ mgr .cache .Remove (id )
327+ }
328+ }()
298329
299330 if name == "" {
300331 name = mgr .generateName (id )
301332 } else if mgr .NameToID .Get (name ).Exist () {
302333 return nil , errors .Wrapf (errtypes .ErrAlreadyExisted , "container name %s" , name )
303334 }
335+ //todo: consider to do as the former ?
304336
305337 // set hostname.
306338 if config .Hostname .String () == "" {
@@ -1860,17 +1892,21 @@ func (mgr *ContainerManager) execExitedAndRelease(id string, m *ctrd.Message) er
18601892 execConfig .Running = false
18611893 execConfig .Error = m .RawError ()
18621894
1863- io := mgr .IOs .Get (id )
1864- if io == nil {
1895+ eio := mgr .IOs .Get (id )
1896+ if eio == nil {
18651897 return nil
18661898 }
18671899
18681900 if err := m .RawError (); err != nil {
1869- fmt .Fprintf (io .Stdout , "%v\n " , err )
1901+ var stdout io.Writer = eio .Stdout
1902+ if ! execConfig .Tty && ! eio .MuxDisabled {
1903+ stdout = stdcopy .NewStdWriter (stdout , stdcopy .Stdout )
1904+ }
1905+ stdout .Write ([]byte (err .Error () + "\r \n " ))
18701906 }
18711907
18721908 // close io
1873- io .Close ()
1909+ eio .Close ()
18741910 mgr .IOs .Remove (id )
18751911
18761912 return nil
0 commit comments