diff --git a/docs/docs/gen/api/post-node-container-docker.api.mdx b/docs/docs/gen/api/post-node-container-docker.api.mdx index a6f68a74b..faa7ae9c9 100644 --- a/docs/docs/gen/api/post-node-container-docker.api.mdx +++ b/docs/docs/gen/api/post-node-container-docker.api.mdx @@ -5,7 +5,7 @@ description: "Create a new container on the target node. Returns a job ID for tr sidebar_label: "Create a container" hide_title: true hide_table_of_contents: true -api: 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 +api: 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 sidebar_class_name: "post api-method" info_path: gen/api/agent-management-api custom_edit_url: null @@ -134,6 +134,24 @@ Create a new container on the target node. Returns a job ID for tracking the asy schema={{"type":"string","description":"Optional name for the container.","example":"my-nginx","x-oapi-codegen-extra-tags":{"validate":"omitempty,min=1"}}} > + + + + 0 { config.Cmd = params.Command } @@ -88,6 +92,10 @@ func (d *Client) Create( // Build host configuration hostConfig := &container.HostConfig{} + if len(params.DNS) > 0 { + hostConfig.DNS = params.DNS + } + // Convert port mappings if len(params.Ports) > 0 { portBindings := nat.PortMap{} diff --git a/internal/provider/container/docker/docker_public_test.go b/internal/provider/container/docker/docker_public_test.go index cac9c1283..1345ac929 100644 --- a/internal/provider/container/docker/docker_public_test.go +++ b/internal/provider/container/docker/docker_public_test.go @@ -432,6 +432,134 @@ func (s *DockerDriverPublicTestSuite) TestCreate() { s.Equal("vols-id", c.ID) }, }, + { + name: "with hostname", + setupMock: func( + ctrl *gomock.Controller, + ) *dockermocks.MockAPIClient { + m := dockermocks.NewMockAPIClient(ctrl) + m.EXPECT(). + ContainerCreate( + gomock.Any(), + gomock.AssignableToTypeOf(&container.Config{}), + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + ). + DoAndReturn(func( + _ context.Context, + config *container.Config, + _ *container.HostConfig, + _ *network.NetworkingConfig, + _ *ocispec.Platform, + _ string, + ) (container.CreateResponse, error) { + s.Equal("web-01", config.Hostname) + return container.CreateResponse{ID: "hostname-id"}, nil + }) + return m + }, + params: dockerprov.CreateParams{ + Image: "nginx:latest", + Name: "test-hostname", + Hostname: "web-01", + }, + validateFunc: func( + c *dockerprov.Container, + err error, + ) { + s.NoError(err) + s.NotNil(c) + s.Equal("hostname-id", c.ID) + }, + }, + { + name: "with dns", + setupMock: func( + ctrl *gomock.Controller, + ) *dockermocks.MockAPIClient { + m := dockermocks.NewMockAPIClient(ctrl) + m.EXPECT(). + ContainerCreate( + gomock.Any(), + gomock.Any(), + gomock.AssignableToTypeOf(&container.HostConfig{}), + gomock.Any(), + gomock.Any(), + gomock.Any(), + ). + DoAndReturn(func( + _ context.Context, + _ *container.Config, + hostConfig *container.HostConfig, + _ *network.NetworkingConfig, + _ *ocispec.Platform, + _ string, + ) (container.CreateResponse, error) { + s.Equal([]string{"8.8.8.8", "8.8.4.4"}, hostConfig.DNS) + return container.CreateResponse{ID: "dns-id"}, nil + }) + return m + }, + params: dockerprov.CreateParams{ + Image: "nginx:latest", + Name: "test-dns", + DNS: []string{"8.8.8.8", "8.8.4.4"}, + }, + validateFunc: func( + c *dockerprov.Container, + err error, + ) { + s.NoError(err) + s.NotNil(c) + s.Equal("dns-id", c.ID) + }, + }, + { + name: "with hostname and dns", + setupMock: func( + ctrl *gomock.Controller, + ) *dockermocks.MockAPIClient { + m := dockermocks.NewMockAPIClient(ctrl) + m.EXPECT(). + ContainerCreate( + gomock.Any(), + gomock.AssignableToTypeOf(&container.Config{}), + gomock.AssignableToTypeOf(&container.HostConfig{}), + gomock.Any(), + gomock.Any(), + gomock.Any(), + ). + DoAndReturn(func( + _ context.Context, + config *container.Config, + hostConfig *container.HostConfig, + _ *network.NetworkingConfig, + _ *ocispec.Platform, + _ string, + ) (container.CreateResponse, error) { + s.Equal("web-01", config.Hostname) + s.Equal([]string{"1.1.1.1"}, hostConfig.DNS) + return container.CreateResponse{ID: "both-id"}, nil + }) + return m + }, + params: dockerprov.CreateParams{ + Image: "nginx:latest", + Name: "test-both", + Hostname: "web-01", + DNS: []string{"1.1.1.1"}, + }, + validateFunc: func( + c *dockerprov.Container, + err error, + ) { + s.NoError(err) + s.NotNil(c) + s.Equal("both-id", c.ID) + }, + }, { name: "returns error when container create fails", setupMock: func( diff --git a/internal/provider/container/docker/types.go b/internal/provider/container/docker/types.go index ee3625775..fafcd6d6d 100644 --- a/internal/provider/container/docker/types.go +++ b/internal/provider/container/docker/types.go @@ -70,6 +70,10 @@ type CreateParams struct { Image string `json:"image"` // Name is an optional container name. Name string `json:"name,omitempty"` + // Hostname sets the container hostname. + Hostname string `json:"hostname,omitempty"` + // DNS sets custom DNS servers for the container. + DNS []string `json:"dns,omitempty"` // Command overrides the image's default command. Command []string `json:"command,omitempty"` // Env sets environment variables. diff --git a/pkg/sdk/client/docker.go b/pkg/sdk/client/docker.go index 56d7f2bac..97c6896bf 100644 --- a/pkg/sdk/client/docker.go +++ b/pkg/sdk/client/docker.go @@ -44,6 +44,12 @@ func (s *DockerService) Create( if opts.Name != "" { body.Name = &opts.Name } + if opts.Hostname != "" { + body.Hostname = &opts.Hostname + } + if len(opts.DNS) > 0 { + body.Dns = &opts.DNS + } if len(opts.Command) > 0 { body.Command = &opts.Command } diff --git a/pkg/sdk/client/docker_public_test.go b/pkg/sdk/client/docker_public_test.go index 165f332db..193a339eb 100644 --- a/pkg/sdk/client/docker_public_test.go +++ b/pkg/sdk/client/docker_public_test.go @@ -117,6 +117,35 @@ func (suite *DockerPublicTestSuite) TestCreate() { suite.Equal("my-app", resp.Data.Results[0].Name) }, }, + { + name: "when creating container with hostname and dns returns result", + handler: func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + _, _ = w.Write( + []byte( + `{"job_id":"00000000-0000-0000-0000-000000000003","results":[{"hostname":"web-01","id":"ghi789","name":"my-dns","image":"nginx:latest","state":"running","created":"2026-01-01T00:00:00Z","changed":true}]}`, + ), + ) + }, + opts: client.DockerCreateOpts{ + Image: "nginx:latest", + Name: "my-dns", + Hostname: "web-01", + DNS: []string{"8.8.8.8", "8.8.4.4"}, + }, + validateFunc: func( + resp *client.Response[client.Collection[client.DockerResult]], + err error, + ) { + suite.NoError(err) + suite.NotNil(resp) + suite.Equal("00000000-0000-0000-0000-000000000003", resp.Data.JobID) + suite.Len(resp.Data.Results, 1) + suite.Equal("ghi789", resp.Data.Results[0].ID) + suite.Equal("my-dns", resp.Data.Results[0].Name) + }, + }, { name: "when server returns 403 returns AuthError", handler: func(w http.ResponseWriter, _ *http.Request) { diff --git a/pkg/sdk/client/docker_types.go b/pkg/sdk/client/docker_types.go index 890239af1..758813403 100644 --- a/pkg/sdk/client/docker_types.go +++ b/pkg/sdk/client/docker_types.go @@ -30,6 +30,10 @@ type DockerCreateOpts struct { Image string // Name is an optional container name. Name string + // Hostname sets the container hostname. + Hostname string + // DNS sets custom DNS servers for the container. + DNS []string // Command overrides the image's default command. Command []string // Env is environment variables in KEY=VALUE format. diff --git a/pkg/sdk/client/gen/client.gen.go b/pkg/sdk/client/gen/client.gen.go index d4d5952d0..c0ebea1d3 100644 --- a/pkg/sdk/client/gen/client.gen.go +++ b/pkg/sdk/client/gen/client.gen.go @@ -1107,9 +1107,15 @@ type DockerCreateRequest struct { // Command Command to run in the container. Command *[]string `json:"command,omitempty"` + // Dns Custom DNS servers for the container. + Dns *[]string `json:"dns,omitempty" validate:"omitempty,dive,ip"` + // Env Environment variables in KEY=VALUE format. Env *[]string `json:"env,omitempty"` + // Hostname Container hostname. + Hostname *string `json:"hostname,omitempty" validate:"omitempty,min=1,max=253"` + // Image Container image reference (e.g., "nginx:latest"). Image string `json:"image" validate:"required,min=1"`