From 553865f294a8c1f91e0c4110c96ae6a07e032c70 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 16 Oct 2020 14:29:36 +0200 Subject: [PATCH 1/4] minimalist container image to setup /etc/hosts on ACI Signed-off-by: Nicolas De Loof --- aci/convert/convert.go | 12 +++---- aci/convert/convert_test.go | 2 +- aci/etchosts/Dockerfile | 22 ++++++++++++ aci/etchosts/hosts.go | 35 +++++++++++++++++++ aci/etchosts/hosts_test.go | 48 +++++++++++++++++++++++++++ aci/etchosts/main/main.go | 43 ++++++++++++++++++++++++ aci/etchosts/testdata/etchosts.golden | 4 +++ ecs/resolv/Dockerfile | 5 ++- ecs/secrets/Dockerfile | 4 +-- 9 files changed, 161 insertions(+), 14 deletions(-) create mode 100644 aci/etchosts/Dockerfile create mode 100644 aci/etchosts/hosts.go create mode 100644 aci/etchosts/hosts_test.go create mode 100644 aci/etchosts/main/main.go create mode 100644 aci/etchosts/testdata/etchosts.golden diff --git a/aci/convert/convert.go b/aci/convert/convert.go index 34d60df5a..31c71955f 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 + var names []string 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..fcacfd6b2 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{"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..da243fc30 --- /dev/null +++ b/aci/etchosts/Dockerfile @@ -0,0 +1,22 @@ +# 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 +ENTRYPOINT ["/hosts"] diff --git a/aci/etchosts/hosts.go b/aci/etchosts/hosts.go new file mode 100644 index 000000000..7caa23d02 --- /dev/null +++ b/aci/etchosts/hosts.go @@ -0,0 +1,35 @@ +/* + 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 ( + "os" +) + +// 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 + + 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..cb2b8315c --- /dev/null +++ b/aci/etchosts/main/main.go @@ -0,0 +1,43 @@ +/* + 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" + + "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 + // Pricing is done at the container group level so letting the sidecar container "sleep" should not impact the price for the whole group + fmt.Scanln() // pause +} 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 From 3dddbcd7df4d80872152795a9ab088a4fa2b3545 Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Mon, 26 Oct 2020 15:03:37 +0100 Subject: [PATCH 2/4] We need to pass the full command and not rely on ENTRYPOINT for ACI containers, ACI overrides the ENTRYPOINT with commands. Signed-off-by: Guillaume Tardif --- aci/convert/convert.go | 2 +- aci/convert/convert_test.go | 2 +- aci/etchosts/Dockerfile | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/aci/convert/convert.go b/aci/convert/convert.go index 31c71955f..532df6787 100644 --- a/aci/convert/convert.go +++ b/aci/convert/convert.go @@ -129,7 +129,7 @@ func ToContainerGroup(ctx context.Context, aciContext store.AciContext, p types. } func getDNSSidecar(containers []containerinstance.Container) containerinstance.Container { - var names []string + names := []string{"/hosts"} for _, container := range containers { names = append(names, *container.Name) } diff --git a/aci/convert/convert_test.go b/aci/convert/convert_test.go index fcacfd6b2..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{"service1", "service2"}) + 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 index da243fc30..b7e0f5497 100644 --- a/aci/etchosts/Dockerfile +++ b/aci/etchosts/Dockerfile @@ -18,5 +18,4 @@ 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 -ENTRYPOINT ["/hosts"] +COPY --from=builder /go/bin/hosts /hosts \ No newline at end of file From 18e43b277c46c7b2d3f0dd9a8289ee7b7f661c7b Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Mon, 26 Oct 2020 15:06:30 +0100 Subject: [PATCH 3/4] =?UTF-8?q?fmt.Scanln()=20does=20not=20wait=20forever?= =?UTF-8?q?=E2=80=A6=20waiting=20for=20process=20termination=20signal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guillaume Tardif --- aci/etchosts/main/main.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/aci/etchosts/main/main.go b/aci/etchosts/main/main.go index cb2b8315c..547d64751 100644 --- a/aci/etchosts/main/main.go +++ b/aci/etchosts/main/main.go @@ -18,9 +18,10 @@ package main import ( "fmt" - "os" - "github.com/docker/compose-cli/aci/etchosts" + "os" + "os/signal" + "syscall" ) const hosts = "/etc/hosts" @@ -38,6 +39,8 @@ func main() { } // 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 - fmt.Scanln() // pause + // Pause forever (until someone explicitely terminates this process ; go is not happy to stop all goroutines otherwise) + exitSignal := make(chan os.Signal) + signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM) + <-exitSignal } From b138a3e4f62bf72d468bf961697a744373fa6e41 Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Mon, 26 Oct 2020 15:06:54 +0100 Subject: [PATCH 4/4] Log something to be able to see what is happening remotely Signed-off-by: Guillaume Tardif --- aci/etchosts/hosts.go | 3 +++ aci/etchosts/main/main.go | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/aci/etchosts/hosts.go b/aci/etchosts/hosts.go index 7caa23d02..468197ed3 100644 --- a/aci/etchosts/hosts.go +++ b/aci/etchosts/hosts.go @@ -17,7 +17,9 @@ package etchosts import ( + "fmt" "os" + "strings" ) // SetHostNames appends hosts aliases for loopback address to etc/host file @@ -28,6 +30,7 @@ func SetHostNames(file string, hosts ...string) error { } 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) } diff --git a/aci/etchosts/main/main.go b/aci/etchosts/main/main.go index 547d64751..925bb166d 100644 --- a/aci/etchosts/main/main.go +++ b/aci/etchosts/main/main.go @@ -18,10 +18,11 @@ package main import ( "fmt" - "github.com/docker/compose-cli/aci/etchosts" "os" "os/signal" "syscall" + + "github.com/docker/compose-cli/aci/etchosts" ) const hosts = "/etc/hosts" @@ -39,8 +40,8 @@ func main() { } // 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 explicitely terminates this process ; go is not happy to stop all goroutines otherwise) - exitSignal := make(chan os.Signal) + // 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 }