Browse Source

Merge pull request #358 from docker/aci_env_variable

Aci env variable
Guillaume Tardif 5 years ago
parent
commit
de4ba33c60

+ 1 - 31
azure/backend.go

@@ -162,41 +162,11 @@ func (cs *aciContainerService) Run(ctx context.Context, r containers.ContainerCo
 		return errors.New(fmt.Sprintf("invalid container name. ACI container name cannot include %q", composeContainerSeparator))
 	}
 
-	var ports []types.ServicePortConfig
-	for _, p := range r.Ports {
-		ports = append(ports, types.ServicePortConfig{
-			Target:    p.ContainerPort,
-			Published: p.HostPort,
-		})
-	}
-
-	projectVolumes, serviceConfigVolumes, err := convert.GetRunVolumes(r.Volumes)
+	project, err := convert.ContainerToComposeProject(r, singleContainerName)
 	if err != nil {
 		return err
 	}
 
-	project := types.Project{
-		Name: r.ID,
-		Services: []types.ServiceConfig{
-			{
-				Name:    singleContainerName,
-				Image:   r.Image,
-				Ports:   ports,
-				Labels:  r.Labels,
-				Volumes: serviceConfigVolumes,
-				Deploy: &types.DeployConfig{
-					Resources: types.Resources{
-						Limits: &types.Resource{
-							NanoCPUs:    fmt.Sprintf("%f", r.CPULimit),
-							MemoryBytes: types.UnitBytes(r.MemLimit.Value()),
-						},
-					},
-				},
-			},
-		},
-		Volumes: projectVolumes,
-	}
-
 	logrus.Debugf("Running container %q with name %q\n", r.Image, r.ID)
 	groupDefinition, err := convert.ToContainerGroup(cs.ctx, project)
 	if err != nil {

+ 63 - 0
azure/convert/container.go

@@ -0,0 +1,63 @@
+package convert
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/compose-spec/compose-go/types"
+
+	"github.com/docker/api/containers"
+)
+
+// ContainerToComposeProject convert container config to compose project
+func ContainerToComposeProject(r containers.ContainerConfig, containerID string) (types.Project, error) {
+	var ports []types.ServicePortConfig
+	for _, p := range r.Ports {
+		ports = append(ports, types.ServicePortConfig{
+			Target:    p.ContainerPort,
+			Published: p.HostPort,
+		})
+	}
+
+	projectVolumes, serviceConfigVolumes, err := GetRunVolumes(r.Volumes)
+	if err != nil {
+		return types.Project{}, err
+	}
+
+	project := types.Project{
+		Name: r.ID,
+		Services: []types.ServiceConfig{
+			{
+				Name:        containerID,
+				Image:       r.Image,
+				Ports:       ports,
+				Labels:      r.Labels,
+				Volumes:     serviceConfigVolumes,
+				Environment: toComposeEnvs(r.Environment),
+				Deploy: &types.DeployConfig{
+					Resources: types.Resources{
+						Limits: &types.Resource{
+							NanoCPUs:    fmt.Sprintf("%f", r.CPULimit),
+							MemoryBytes: types.UnitBytes(r.MemLimit.Value()),
+						},
+					},
+				},
+			},
+		},
+		Volumes: projectVolumes,
+	}
+	return project, nil
+}
+
+func toComposeEnvs(opts []string) types.MappingWithEquals {
+	result := map[string]*string{}
+	for _, env := range opts {
+		tokens := strings.Split(env, "=")
+		if len(tokens) > 1 {
+			result[tokens[0]] = &tokens[1]
+		} else {
+			result[env] = nil
+		}
+	}
+	return result
+}

+ 54 - 0
azure/convert/container_test.go

