diff --git a/aci/convert/convert.go b/aci/convert/convert.go index 34d60df5a..532df6787 100644 --- a/aci/convert/convert.go +++ b/aci/convert/convert.go @@ -42,7 +42,7 @@ const ( // ComposeDNSSidecarName name of the dns sidecar container ComposeDNSSidecarName = "aci--dns--sidecar" - dnsSidecarImage = "busybox:1.31.1" + dnsSidecarImage = "docker/aci-hostnames-sidecar" ) // ToContainerGroup converts a compose project into a ACI container group @@ -129,19 +129,15 @@ func ToContainerGroup(ctx context.Context, aciContext store.AciContext, p types. } func getDNSSidecar(containers []containerinstance.Container) containerinstance.Container { - var commands []string + names := []string{"/hosts"} for _, container := range containers { - commands = append(commands, fmt.Sprintf("echo 127.0.0.1 %s >> /etc/hosts", *container.Name)) + names = append(names, *container.Name) } - // ACI restart policy is currently at container group level, cannot let the sidecar terminate quietly once /etc/hosts has been edited - // Pricing is done at the container group level so letting the sidecar container "sleep" should not impact the price for the whole group - commands = append(commands, "sleep infinity") - alpineCmd := []string{"sh", "-c", strings.Join(commands, ";")} dnsSideCar := containerinstance.Container{ Name: to.StringPtr(ComposeDNSSidecarName), ContainerProperties: &containerinstance.ContainerProperties{ Image: to.StringPtr(dnsSidecarImage), - Command: &alpineCmd, + Command: &names, Resources: &containerinstance.ResourceRequirements{ Requests: &containerinstance.ResourceRequests{ MemoryInGB: to.Float64Ptr(0.1), diff --git a/aci/convert/convert_test.go b/aci/convert/convert_test.go index f14bbe0e8..9917edbf9 100644 --- a/aci/convert/convert_test.go +++ b/aci/convert/convert_test.go @@ -179,7 +179,7 @@ func TestComposeContainerGroupToContainerWithDnsSideCarSide(t *testing.T) { assert.Equal(t, *(*group.Containers)[1].Name, "service2") assert.Equal(t, *(*group.Containers)[2].Name, ComposeDNSSidecarName) - assert.DeepEqual(t, *(*group.Containers)[2].Command, []string{"sh", "-c", "echo 127.0.0.1 service1 >> /etc/hosts;echo 127.0.0.1 service2 >> /etc/hosts;sleep infinity"}) + assert.DeepEqual(t, *(*group.Containers)[2].Command, []string{"/hosts", "service1", "service2"}) assert.Equal(t, *(*group.Containers)[0].Image, "image1") assert.Equal(t, *(*group.Containers)[1].Image, "image2") diff --git a/aci/etchosts/Dockerfile b/aci/etchosts/Dockerfile new file mode 100644 index 000000000..b7e0f5497 --- /dev/null +++ b/aci/etchosts/Dockerfile @@ -0,0 +1,21 @@ +# Copyright 2020 Docker Compose CLI authors + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM golang:1.15 AS builder +WORKDIR $GOPATH/src/github.com/docker/compose-cli/aci/etchosts +COPY . . +RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o /go/bin/hosts main/main.go + +FROM scratch +COPY --from=builder /go/bin/hosts /hosts \ No newline at end of file diff --git a/aci/etchosts/hosts.go b/aci/etchosts/hosts.go new file mode 100644 index 000000000..468197ed3 --- /dev/null +++ b/aci/etchosts/hosts.go @@ -0,0 +1,38 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package etchosts + +import ( + "fmt" + "os" + "strings" +) + +// SetHostNames appends hosts aliases for loopback address to etc/host file +func SetHostNames(file string, hosts ...string) error { + f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() //nolint:errcheck + + fmt.Println("Setting local hosts for " + strings.Join(hosts, ", ")) + for _, host := range hosts { + _, err = f.WriteString("\n127.0.0.1 " + host) + } + return err +} diff --git a/aci/etchosts/hosts_test.go b/aci/etchosts/hosts_test.go new file mode 100644 index 000000000..d3da7edcd --- /dev/null +++ b/aci/etchosts/hosts_test.go @@ -0,0 +1,48 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package etchosts + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" + "gotest.tools/v3/golden" +) + +func TestSetDomain(t *testing.T) { + dir := fs.NewDir(t, "resolv").Path() + f := filepath.Join(dir, "hosts") + touch(t, f) + + err := SetHostNames(f, "foo", "bar", "zot") + assert.NilError(t, err) + + got, err := ioutil.ReadFile(f) + assert.NilError(t, err) + golden.Assert(t, string(got), "etchosts.golden") +} + +func touch(t *testing.T, f string) { + file, err := os.Create(f) + assert.NilError(t, err) + err = file.Close() + assert.NilError(t, err) +} diff --git a/aci/etchosts/main/main.go b/aci/etchosts/main/main.go new file mode 100644 index 000000000..925bb166d --- /dev/null +++ b/aci/etchosts/main/main.go @@ -0,0 +1,47 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/docker/compose-cli/aci/etchosts" +) + +const hosts = "/etc/hosts" + +func main() { + if len(os.Args) < 2 { + fmt.Fprint(os.Stderr, "usage: hosts HOSTNAME [HOSTNAME]") + os.Exit(1) + } + + err := etchosts.SetHostNames(hosts, os.Args[1:]...) + if err != nil { + fmt.Fprint(os.Stderr, err.Error()) + os.Exit(1) + } + + // ACI restart policy is currently at container group level, cannot let the sidecar terminate quietly once /etc/hosts has been edited + // Pause forever (until someone explicitly terminates this process ; go is not happy to stop all goroutines otherwise) + exitSignal := make(chan os.Signal, 1) + signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM) + <-exitSignal +} diff --git a/aci/etchosts/testdata/etchosts.golden b/aci/etchosts/testdata/etchosts.golden new file mode 100644 index 000000000..5416fde63 --- /dev/null +++ b/aci/etchosts/testdata/etchosts.golden @@ -0,0 +1,4 @@ + +127.0.0.1 foo +127.0.0.1 bar +127.0.0.1 zot \ No newline at end of file diff --git a/ecs/resolv/Dockerfile b/ecs/resolv/Dockerfile index 679c4a909..2f6a2c3dd 100644 --- a/ecs/resolv/Dockerfile +++ b/ecs/resolv/Dockerfile @@ -12,11 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.14.4-alpine AS builder +FROM FROM golang:1.15 AS builder WORKDIR $GOPATH/src/github.com/docker/compose-cli/ecs/resolv COPY . . -RUN GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/resolv main/main.go -RUN chmod +x /go/bin/resolv +RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o /go/bin/resolv main/main.go FROM scratch COPY --from=builder /go/bin/resolv /resolv diff --git a/ecs/secrets/Dockerfile b/ecs/secrets/Dockerfile index c41e0d071..85fdadd9e 100644 --- a/ecs/secrets/Dockerfile +++ b/ecs/secrets/Dockerfile @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.14.4-alpine AS builder +FROM FROM golang:1.15 AS builder WORKDIR $GOPATH/src/github.com/docker/compose-cli/ecs/secrets COPY . . -RUN GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/secrets main/main.go +RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o /go/bin/secrets main/main.go FROM scratch COPY --from=builder /go/bin/secrets /secrets