Skip to content

Commit

Permalink
#1152 execute HostConfigModifier at last (#1153)
Browse files Browse the repository at this point in the history
* #1152 execute HostConfigModifier at last

#1152  execute HostConfigModifier at last, or some values will be overwrite

* add UT: Request contains exposed port modifiers

* merge nat.ParsePortSpecs and hostConfig.PortBindings

* update UT

* clean code

* add case to UT

* update PortBinding logic
  • Loading branch information
xmh19936688 authored May 24, 2023
1 parent 2b975fa commit 0d47b42
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 6 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/moby/term v0.0.0-20221128092401-c43b287e0e0f
github.com/opencontainers/image-spec v1.1.0-rc2
github.com/stretchr/testify v1.8.2
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea
golang.org/x/sys v0.7.0
gotest.tools/gotestsum v1.10.0
)
Expand Down Expand Up @@ -45,12 +46,12 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.8.1 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/tools v0.2.0 // indirect
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad // indirect
google.golang.org/grpc v1.47.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
Expand Down
9 changes: 6 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4=
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -263,8 +266,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
23 changes: 22 additions & 1 deletion lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package testcontainers

import (
"context"
"strings"

"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
"golang.org/x/exp/slices"
)

// ContainerRequestHook is a hook that will be called before a container is created.
Expand Down Expand Up @@ -318,11 +320,30 @@ func (p *DockerProvider) preCreateContainerHook(ctx context.Context, req Contain
}

dockerInput.ExposedPorts = exposedPortSet
hostConfig.PortBindings = exposedPortMap

// only exposing those ports automatically if the container request exposes zero ports and the container does not run in a container network
if len(exposedPorts) == 0 && !hostConfig.NetworkMode.IsContainer() {
hostConfig.PortBindings = exposedPortMap
} else {
hostConfig.PortBindings = mergePortBindings(hostConfig.PortBindings, exposedPortMap, req.ExposedPorts)
}

return nil
}

func mergePortBindings(configPortMap, exposedPortMap nat.PortMap, exposedPorts []string) nat.PortMap {
if exposedPortMap == nil {
exposedPortMap = make(map[nat.Port][]nat.PortBinding)
}

for k, v := range configPortMap {
if slices.Contains(exposedPorts, strings.Split(string(k), "/")[0]) {
exposedPortMap[k] = v
}
}
return exposedPortMap
}

// defaultHostConfigModifier provides a default modifier including the deprecated fields
func defaultHostConfigModifier(req ContainerRequest) func(hostConfig *container.HostConfig) {
return func(hostConfig *container.HostConfig) {
Expand Down
120 changes: 120 additions & 0 deletions lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,126 @@ func TestPreCreateModifierHook(t *testing.T) {
"Networking config's network ID should be retrieved from Docker",
)
})

t.Run("Request contains exposed port modifiers", func(t *testing.T) {
req := ContainerRequest{
Image: nginxAlpineImage, // alpine image does expose port 80
HostConfigModifier: func(hostConfig *container.HostConfig) {
hostConfig.PortBindings = nat.PortMap{
"80/tcp": []nat.PortBinding{
{
HostIP: "localhost",
HostPort: "8080",
},
},
}
},
ExposedPorts: []string{"80"},
}

// define empty inputs to be overwritten by the pre create hook
inputConfig := &container.Config{
Image: req.Image,
}
inputHostConfig := &container.HostConfig{}
inputNetworkingConfig := &network.NetworkingConfig{}

err = provider.preCreateContainerHook(ctx, req, inputConfig, inputHostConfig, inputNetworkingConfig)
require.Nil(t, err)

// assertions
assert.Equal(t, inputHostConfig.PortBindings["80/tcp"][0].HostIP, "localhost")
assert.Equal(t, inputHostConfig.PortBindings["80/tcp"][0].HostPort, "8080")
})
}

func TestMergePortBindings(t *testing.T) {
type arg struct {
configPortMap nat.PortMap
parsedPortMap nat.PortMap
exposedPorts []string
}
cases := []struct {
name string
arg arg
expected nat.PortMap
}{
{
name: "empty ports",
arg: arg{
configPortMap: nil,
parsedPortMap: nil,
exposedPorts: nil,
},
expected: map[nat.Port][]nat.PortBinding{},
},
{
name: "config port map but not exposed",
arg: arg{
configPortMap: map[nat.Port][]nat.PortBinding{
"80/tcp": {{HostIP: "1", HostPort: "2"}},
},
parsedPortMap: nil,
exposedPorts: nil,
},
expected: map[nat.Port][]nat.PortBinding{},
},
{
name: "parsed port map without config",
arg: arg{
configPortMap: nil,
parsedPortMap: map[nat.Port][]nat.PortBinding{
"80/tcp": {{HostIP: "", HostPort: ""}},
},
exposedPorts: nil,
},
expected: map[nat.Port][]nat.PortBinding{
"80/tcp": {{HostIP: "", HostPort: ""}},
},
},
{
name: "parsed and configured but not exposed",
arg: arg{
configPortMap: map[nat.Port][]nat.PortBinding{
"80/tcp": {{HostIP: "1", HostPort: "2"}},
},
parsedPortMap: map[nat.Port][]nat.PortBinding{
"80/tcp": {{HostIP: "", HostPort: ""}},
},
exposedPorts: nil,
},
expected: map[nat.Port][]nat.PortBinding{
"80/tcp": {{HostIP: "", HostPort: ""}},
},
},
{
name: "merge both parsed and config",
arg: arg{
configPortMap: map[nat.Port][]nat.PortBinding{
"60/tcp": {{HostIP: "1", HostPort: "2"}},
"70/tcp": {{HostIP: "1", HostPort: "2"}},
"80/tcp": {{HostIP: "1", HostPort: "2"}},
},
parsedPortMap: map[nat.Port][]nat.PortBinding{
"80/tcp": {{HostIP: "", HostPort: ""}},
"90/tcp": {{HostIP: "", HostPort: ""}},
},
exposedPorts: []string{"70", "80"},
},
expected: map[nat.Port][]nat.PortBinding{
"70/tcp": {{HostIP: "1", HostPort: "2"}},
"80/tcp": {{HostIP: "1", HostPort: "2"}},
"90/tcp": {{HostIP: "", HostPort: ""}},
},
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
res := mergePortBindings(c.arg.configPortMap, c.arg.parsedPortMap, c.arg.exposedPorts)
assert.Equal(t, c.expected, res)
})
}
}

func TestLifecycleHooks(t *testing.T) {
Expand Down

0 comments on commit 0d47b42

Please sign in to comment.