@@ -8,9 +8,11 @@ import (
88 "strconv"
99 "text/template"
1010
11+ "github.com/phayes/freeport"
1112 xfs "github.com/saitho/golang-extended-fs/v2"
1213
1314 container_docker_definitions "github.com/getstackhead/stackhead/modules/container/docker/definitions"
15+ "github.com/getstackhead/stackhead/project"
1416 "github.com/getstackhead/stackhead/system"
1517)
1618
@@ -56,6 +58,43 @@ func resolveUserNameWithCache(userName string) (int, error) {
5658 return resolvedUserId , err
5759}
5860
61+ func getPortMap (project * project.Project ) (map [string ]int , error ) {
62+ dockerPortMap := map [string ]int {}
63+
64+ // find ports for running containers
65+ for _ , service := range project .Container .Services {
66+ res , _ , err := system .RemoteRun ("docker" , "port" , "stackhead-" + project .Name + "-" + service .Name )
67+ if err == nil { // ignore error (container not running)
68+ // e.g. 80/tcp -> 0.0.0.0:49155
69+ re := regexp .MustCompile (`(?P<Internal>\d+)\/tcp -> 0\.0\.0\.0:(?P<External>\d+)` )
70+ matches := re .FindAllStringSubmatch (res .String (), - 1 )
71+ for _ , match := range matches {
72+ externalPort , _ := strconv .Atoi (match [re .SubexpIndex ("External" )])
73+ dockerPortMap [service .Name + "-" + match [re .SubexpIndex ("Internal" )]] = externalPort
74+ }
75+ }
76+ }
77+
78+ // determine ports for missing containers
79+ missingPortServices := []string {}
80+ for _ , domain := range project .Domains {
81+ for _ , expose := range domain .Expose {
82+ mapKey := expose .Service + "-" + strconv .Itoa (expose .InternalPort )
83+ if _ , ok := dockerPortMap [mapKey ]; ! ok {
84+ missingPortServices = append (missingPortServices , mapKey )
85+ }
86+ }
87+ }
88+ ports , err := freeport .GetFreePorts (len (missingPortServices ))
89+ if err != nil {
90+ return nil , fmt .Errorf ("unable to determine free ports: " + err .Error ())
91+ }
92+ for i := range missingPortServices {
93+ dockerPortMap [missingPortServices [i ]] = ports [i ]
94+ }
95+ return dockerPortMap , nil
96+ }
97+
5998func (m Module ) Deploy (modulesSettings interface {}) error {
6099 project := system .Context .Project
61100
@@ -146,9 +185,15 @@ func (m Module) Deploy(modulesSettings interface{}) error {
146185 },
147186 }
148187
188+ dockerPortMap , err := getPortMap (project )
189+ if err != nil {
190+ return fmt .Errorf ("unable to determine free ports: " + err .Error ())
191+ }
192+
149193 data := map [string ]interface {}{
150- "Context" : system .Context ,
151- "DockerPaths" : GetDockerPaths (),
194+ "Context" : system .Context ,
195+ "DockerPaths" : GetDockerPaths (),
196+ "DockerPortMap" : dockerPortMap ,
152197 }
153198 dockerTf , err := system .RenderModuleTemplate (
154199 templates ,
@@ -158,5 +203,7 @@ func (m Module) Deploy(modulesSettings interface{}) error {
158203 if err != nil {
159204 return err
160205 }
206+
207+ _ = xfs .WriteFile ("ssh://" + project .GetTerraformDirectoryPath ()+ "/docker.tf" , dockerTf )
161208 return xfs .WriteFile ("ssh://" + project .GetTerraformDirectoryPath ()+ "/docker.tf" , dockerTf )
162209}
0 commit comments