diff --git a/container/provider.go b/container/provider.go index 9434285..bf865fb 100644 --- a/container/provider.go +++ b/container/provider.go @@ -18,14 +18,12 @@ package container import ( "context" - - "docker.io/go-docker/api/types" - "errors" - "net" + "strconv" "time" + "docker.io/go-docker/api/types" "github.com/it-chain/iLogger" "github.com/it-chain/tesseract" "github.com/it-chain/tesseract/docker" @@ -33,6 +31,7 @@ import ( ) var ErrFailedPullImage = errors.New("failed to pull image") +var defaultPort = "50001" func Create(config tesseract.ContainerConfig) (DockerContainer, error) { @@ -50,6 +49,14 @@ func Create(config tesseract.ContainerConfig) (DockerContainer, error) { return DockerContainer{}, ErrFailedPullImage } + if config.Network == nil { + var port string + if port, err = getAvailablePort(); err != nil { + return DockerContainer{}, err + } + config.Port = port + } + // Create Docker res, err := docker.CreateContainer( config, @@ -145,3 +152,35 @@ func retryConnectWithTimeOut(ipAddress string, port string, timeout time.Duratio return client, nil } } + +func getAvailablePort() (string, error) { + portList, err := docker.GetPorts() + if err != nil { + return "", err + } + +findLoop: + for { + portNumber, err := strconv.Atoi(defaultPort) + if err != nil { + return "", err + } + for _, portInfo := range portList { + if portNumber == int(portInfo.PublicPort) || portNumber == int(portInfo.PrivatePort) { + portNumber++ + defaultPort = strconv.Itoa(portNumber) + continue findLoop + } + } + + lis, err := net.Listen("tcp", "127.0.0.1:"+defaultPort) + + if err == nil { + lis.Close() + return defaultPort, nil + } + + portNumber++ + defaultPort = strconv.Itoa(portNumber) + } +} diff --git a/docker/docker.go b/docker/docker.go index 2fe1d70..13c516a 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -365,6 +365,33 @@ func FindVolumeByName(name string) (tesseract.Volume, error) { return tesseract.Volume{}, nil } +func GetPorts() ([]types.Port, error) { + + ctx := context.Background() + cli, _ := docker.NewEnvClient() + defer cli.Close() + + portList := make([]types.Port, 0) + containerList, err := cli.ContainerList(ctx, types.ContainerListOptions{All: true}) + + if err != nil { + return portList, err + } + + for _, container := range containerList { + for _, port := range container.Ports { + portInfo := types.Port{ + IP: port.IP, + PrivatePort: port.PrivatePort, + PublicPort: port.PublicPort, + } + portList = append(portList, portInfo) + } + } + + return portList, nil +} + func isVolumeEmpty(vol tesseract.Volume) bool { return reflect.DeepEqual(vol, tesseract.Volume{}) }