Browse Source

Merge pull request #799 from docker/aci_etchosts

minimalist container image to setup /etc/hosts on ACI
Guillaume Tardif 5 years ago
parent
commit
382827241d

+ 4 - 8
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),

+ 1 - 1
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")

+ 21 - 0
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

+ 38 - 0
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
+}

+ 48 - 0
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)
+}

+ 47 - 0
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
+}

+ 4 - 0
aci/etchosts/testdata/etchosts.golden

@@ -0,0 +1,4 @@
+
+127.0.0.1 foo
+127.0.0.1 bar
+127.0.0.1 zot

+ 2 - 3
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

+ 2 - 2
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