diff --git a/gnomock.go b/gnomock.go index bde34117..2ba75281 100644 --- a/gnomock.go +++ b/gnomock.go @@ -78,7 +78,7 @@ type g struct { // include tag, which is set to "latest" by default. Optional configuration is // available through Option functions. The returned container must be stopped // when no longer needed using its Stop() method. -func StartCustom(image string, ports NamedPorts, opts ...Option) (c *Container, err error) { +func StartCustom(image string, ports NamedPorts, opts ...Option) (*Container, error) { config, image := buildConfig(opts...), buildImage(image) g, err := newG(config.Debug) @@ -88,9 +88,24 @@ func StartCustom(image string, ports NamedPorts, opts ...Option) (c *Container, defer func() { _ = g.log.Sync() }() + if config.CustomNamedPorts != nil { + ports = config.CustomNamedPorts + } + g.log.Infow("starting", "image", image, "ports", ports) g.log.Infow("using config", "image", image, "ports", ports, "config", config) + c, err := newContainer(g, image, ports, config) + if err != nil { + return c, err + } + + g.log.Infow("container is ready to use", "id", c.ID, "ports", c.Ports) + + return c, nil +} + +func newContainer(g *g, image string, ports NamedPorts, config *Options) (c *Container, err error) { ctx, cancel := context.WithTimeout(config.ctx, config.Timeout) defer cancel() @@ -127,8 +142,6 @@ func StartCustom(image string, ports NamedPorts, opts ...Option) (c *Container, return c, fmt.Errorf("can't init container: %w", err) } - g.log.Infow("container is ready to use", "id", c.ID, "ports", c.Ports) - return c, nil } diff --git a/internal/gnomockd/gnomockd_test.go b/internal/gnomockd/gnomockd_test.go index 6be484e9..ca399de0 100644 --- a/internal/gnomockd/gnomockd_test.go +++ b/internal/gnomockd/gnomockd_test.go @@ -2,10 +2,13 @@ package gnomockd_test import ( "bytes" + "encoding/json" + "io" "net/http" "net/http/httptest" "testing" + "github.com/orlangure/gnomock" "github.com/orlangure/gnomock/internal/gnomockd" _ "github.com/orlangure/gnomock/preset/mongo" // this is only to prevent error 404 "github.com/stretchr/testify/require" @@ -73,7 +76,7 @@ func TestGnomockd(t *testing.T) { t.Parallel() h := gnomockd.Handler() - buf := bytes.NewBuffer([]byte(`{"id":"invalid"}`)) + buf := bytes.NewBufferString(`{"id":"invalid"}`) w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/stop", buf) h.ServeHTTP(w, r) @@ -83,4 +86,37 @@ func TestGnomockd(t *testing.T) { require.Equal(t, http.StatusInternalServerError, res.StatusCode) }) + + t.Run("fixed host port using custom named ports", func(t *testing.T) { + t.Parallel() + + port := gnomock.TCP(27017) + port.HostPort = 43210 + + body, err := json.Marshal(struct { + Options gnomock.Options `json:"options"` + }{ + gnomock.Options{ + CustomNamedPorts: gnomock.NamedPorts{ + gnomock.DefaultPort: port, + }, + }, + }) + require.NoError(t, err) + + h := gnomockd.Handler() + w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/start/mongo", bytes.NewBuffer(body)) + h.ServeHTTP(w, r) + + res := w.Result() + t.Cleanup(func() { require.NoError(t, res.Body.Close()) }) + require.Equal(t, http.StatusOK, res.StatusCode) + + body, err = io.ReadAll(res.Body) + require.NoError(t, err) + + c := gnomock.Container{} + require.NoError(t, json.Unmarshal(body, &c)) + require.Equal(t, 43210, c.DefaultPort()) + }) } diff --git a/options.go b/options.go index 3ec04395..a8c13a79 100644 --- a/options.go +++ b/options.go @@ -117,6 +117,10 @@ func WithOptions(options *Options) Option { o.Timeout = options.Timeout } + if options.CustomNamedPorts != nil { + o.CustomNamedPorts = options.CustomNamedPorts + } + o.Env = append(o.Env, options.Env...) o.Debug = options.Debug o.ContainerName = options.ContainerName @@ -160,6 +164,14 @@ func WithUseLocalImagesFirst() Option { } } +// WithCustomNamedPorts allows to define custom ports for a container. This +// option should be used to override the ports defined by presets. +func WithCustomNamedPorts(namedPorts NamedPorts) Option { + return func(o *Options) { + o.CustomNamedPorts = namedPorts + } +} + // WithRegistryAuth allows to access private docker images. The credentials // should be passes as a Base64 encoded string, where the content is a JSON // string with two fields: username and password. @@ -234,6 +246,18 @@ type Options struct { // instead of always pulling the images. UseLocalImagesFirst bool `json:"use_local_images_first"` + // CustomNamedPorts allows to override the ports set by the presets. This + // option is useful for cases when the presets need to be created with + // custom port definitions. This is an advanced feature and should be used + // with care. + // + // Note that when using this option, you should provide custom named ports + // with names matching the original ports returned by the used preset. + // + // When calling StartCustom directly from Go, it is possible to provide the + // ports directly to the function. + CustomNamedPorts NamedPorts `json:"custom_named_ports"` + // Base64 encoded JSON string with docker access credentials. JSON string // should include two fields: username and password. For Docker Hub, if 2FA // authentication is enabled, an access token should be used instead of a diff --git a/preset_test.go b/preset_test.go index 250967a1..5a5bdc6f 100644 --- a/preset_test.go +++ b/preset_test.go @@ -97,3 +97,23 @@ func TestPreset_duplicateContainerName(t *testing.T) { require.Error(t, gnomock.Stop(originalContainer)) require.NoError(t, gnomock.Stop(newContainer)) } + +func TestPreset_customNamedPorts(t *testing.T) { + t.Parallel() + + p := &testutil.TestPreset{Img: testutil.TestImage} + presetPorts := p.Ports() + pr := presetPorts["web80"] + pr.HostPort = 23080 + presetPorts["web80"] = pr + + container, err := gnomock.Start( + p, + gnomock.WithCustomNamedPorts(presetPorts), + gnomock.WithDebugMode(), + ) + + t.Cleanup(func() { require.NoError(t, gnomock.Stop(container)) }) + require.NoError(t, err) + require.Equal(t, 23080, container.Ports.Get("web80").Port) +} diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml index f367a2ad..bc3aabd6 100644 --- a/swagger/swagger.yaml +++ b/swagger/swagger.yaml @@ -478,6 +478,8 @@ components: use_local_images_first: type: boolean description: If possible to avoid hitting the Docker Hub pull rate limit. + custom_named_ports: + $ref: '#/components/schemas/named-ports' auth: type: string description: >