Add Restart Policy support when running single container

This commit is contained in:
Ulysses Souza 2020-07-16 11:05:31 +02:00 коммит произвёл Guillaume Tardif
Родитель 2c4be4f7e3
Коммит a2c2d6aa5d
12 изменённых файлов: 189 добавлений и 48 удалений

Просмотреть файл

@ -24,6 +24,11 @@ func ContainerToComposeProject(r containers.ContainerConfig) (types.Project, err
return types.Project{}, err
}
composeRestartPolicyCondition := r.RestartPolicyCondition
if composeRestartPolicyCondition == "no" {
composeRestartPolicyCondition = "none"
}
project := types.Project{
Name: r.ID,
Services: []types.ServiceConfig{
@ -41,6 +46,9 @@ func ContainerToComposeProject(r containers.ContainerConfig) (types.Project, err
MemoryBytes: types.UnitBytes(r.MemLimit.Value()),
},
},
RestartPolicy: &types.RestartPolicy{
Condition: composeRestartPolicyCondition,
},
},
},
},

Просмотреть файл

@ -48,6 +48,18 @@ func (suite *ContainerConvertTestSuite) TestConvertContainerEnvironment() {
}))
}
func (suite *ContainerConvertTestSuite) TestConvertRestartPolicy() {
container := containers.ContainerConfig{
ID: "container1",
RestartPolicyCondition: "no",
}
project, err := ContainerToComposeProject(container)
Expect(err).To(BeNil())
service1 := project.Services[0]
Expect(service1.Name).To(Equal(container.ID))
Expect(service1.Deploy.RestartPolicy.Condition).To(Equal("none"))
}
func TestContainerConvertTestSuite(t *testing.T) {
RegisterTestingT(t)
suite.Run(t, new(ContainerConvertTestSuite))

Просмотреть файл

@ -26,7 +26,7 @@ import (
"strconv"
"strings"
"github.com/Azure/azure-sdk-for-go/profiles/latest/containerinstance/mgmt/containerinstance"
"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance"
"github.com/Azure/go-autorest/autorest/to"
"github.com/compose-spec/compose-go/types"
@ -71,6 +71,15 @@ func ToContainerGroup(aciContext store.AciContext, p types.Project) (containerin
return containerinstance.ContainerGroup{}, err
}
var restartPolicyCondition containerinstance.ContainerGroupRestartPolicy
if len(p.Services) == 1 &&
p.Services[0].Deploy != nil &&
p.Services[0].Deploy.RestartPolicy != nil {
restartPolicyCondition = toAciRestartPolicy(p.Services[0].Deploy.RestartPolicy.Condition)
} else {
restartPolicyCondition = containerinstance.Always
}
var containers []containerinstance.Container
groupDefinition := containerinstance.ContainerGroup{
Name: &containerGroupName,
@ -80,6 +89,7 @@ func ToContainerGroup(aciContext store.AciContext, p types.Project) (containerin
Containers: &containers,
Volumes: volumes,
ImageRegistryCredentials: &registryCreds,
RestartPolicy: restartPolicyCondition,
},
}
@ -125,6 +135,32 @@ func ToContainerGroup(aciContext store.AciContext, p types.Project) (containerin
return groupDefinition, nil
}
func toAciRestartPolicy(restartPolicy string) containerinstance.ContainerGroupRestartPolicy {
switch restartPolicy {
case containers.RestartPolicyNone:
return containerinstance.Never
case containers.RestartPolicyAny:
return containerinstance.Always
case containers.RestartPolicyOnFailure:
return containerinstance.OnFailure
default:
return containerinstance.Always
}
}
func toContainerRestartPolicy(aciRestartPolicy containerinstance.ContainerGroupRestartPolicy) string {
switch aciRestartPolicy {
case containerinstance.Never:
return containers.RestartPolicyNone
case containerinstance.Always:
return containers.RestartPolicyAny
case containerinstance.OnFailure:
return containers.RestartPolicyOnFailure
default:
return containers.RestartPolicyAny
}
}
func getDNSSidecar(containers []containerinstance.Container) containerinstance.Container {
var commands []string
for _, container := range containers {
@ -348,19 +384,20 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
platform := string(cg.OsType)
c := containers.Container{
ID: containerID,
Status: status,
Image: to.String(cc.Image),
Command: command,
CPUTime: 0,
CPULimit: cpuLimit,
MemoryUsage: 0,
MemoryLimit: uint64(memLimits),
PidsCurrent: 0,
PidsLimit: 0,
Labels: nil,
Ports: ToPorts(cg.IPAddress, *cc.Ports),
Platform: platform,
ID: containerID,
Status: status,
Image: to.String(cc.Image),
Command: command,
CPUTime: 0,
CPULimit: cpuLimit,
MemoryUsage: 0,
MemoryLimit: uint64(memLimits),
PidsCurrent: 0,
PidsLimit: 0,
Labels: nil,
Ports: ToPorts(cg.IPAddress, *cc.Ports),
Platform: platform,
RestartPolicyCondition: toContainerRestartPolicy(cg.RestartPolicy),
}
return c, nil

Просмотреть файл

@ -104,6 +104,7 @@ func (suite *ConvertTestSuite) TestContainerGroupToContainer() {
Protocol: "tcp",
HostIP: "42.42.42.42",
}},
RestartPolicyCondition: "any",
}
container, err := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer)
@ -158,6 +159,47 @@ func (suite *ConvertTestSuite) TestComposeSingleContainerGroupToContainerNoDnsSi
Expect(*(*group.Containers)[0].Image).To(Equal("image1"))
}
func (suite *ConvertTestSuite) TestComposeSingleContainerGroupToContainerSpecificRestartPolicy() {
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
Deploy: &types.DeployConfig{
RestartPolicy: &types.RestartPolicy{
Condition: "on-failure",
},
},
},
},
}
group, err := ToContainerGroup(suite.ctx, project)
Expect(err).To(BeNil())
Expect(len(*group.Containers)).To(Equal(1))
Expect(*(*group.Containers)[0].Name).To(Equal("service1"))
Expect(group.RestartPolicy).To(Equal(containerinstance.OnFailure))
}
func (suite *ConvertTestSuite) TestComposeSingleContainerGroupToContainerDefaultRestartPolicy() {
project := types.Project{
Services: []types.ServiceConfig{
{
Name: "service1",
Image: "image1",
},
},
}
group, err := ToContainerGroup(suite.ctx, project)
Expect(err).To(BeNil())
Expect(len(*group.Containers)).To(Equal(1))
Expect(*(*group.Containers)[0].Name).To(Equal("service1"))
Expect(group.RestartPolicy).To(Equal(containerinstance.Always))
}
func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerMultiplePorts() {
project := types.Project{
Services: []types.ServiceConfig{
@ -287,6 +329,20 @@ func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerenvVar() {
Expect(envVars).To(ContainElement(containerinstance.EnvironmentVariable{Name: to.StringPtr("key2"), Value: to.StringPtr("value2")}))
}
func (suite *ConvertTestSuite) TestConvertToAciRestartPolicyCondition() {
Expect(toAciRestartPolicy("none")).To(Equal(containerinstance.Never))
Expect(toAciRestartPolicy("always")).To(Equal(containerinstance.Always))
Expect(toAciRestartPolicy("on-failure")).To(Equal(containerinstance.OnFailure))
Expect(toAciRestartPolicy("on-failure:5")).To(Equal(containerinstance.Always))
}
func (suite *ConvertTestSuite) TestConvertToDockerRestartPolicyCondition() {
Expect(toContainerRestartPolicy(containerinstance.Never)).To(Equal("none"))
Expect(toContainerRestartPolicy(containerinstance.Always)).To(Equal("any"))
Expect(toContainerRestartPolicy(containerinstance.OnFailure)).To(Equal("on-failure"))
Expect(toContainerRestartPolicy("")).To(Equal("any"))
}
func TestConvertTestSuite(t *testing.T) {
RegisterTestingT(t)
suite.Run(t, new(ConvertTestSuite))

Просмотреть файл

@ -52,6 +52,7 @@ func Command() *cobra.Command {
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")
cmd.Flags().StringVarP(&opts.RestartPolicyCondition, "restart", "", "no", "Restart policy to apply when a container exits")
return cmd
}

1
cli/cmd/run/testdata/run-help.golden поставляемый
Просмотреть файл

@ -11,4 +11,5 @@ Flags:
-m, --memory bytes Memory limit
--name string Assign a name to the container
-p, --publish stringArray Publish a container's port(s). [HOST_PORT:]CONTAINER_PORT
--restart string Restart policy to apply when a container exits (default "no")
-v, --volume stringArray Volume. Ex: user:key@my_share:/absolute/path/to/target

3
cli/cmd/testdata/inspect-out-id.golden поставляемый
Просмотреть файл

@ -11,5 +11,6 @@
"PidsLimit": 0,
"Labels": null,
"Ports": null,
"Platform": "Linux"
"Platform": "Linux",
"RestartPolicyCondition": ""
}

Просмотреть файл

@ -30,14 +30,15 @@ import (
// Opts contain run command options
type Opts struct {
Name string
Publish []string
Labels []string
Volumes []string
Cpus float64
Memory formatter.MemBytes
Detach bool
Environment []string
Name string
Publish []string
Labels []string
Volumes []string
Cpus float64
Memory formatter.MemBytes
Detach bool
Environment []string
RestartPolicyCondition string
}
// ToContainerConfig convert run options to a container configuration
@ -57,14 +58,15 @@ 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,
Environment: r.Environment,
ID: r.Name,
Image: image,
Ports: publish,
Labels: labels,
Volumes: r.Volumes,
MemLimit: r.Memory,
CPULimit: r.Cpus,
Environment: r.Environment,
RestartPolicyCondition: r.RestartPolicyCondition,
}, nil
}

Просмотреть файл

@ -25,19 +25,20 @@ import (
// Container represents a created container
type Container struct {
ID string
Status string
Image string
Command string
CPUTime uint64
CPULimit float64
MemoryUsage uint64
MemoryLimit uint64
PidsCurrent uint64
PidsLimit uint64
Labels []string
Ports []Port
Platform string
ID string
Status string
Image string
Command string
CPUTime uint64
CPULimit float64
MemoryUsage uint64
MemoryLimit uint64
PidsCurrent uint64
PidsLimit uint64
Labels []string
Ports []Port
Platform string
RestartPolicyCondition string
}
// Port represents a published port of a container
@ -70,6 +71,8 @@ type ContainerConfig struct {
CPULimit float64
// Environment variables
Environment []string
// Restart policy condition
RestartPolicyCondition string
}
// ExecRequest contaiens configuration about an exec request

11
containers/types.go Normal file
Просмотреть файл

@ -0,0 +1,11 @@
package containers
const (
// RestartPolicyAny Always restarts
RestartPolicyAny = "any"
// RestartPolicyNone Never restarts
// "no" is the value for docker run, "none" is the value in compose file (and default differ
RestartPolicyNone = "none"
// RestartPolicyOnFailure Restarts only on failure
RestartPolicyOnFailure = "on-failure"
)

Просмотреть файл

@ -18,6 +18,8 @@ package ecs
import (
"context"
ecsplugin "github.com/docker/ecs-plugin/pkg/amazon/backend"
"github.com/docker/api/backend"
"github.com/docker/api/compose"
"github.com/docker/api/containers"
@ -25,7 +27,6 @@ import (
"github.com/docker/api/context/cloud"
"github.com/docker/api/context/store"
"github.com/docker/api/errdefs"
ecsplugin "github.com/docker/ecs-plugin/pkg/amazon/backend"
)
const backendType = store.EcsContextType

Просмотреть файл

@ -86,6 +86,7 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
defer deleteResourceGroup(resourceGroupName)
var nginxExposedURL string
var containerID string
s.Step("runs nginx on port 80", func() {
aciContext := store.AciContext{
SubscriptionID: subscriptionID,
@ -118,7 +119,7 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
Expect(containerFields[1]).To(Equal("nginx"))
Expect(containerFields[2]).To(Equal("Running"))
exposedIP := containerFields[3]
containerID := containerFields[0]
containerID = containerFields[0]
Expect(exposedIP).To(ContainSubstring(":80->80/tcp"))
nginxExposedURL = strings.ReplaceAll(exposedIP, "->80/tcp", "")
@ -129,6 +130,13 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
Expect(output).To(ContainSubstring("GET"))
})
s.Step("inspect command", func() {
inspect := s.NewDockerCommand("inspect", containerID).ExecOrDie()
Expect(inspect).To(ContainSubstring("\"Platform\": \"Linux\""))
Expect(inspect).To(ContainSubstring("\"CPULimit\": 1"))
Expect(inspect).To(ContainSubstring("\"RestartPolicyCondition\": \"none\""))
})
s.Step("exec command", func() {
output := s.NewDockerCommand("exec", containerName, "pwd").ExecOrDie()
Expect(output).To(ContainSubstring("/"))
@ -174,13 +182,12 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
shutdown := make(chan time.Time)
errs := make(chan error)
outChan := make(chan string)
cmd := s.NewDockerCommand("run", "nginx", "--memory", "0.1G", "--cpus", "0.1", "-p", "80:80", "--name", testContainerName).WithTimeout(shutdown)
cmd := s.NewDockerCommand("run", "nginx", "--restart", "on-failure", "--memory", "0.1G", "--cpus", "0.1", "-p", "80:80", "--name", testContainerName).WithTimeout(shutdown)
go func() {
output, err := cmd.Exec()
outChan <- output
errs <- err
}()
var containerID string
err := WaitFor(time.Second, 100*time.Second, errs, func() bool {
output := s.NewDockerCommand("ps").ExecOrDie()
lines := Lines(output)
@ -201,6 +208,7 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
inspect := s.NewDockerCommand("inspect", containerID).ExecOrDie()
Expect(inspect).To(ContainSubstring("\"CPULimit\": 0.1"))
Expect(inspect).To(ContainSubstring("\"MemoryLimit\": 107374182"))
Expect(inspect).To(ContainSubstring("\"RestartPolicyCondition\": \"on-failure\""))
// Give a little time to get logs of the curl call
time.Sleep(5 * time.Second)