@@ -0,0 +1,54 @@
+/*
+   Copyright 2020 Docker, Inc.
+
+   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 convert
+
+import (
+	"testing"
+
+	"github.com/Azure/go-autorest/autorest/to"
+	"github.com/compose-spec/compose-go/types"
+
+	"github.com/docker/api/containers"
+
+	. "github.com/onsi/gomega"
+	"github.com/stretchr/testify/suite"
+)
+
+type ContainerConvertTestSuite struct {
+	suite.Suite
+}
+
+func (suite *ContainerConvertTestSuite) TestConvertContainerEnvironment() {
+	container := containers.ContainerConfig{
+		ID:          "container1",
+		Environment: []string{"key1=value1", "key2", "key3=value3"},
+	}
+	project, err := ContainerToComposeProject(container, "ID")
+	Expect(err).To(BeNil())
+	service1 := project.Services[0]
+	Expect(service1.Name).To(Equal("ID"))
+	Expect(service1.Environment).To(Equal(types.MappingWithEquals{
+		"key1": to.StringPtr("value1"),
+		"key2": nil,
+		"key3": to.StringPtr("value3"),
+	}))
+}
+
+func TestContainerConvertTestSuite(t *testing.T) {
+	RegisterTestingT(t)
+	suite.Run(t, new(ContainerConvertTestSuite))
+}

+ 19 - 1
azure/convert/convert.go

@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"math"
+	"os"
 	"strconv"
 	"strings"
 
@@ -280,7 +281,8 @@ func (s serviceConfigAciHelper) getAciContainer(volumesCache map[string]bool) (c
 	return containerinstance.Container{
 		Name: to.StringPtr(s.Name),
 		ContainerProperties: &containerinstance.ContainerProperties{
-			Image: to.StringPtr(s.Image),
+			Image:                to.StringPtr(s.Image),
+			EnvironmentVariables: getEnvVariables(s.Environment),
 			Resources: &containerinstance.ResourceRequirements{
 				Limits: &containerinstance.ResourceLimits{
 					MemoryInGB: to.Float64Ptr(memLimit),
@@ -294,7 +296,23 @@ func (s serviceConfigAciHelper) getAciContainer(volumesCache map[string]bool) (c
 			VolumeMounts: volumes,
 		},
 	}, nil
+}
 
+func getEnvVariables(composeEnv types.MappingWithEquals) *[]containerinstance.EnvironmentVariable {
+	result := []containerinstance.EnvironmentVariable{}
+	for key, value := range composeEnv {
+		var strValue string
+		if value == nil {
+			strValue = os.Getenv(key)
+		} else {
+			strValue = *value
+		}
+		result = append(result, containerinstance.EnvironmentVariable{
+			Name:  to.StringPtr(key),
+			Value: to.StringPtr(strValue),
+		})
+	}
+	return &result
 }
 
 func bytesToGb(b types.UnitBytes) float64 {

+ 27 - 0
azure/convert/convert_test.go

@@ -17,6 +17,7 @@
 package convert
 
 import (
+	"os"
 	"testing"
 
 	"github.com/Azure/azure-sdk-for-go/profiles/latest/containerinstance/mgmt/containerinstance"
@@ -260,6 +261,32 @@ func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerResourceLimit
 	Expect(*limits.MemoryInGB).To(Equal(float64(1)))
 }
 
+func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerenvVar() {
+	err := os.Setenv("key2", "value2")
+	Expect(err).To(BeNil())
+	project := types.Project{
+		Services: []types.ServiceConfig{
+			{
+				Name:  "service1",
+				Image: "image1",
+				Environment: types.MappingWithEquals{
+					"key1": to.StringPtr("value1"),
+					"key2": nil,
+				},
+			},
+		},
+	}
+
+	group, err := ToContainerGroup(suite.ctx, project)
+	Expect(err).To(BeNil())
+
+	container1 := (*group.Containers)[0]
+	envVars := *container1.EnvironmentVariables
+	Expect(len(envVars)).To(Equal(2))
+	Expect(envVars).To(ContainElement(containerinstance.EnvironmentVariable{Name: to.StringPtr("key1"), Value: to.StringPtr("value1")}))
+	Expect(envVars).To(ContainElement(containerinstance.EnvironmentVariable{Name: to.StringPtr("key2"), Value: to.StringPtr("value2")}))
+}
+
 func TestConvertTestSuite(t *testing.T) {
 	RegisterTestingT(t)
 	suite.Run(t, new(ConvertTestSuite))

+ 1 - 0
cli/cmd/run/run.go

@@ -51,6 +51,7 @@ func Command() *cobra.Command {
 	cmd.Flags().BoolVarP(&opts.Detach, "detach", "d", false, "Run container in background and print container ID")
 	cmd.Flags().Float64Var(&opts.Cpus, "cpus", 1., "Number of CPUs")
 	cmd.Flags().VarP(&opts.Memory, "memory", "m", "Memory limit")
+	cmd.Flags().StringArrayVarP(&opts.Environment, "env", "e", []string{}, "Set environment variables")
 
 	return cmd
 }

+ 1 - 0
cli/cmd/run/testdata/run-help.golden

@@ -6,6 +6,7 @@ Usage:
 Flags:
       --cpus float            Number of CPUs (default 1)
   -d, --detach                Run container in background and print container ID
+  -e, --env stringArray       Set environment variables
   -l, --label stringArray     Set meta data on a container
   -m, --memory bytes          Memory limit
       --name string           Assign a name to the container

+ 16 - 14
cli/options/run/opts.go

@@ -30,13 +30,14 @@ import (
 
 // Opts contain run command options
 type Opts struct {
-	Name    string
-	Publish []string
-	Labels  []string
-	Volumes []string
-	Cpus    float64
-	Memory  formatter.MemBytes
-	Detach  bool
+	Name        string
+	Publish     []string
+	Labels      []string
+	Volumes     []string
+	Cpus        float64
+	Memory      formatter.MemBytes
+	Detach      bool
+	Environment []string
 }
 
 // ToContainerConfig convert run options to a container configuration
@@ -56,13 +57,14 @@ func (r *Opts) ToContainerConfig(image string) (containers.ContainerConfig, erro
 	}
 
 	return containers.ContainerConfig{
-		ID:       r.Name,
-		Image:    image,
-		Ports:    publish,
-		Labels:   labels,
-		Volumes:  r.Volumes,
-		MemLimit: r.Memory,
-		CPULimit: r.Cpus,
+		ID:          r.Name,
+		Image:       image,
+		Ports:       publish,
+		Labels:      labels,
+		Volumes:     r.Volumes,
+		MemLimit:    r.Memory,
+		CPULimit:    r.Cpus,
+		Environment: r.Environment,
 	}, nil
 }
 

+ 2 - 0
containers/api.go

@@ -68,6 +68,8 @@ type ContainerConfig struct {
 	MemLimit formatter.MemBytes
 	// CPUlimit
 	CPULimit float64
+	// Environment variables
+	Environment []string
 }
 
 // LogsRequest contains configuration about a log request

+ 22 - 0
tests/aci-e2e/e2e-aci_test.go

@@ -302,6 +302,28 @@ func (s *E2eACISuite) TestACIBackend() {
 		s.NewDockerCommand("compose", "down", "--project-name", composeProjectName).ExecOrDie()
 	})
 
+	s.T().Run("runs mysql with env variables", func(t *testing.T) {
+		err := os.Setenv("MYSQL_USER", "user1")
+		Expect(err).To(BeNil())
+		s.NewDockerCommand("run", "-d", "mysql:5.7", "-e", "MYSQL_ROOT_PASSWORD=rootpwd", "-e", "MYSQL_DATABASE=mytestdb", "-e", "MYSQL_USER", "-e", "MYSQL_PASSWORD=userpwd").ExecOrDie()
+
+		output := s.NewDockerCommand("ps").ExecOrDie()
+		lines := Lines(output)
+		Expect(len(lines)).To(Equal(2))
+
+		containerFields := Columns(lines[1])
+		containerID := containerFields[0]
+		Expect(containerFields[1]).To(Equal("mysql:5.7"))
+		Expect(containerFields[2]).To(Equal("Running"))
+
+		errs := make(chan error)
+		err = WaitFor(time.Second, 100*time.Second, errs, func() bool {
+			output = s.NewDockerCommand("logs", containerID).ExecOrDie()
+			return strings.Contains(output, "Giving user user1 access to schema mytestdb")
+		})
+		Expect(err).To(BeNil())
+	})
+
 	s.T().Run("switches back to default context", func(t *testing.T) {
 		output := s.NewCommand("docker", "context", "use", "default").ExecOrDie()
 		Expect(output).To(ContainSubstring("default"))