mirror of https://github.com/docker/compose.git
Add Restart Policy support when running single container
This commit is contained in:
parent
2c4be4f7e3
commit
a2c2d6aa5d
|
@ -24,6 +24,11 @@ func ContainerToComposeProject(r containers.ContainerConfig) (types.Project, err
|
||||||
return types.Project{}, err
|
return types.Project{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composeRestartPolicyCondition := r.RestartPolicyCondition
|
||||||
|
if composeRestartPolicyCondition == "no" {
|
||||||
|
composeRestartPolicyCondition = "none"
|
||||||
|
}
|
||||||
|
|
||||||
project := types.Project{
|
project := types.Project{
|
||||||
Name: r.ID,
|
Name: r.ID,
|
||||||
Services: []types.ServiceConfig{
|
Services: []types.ServiceConfig{
|
||||||
|
@ -41,6 +46,9 @@ func ContainerToComposeProject(r containers.ContainerConfig) (types.Project, err
|
||||||
MemoryBytes: types.UnitBytes(r.MemLimit.Value()),
|
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) {
|
func TestContainerConvertTestSuite(t *testing.T) {
|
||||||
RegisterTestingT(t)
|
RegisterTestingT(t)
|
||||||
suite.Run(t, new(ContainerConvertTestSuite))
|
suite.Run(t, new(ContainerConvertTestSuite))
|
||||||
|
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"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/Azure/go-autorest/autorest/to"
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
|
||||||
|
@ -71,6 +71,15 @@ func ToContainerGroup(aciContext store.AciContext, p types.Project) (containerin
|
||||||
return containerinstance.ContainerGroup{}, err
|
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
|
var containers []containerinstance.Container
|
||||||
groupDefinition := containerinstance.ContainerGroup{
|
groupDefinition := containerinstance.ContainerGroup{
|
||||||
Name: &containerGroupName,
|
Name: &containerGroupName,
|
||||||
|
@ -80,6 +89,7 @@ func ToContainerGroup(aciContext store.AciContext, p types.Project) (containerin
|
||||||
Containers: &containers,
|
Containers: &containers,
|
||||||
Volumes: volumes,
|
Volumes: volumes,
|
||||||
ImageRegistryCredentials: ®istryCreds,
|
ImageRegistryCredentials: ®istryCreds,
|
||||||
|
RestartPolicy: restartPolicyCondition,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +135,32 @@ func ToContainerGroup(aciContext store.AciContext, p types.Project) (containerin
|
||||||
return groupDefinition, nil
|
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 {
|
func getDNSSidecar(containers []containerinstance.Container) containerinstance.Container {
|
||||||
var commands []string
|
var commands []string
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
|
@ -361,6 +397,7 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
|
||||||
Labels: nil,
|
Labels: nil,
|
||||||
Ports: ToPorts(cg.IPAddress, *cc.Ports),
|
Ports: ToPorts(cg.IPAddress, *cc.Ports),
|
||||||
Platform: platform,
|
Platform: platform,
|
||||||
|
RestartPolicyCondition: toContainerRestartPolicy(cg.RestartPolicy),
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
|
|
|
@ -104,6 +104,7 @@ func (suite *ConvertTestSuite) TestContainerGroupToContainer() {
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
HostIP: "42.42.42.42",
|
HostIP: "42.42.42.42",
|
||||||
}},
|
}},
|
||||||
|
RestartPolicyCondition: "any",
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer)
|
container, err := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer)
|
||||||
|
@ -158,6 +159,47 @@ func (suite *ConvertTestSuite) TestComposeSingleContainerGroupToContainerNoDnsSi
|
||||||
Expect(*(*group.Containers)[0].Image).To(Equal("image1"))
|
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() {
|
func (suite *ConvertTestSuite) TestComposeContainerGroupToContainerMultiplePorts() {
|
||||||
project := types.Project{
|
project := types.Project{
|
||||||
Services: []types.ServiceConfig{
|
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")}))
|
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) {
|
func TestConvertTestSuite(t *testing.T) {
|
||||||
RegisterTestingT(t)
|
RegisterTestingT(t)
|
||||||
suite.Run(t, new(ConvertTestSuite))
|
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().Float64Var(&opts.Cpus, "cpus", 1., "Number of CPUs")
|
||||||
cmd.Flags().VarP(&opts.Memory, "memory", "m", "Memory limit")
|
cmd.Flags().VarP(&opts.Memory, "memory", "m", "Memory limit")
|
||||||
cmd.Flags().StringArrayVarP(&opts.Environment, "env", "e", []string{}, "Set environment variables")
|
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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,5 @@ Flags:
|
||||||
-m, --memory bytes Memory limit
|
-m, --memory bytes Memory limit
|
||||||
--name string Assign a name to the container
|
--name string Assign a name to the container
|
||||||
-p, --publish stringArray Publish a container's port(s). [HOST_PORT:]CONTAINER_PORT
|
-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
|
-v, --volume stringArray Volume. Ex: user:key@my_share:/absolute/path/to/target
|
||||||
|
|
|
@ -11,5 +11,6 @@
|
||||||
"PidsLimit": 0,
|
"PidsLimit": 0,
|
||||||
"Labels": null,
|
"Labels": null,
|
||||||
"Ports": null,
|
"Ports": null,
|
||||||
"Platform": "Linux"
|
"Platform": "Linux",
|
||||||
|
"RestartPolicyCondition": ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ type Opts struct {
|
||||||
Memory formatter.MemBytes
|
Memory formatter.MemBytes
|
||||||
Detach bool
|
Detach bool
|
||||||
Environment []string
|
Environment []string
|
||||||
|
RestartPolicyCondition string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToContainerConfig convert run options to a container configuration
|
// ToContainerConfig convert run options to a container configuration
|
||||||
|
@ -65,6 +66,7 @@ func (r *Opts) ToContainerConfig(image string) (containers.ContainerConfig, erro
|
||||||
MemLimit: r.Memory,
|
MemLimit: r.Memory,
|
||||||
CPULimit: r.Cpus,
|
CPULimit: r.Cpus,
|
||||||
Environment: r.Environment,
|
Environment: r.Environment,
|
||||||
|
RestartPolicyCondition: r.RestartPolicyCondition,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ type Container struct {
|
||||||
Labels []string
|
Labels []string
|
||||||
Ports []Port
|
Ports []Port
|
||||||
Platform string
|
Platform string
|
||||||
|
RestartPolicyCondition string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Port represents a published port of a container
|
// Port represents a published port of a container
|
||||||
|
@ -70,6 +71,8 @@ type ContainerConfig struct {
|
||||||
CPULimit float64
|
CPULimit float64
|
||||||
// Environment variables
|
// Environment variables
|
||||||
Environment []string
|
Environment []string
|
||||||
|
// Restart policy condition
|
||||||
|
RestartPolicyCondition string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecRequest contaiens configuration about an exec request
|
// ExecRequest contaiens configuration about an exec request
|
||||||
|
|
|
@ -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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
ecsplugin "github.com/docker/ecs-plugin/pkg/amazon/backend"
|
||||||
|
|
||||||
"github.com/docker/api/backend"
|
"github.com/docker/api/backend"
|
||||||
"github.com/docker/api/compose"
|
"github.com/docker/api/compose"
|
||||||
"github.com/docker/api/containers"
|
"github.com/docker/api/containers"
|
||||||
|
@ -25,7 +27,6 @@ import (
|
||||||
"github.com/docker/api/context/cloud"
|
"github.com/docker/api/context/cloud"
|
||||||
"github.com/docker/api/context/store"
|
"github.com/docker/api/context/store"
|
||||||
"github.com/docker/api/errdefs"
|
"github.com/docker/api/errdefs"
|
||||||
ecsplugin "github.com/docker/ecs-plugin/pkg/amazon/backend"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const backendType = store.EcsContextType
|
const backendType = store.EcsContextType
|
||||||
|
|
|
@ -86,6 +86,7 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
|
||||||
defer deleteResourceGroup(resourceGroupName)
|
defer deleteResourceGroup(resourceGroupName)
|
||||||
|
|
||||||
var nginxExposedURL string
|
var nginxExposedURL string
|
||||||
|
var containerID string
|
||||||
s.Step("runs nginx on port 80", func() {
|
s.Step("runs nginx on port 80", func() {
|
||||||
aciContext := store.AciContext{
|
aciContext := store.AciContext{
|
||||||
SubscriptionID: subscriptionID,
|
SubscriptionID: subscriptionID,
|
||||||
|
@ -118,7 +119,7 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
|
||||||
Expect(containerFields[1]).To(Equal("nginx"))
|
Expect(containerFields[1]).To(Equal("nginx"))
|
||||||
Expect(containerFields[2]).To(Equal("Running"))
|
Expect(containerFields[2]).To(Equal("Running"))
|
||||||
exposedIP := containerFields[3]
|
exposedIP := containerFields[3]
|
||||||
containerID := containerFields[0]
|
containerID = containerFields[0]
|
||||||
Expect(exposedIP).To(ContainSubstring(":80->80/tcp"))
|
Expect(exposedIP).To(ContainSubstring(":80->80/tcp"))
|
||||||
|
|
||||||
nginxExposedURL = strings.ReplaceAll(exposedIP, "->80/tcp", "")
|
nginxExposedURL = strings.ReplaceAll(exposedIP, "->80/tcp", "")
|
||||||
|
@ -129,6 +130,13 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
|
||||||
Expect(output).To(ContainSubstring("GET"))
|
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() {
|
s.Step("exec command", func() {
|
||||||
output := s.NewDockerCommand("exec", containerName, "pwd").ExecOrDie()
|
output := s.NewDockerCommand("exec", containerName, "pwd").ExecOrDie()
|
||||||
Expect(output).To(ContainSubstring("/"))
|
Expect(output).To(ContainSubstring("/"))
|
||||||
|
@ -174,13 +182,12 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
|
||||||
shutdown := make(chan time.Time)
|
shutdown := make(chan time.Time)
|
||||||
errs := make(chan error)
|
errs := make(chan error)
|
||||||
outChan := make(chan string)
|
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() {
|
go func() {
|
||||||
output, err := cmd.Exec()
|
output, err := cmd.Exec()
|
||||||
outChan <- output
|
outChan <- output
|
||||||
errs <- err
|
errs <- err
|
||||||
}()
|
}()
|
||||||
var containerID string
|
|
||||||
err := WaitFor(time.Second, 100*time.Second, errs, func() bool {
|
err := WaitFor(time.Second, 100*time.Second, errs, func() bool {
|
||||||
output := s.NewDockerCommand("ps").ExecOrDie()
|
output := s.NewDockerCommand("ps").ExecOrDie()
|
||||||
lines := Lines(output)
|
lines := Lines(output)
|
||||||
|
@ -201,6 +208,7 @@ func (s *E2eACISuite) TestACIRunSingleContainer() {
|
||||||
inspect := s.NewDockerCommand("inspect", containerID).ExecOrDie()
|
inspect := s.NewDockerCommand("inspect", containerID).ExecOrDie()
|
||||||
Expect(inspect).To(ContainSubstring("\"CPULimit\": 0.1"))
|
Expect(inspect).To(ContainSubstring("\"CPULimit\": 0.1"))
|
||||||
Expect(inspect).To(ContainSubstring("\"MemoryLimit\": 107374182"))
|
Expect(inspect).To(ContainSubstring("\"MemoryLimit\": 107374182"))
|
||||||
|
Expect(inspect).To(ContainSubstring("\"RestartPolicyCondition\": \"on-failure\""))
|
||||||
|
|
||||||
// Give a little time to get logs of the curl call
|
// Give a little time to get logs of the curl call
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
Loading…
Reference in New Issue