Merge pull request #358 from docker/aci_env_variable

Aci env variable
This commit is contained in:
Guillaume Tardif 2020-07-07 12:40:27 +02:00 committed by GitHub
commit de4ba33c60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 206 additions and 46 deletions

View File

@ -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 {

View File

@ -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
}

View File

@ -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))
}

View File

@ -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 {

View File

@ -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))

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

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

View File

@ -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"))