mirror of
https://github.com/docker/compose.git
synced 2025-04-08 17:05:13 +02:00
Merge pull request #631 from docker/aci_domainname
ACI: allow users to set DNSLabelName and deploy containers with fqdn like `myapp.eastus.azurecontainers.io`
This commit is contained in:
commit
d42a931d67
@ -89,7 +89,7 @@ func (cs *aciComposeService) Ps(ctx context.Context, project string) ([]compose.
|
||||
if isContainerVisible(container, group, false) {
|
||||
continue
|
||||
}
|
||||
res = append(res, convert.ContainerGroupToServiceStatus(getContainerID(group, container), group, container))
|
||||
res = append(res, convert.ContainerGroupToServiceStatus(getContainerID(group, container), group, container, cs.ctx.Location))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func (cs *aciContainerService) List(ctx context.Context, all bool) ([]containers
|
||||
if isContainerVisible(container, group, all) {
|
||||
continue
|
||||
}
|
||||
c := convert.ContainerGroupToContainer(getContainerID(group, container), group, container)
|
||||
c := convert.ContainerGroupToContainer(getContainerID(group, container), group, container, cs.ctx.Location)
|
||||
res = append(res, c)
|
||||
}
|
||||
}
|
||||
@ -257,5 +257,5 @@ func (cs *aciContainerService) Inspect(ctx context.Context, containerID string)
|
||||
return containers.Container{}, errdefs.ErrNotFound
|
||||
}
|
||||
|
||||
return convert.ContainerGroupToContainer(containerID, cg, cc), nil
|
||||
return convert.ContainerGroupToContainer(containerID, cg, cc, cs.ctx.Location), nil
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ func ContainerToComposeProject(r containers.ContainerConfig) (types.Project, err
|
||||
Ports: ports,
|
||||
Labels: r.Labels,
|
||||
Volumes: serviceConfigVolumes,
|
||||
DomainName: r.DomainName,
|
||||
Environment: toComposeEnvs(r.Environment),
|
||||
Deploy: &types.DeployConfig{
|
||||
Resources: types.Resources{
|
||||
|
@ -54,6 +54,18 @@ func TestConvertRestartPolicy(t *testing.T) {
|
||||
assert.Equal(t, service1.Deploy.RestartPolicy.Condition, "none")
|
||||
}
|
||||
|
||||
func TestConvertDomainName(t *testing.T) {
|
||||
container := containers.ContainerConfig{
|
||||
ID: "container1",
|
||||
DomainName: "myapp",
|
||||
}
|
||||
project, err := ContainerToComposeProject(container)
|
||||
assert.NilError(t, err)
|
||||
service1 := project.Services[0]
|
||||
assert.Equal(t, service1.Name, container.ID)
|
||||
assert.Equal(t, service1.DomainName, "myapp")
|
||||
}
|
||||
|
||||
func TestConvertEnvVariables(t *testing.T) {
|
||||
container := containers.ContainerConfig{
|
||||
ID: "container1",
|
||||
|
@ -43,8 +43,8 @@ const (
|
||||
StatusRunning = "Running"
|
||||
// ComposeDNSSidecarName name of the dns sidecar container
|
||||
ComposeDNSSidecarName = "aci--dns--sidecar"
|
||||
dnsSidecarImage = "busybox:1.31.1"
|
||||
|
||||
dnsSidecarImage = "busybox:1.31.1"
|
||||
azureFileDriverName = "azure_file"
|
||||
volumeDriveroptsShareNameKey = "share_name"
|
||||
volumeDriveroptsAccountNameKey = "storage_account_name"
|
||||
@ -93,6 +93,7 @@ func ToContainerGroup(ctx context.Context, aciContext store.AciContext, p types.
|
||||
}
|
||||
|
||||
var groupPorts []containerinstance.Port
|
||||
var dnsLabelName *string
|
||||
for _, s := range project.Services {
|
||||
service := serviceConfigAciHelper(s)
|
||||
containerDefinition, err := service.getAciContainer(volumesCache)
|
||||
@ -102,32 +103,29 @@ func ToContainerGroup(ctx context.Context, aciContext store.AciContext, p types.
|
||||
if service.Labels != nil && len(service.Labels) > 0 {
|
||||
return containerinstance.ContainerGroup{}, errors.New("ACI integration does not support labels in compose applications")
|
||||
}
|
||||
if service.Ports != nil {
|
||||
var containerPorts []containerinstance.ContainerPort
|
||||
for _, portConfig := range service.Ports {
|
||||
if portConfig.Published != 0 && portConfig.Published != portConfig.Target {
|
||||
msg := fmt.Sprintf("Port mapping is not supported with ACI, cannot map port %d to %d for container %s",
|
||||
portConfig.Published, portConfig.Target, service.Name)
|
||||
return groupDefinition, errors.New(msg)
|
||||
}
|
||||
portNumber := int32(portConfig.Target)
|
||||
containerPorts = append(containerPorts, containerinstance.ContainerPort{
|
||||
Port: to.Int32Ptr(portNumber),
|
||||
})
|
||||
groupPorts = append(groupPorts, containerinstance.Port{
|
||||
Port: to.Int32Ptr(portNumber),
|
||||
Protocol: containerinstance.TCP,
|
||||
})
|
||||
}
|
||||
containerDefinition.ContainerProperties.Ports = &containerPorts
|
||||
groupDefinition.ContainerGroupProperties.IPAddress = &containerinstance.IPAddress{
|
||||
Type: containerinstance.Public,
|
||||
Ports: &groupPorts,
|
||||
|
||||
containerPorts, serviceGroupPorts, serviceDomainName, err := convertPortsToAci(service)
|
||||
if err != nil {
|
||||
return groupDefinition, err
|
||||
}
|
||||
containerDefinition.ContainerProperties.Ports = &containerPorts
|
||||
groupPorts = append(groupPorts, serviceGroupPorts...)
|
||||
if serviceDomainName != nil {
|
||||
if dnsLabelName != nil && *serviceDomainName != *dnsLabelName {
|
||||
return containerinstance.ContainerGroup{}, fmt.Errorf("ACI integration does not support specifying different domain names on services in the same compose application")
|
||||
}
|
||||
dnsLabelName = serviceDomainName
|
||||
}
|
||||
|
||||
containers = append(containers, containerDefinition)
|
||||
}
|
||||
if len(groupPorts) > 0 {
|
||||
groupDefinition.ContainerGroupProperties.IPAddress = &containerinstance.IPAddress{
|
||||
Type: containerinstance.Public,
|
||||
Ports: &groupPorts,
|
||||
DNSNameLabel: dnsLabelName,
|
||||
}
|
||||
}
|
||||
if len(containers) > 1 {
|
||||
dnsSideCar := getDNSSidecar(containers)
|
||||
containers = append(containers, dnsSideCar)
|
||||
@ -137,6 +135,31 @@ func ToContainerGroup(ctx context.Context, aciContext store.AciContext, p types.
|
||||
return groupDefinition, nil
|
||||
}
|
||||
|
||||
func convertPortsToAci(service serviceConfigAciHelper) ([]containerinstance.ContainerPort, []containerinstance.Port, *string, error) {
|
||||
var groupPorts []containerinstance.Port
|
||||
var containerPorts []containerinstance.ContainerPort
|
||||
for _, portConfig := range service.Ports {
|
||||
if portConfig.Published != 0 && portConfig.Published != portConfig.Target {
|
||||
msg := fmt.Sprintf("Port mapping is not supported with ACI, cannot map port %d to %d for container %s",
|
||||
portConfig.Published, portConfig.Target, service.Name)
|
||||
return nil, nil, nil, errors.New(msg)
|
||||
}
|
||||
portNumber := int32(portConfig.Target)
|
||||
containerPorts = append(containerPorts, containerinstance.ContainerPort{
|
||||
Port: to.Int32Ptr(portNumber),
|
||||
})
|
||||
groupPorts = append(groupPorts, containerinstance.Port{
|
||||
Port: to.Int32Ptr(portNumber),
|
||||
Protocol: containerinstance.TCP,
|
||||
})
|
||||
}
|
||||
var dnsLabelName *string = nil
|
||||
if service.DomainName != "" {
|
||||
dnsLabelName = &service.DomainName
|
||||
}
|
||||
return containerPorts, groupPorts, dnsLabelName, nil
|
||||
}
|
||||
|
||||
func getDNSSidecar(containers []containerinstance.Container) containerinstance.Container {
|
||||
var commands []string
|
||||
for _, container := range containers {
|
||||
@ -249,7 +272,7 @@ func (p projectAciHelper) getRestartPolicy() (containerinstance.ContainerGroupRe
|
||||
restartPolicyCondition = toAciRestartPolicy(service.Deploy.RestartPolicy.Condition)
|
||||
}
|
||||
if alreadySpecified && restartPolicyCondition != toAciRestartPolicy(service.Deploy.RestartPolicy.Condition) {
|
||||
return "", errors.New("ACI integration does not support specifying different restart policies on containers in the same compose application")
|
||||
return "", errors.New("ACI integration does not support specifying different restart policies on services in the same compose application")
|
||||
}
|
||||
|
||||
}
|
||||
@ -390,7 +413,7 @@ func bytesToGb(b types.UnitBytes) float64 {
|
||||
}
|
||||
|
||||
// ContainerGroupToServiceStatus convert from an ACI container definition to service status
|
||||
func ContainerGroupToServiceStatus(containerID string, group containerinstance.ContainerGroup, container containerinstance.Container) compose.ServiceStatus {
|
||||
func ContainerGroupToServiceStatus(containerID string, group containerinstance.ContainerGroup, container containerinstance.Container, region string) compose.ServiceStatus {
|
||||
var replicas = 1
|
||||
if GetStatus(container, group) != StatusRunning {
|
||||
replicas = 0
|
||||
@ -398,14 +421,22 @@ func ContainerGroupToServiceStatus(containerID string, group containerinstance.C
|
||||
return compose.ServiceStatus{
|
||||
ID: containerID,
|
||||
Name: *container.Name,
|
||||
Ports: formatter.PortsToStrings(ToPorts(group.IPAddress, *container.Ports)),
|
||||
Ports: formatter.PortsToStrings(ToPorts(group.IPAddress, *container.Ports), fqdn(group, region)),
|
||||
Replicas: replicas,
|
||||
Desired: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func fqdn(group containerinstance.ContainerGroup, region string) string {
|
||||
fqdn := ""
|
||||
if group.IPAddress != nil && group.IPAddress.DNSNameLabel != nil && *group.IPAddress.DNSNameLabel != "" {
|
||||
fqdn = *group.IPAddress.DNSNameLabel + "." + region + ".azurecontainer.io"
|
||||
}
|
||||
return fqdn
|
||||
}
|
||||
|
||||
// ContainerGroupToContainer composes a Container from an ACI container definition
|
||||
func ContainerGroupToContainer(containerID string, cg containerinstance.ContainerGroup, cc containerinstance.Container) containers.Container {
|
||||
func ContainerGroupToContainer(containerID string, cg containerinstance.ContainerGroup, cc containerinstance.Container, region string) containers.Container {
|
||||
memLimits := 0.
|
||||
if cc.Resources != nil &&
|
||||
cc.Resources.Limits != nil &&
|
||||
@ -436,9 +467,9 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
|
||||
}
|
||||
}
|
||||
|
||||
var config *containers.RuntimeConfig = nil
|
||||
var config *containers.RuntimeConfig = &containers.RuntimeConfig{FQDN: fqdn(cg, region)}
|
||||
if envVars != nil {
|
||||
config = &containers.RuntimeConfig{Env: envVars}
|
||||
config.Env = envVars
|
||||
}
|
||||
c := containers.Container{
|
||||
ID: containerID,
|
||||
|
@ -59,7 +59,8 @@ func TestContainerGroupToContainer(t *testing.T) {
|
||||
Ports: &[]containerinstance.Port{{
|
||||
Port: to.Int32Ptr(80),
|
||||
}},
|
||||
IP: to.StringPtr("42.42.42.42"),
|
||||
IP: to.StringPtr("42.42.42.42"),
|
||||
DNSNameLabel: to.StringPtr("myapp"),
|
||||
},
|
||||
OsType: "Linux",
|
||||
},
|
||||
@ -102,10 +103,13 @@ func TestContainerGroupToContainer(t *testing.T) {
|
||||
Protocol: "tcp",
|
||||
HostIP: "42.42.42.42",
|
||||
}},
|
||||
Config: &containers.RuntimeConfig{
|
||||
FQDN: "myapp.eastus.azurecontainer.io",
|
||||
},
|
||||
RestartPolicyCondition: "any",
|
||||
}
|
||||
|
||||
container := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer)
|
||||
container := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer, "eastus")
|
||||
assert.DeepEqual(t, container, expectedContainer)
|
||||
}
|
||||
|
||||
@ -143,7 +147,7 @@ func TestContainerGroupToServiceStatus(t *testing.T) {
|
||||
Desired: 1,
|
||||
}
|
||||
|
||||
container := ContainerGroupToServiceStatus("myContainerID", myContainerGroup, myContainer)
|
||||
container := ContainerGroupToServiceStatus("myContainerID", myContainerGroup, myContainer, "eastus")
|
||||
assert.DeepEqual(t, container, expectedService)
|
||||
}
|
||||
|
||||
@ -366,7 +370,7 @@ func TestComposeInconsistentMultiContainerRestartPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
_, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
|
||||
assert.Error(t, err, "ACI integration does not support specifying different restart policies on containers in the same compose application")
|
||||
assert.Error(t, err, "ACI integration does not support specifying different restart policies on services in the same compose application")
|
||||
}
|
||||
|
||||
func TestLabelsErrorMessage(t *testing.T) {
|
||||
@ -448,6 +452,88 @@ func TestComposeContainerGroupToContainerMultiplePorts(t *testing.T) {
|
||||
assert.Assert(t, is.Len(groupPorts, 2))
|
||||
assert.Equal(t, *groupPorts[0].Port, int32(80))
|
||||
assert.Equal(t, *groupPorts[1].Port, int32(8080))
|
||||
assert.Assert(t, group.IPAddress.DNSNameLabel == nil)
|
||||
}
|
||||
|
||||
func TestComposeContainerGroupToContainerWithDomainName(t *testing.T) {
|
||||
project := types.Project{
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "service1",
|
||||
Image: "image1",
|
||||
Ports: []types.ServicePortConfig{
|
||||
{
|
||||
Published: 80,
|
||||
Target: 80,
|
||||
},
|
||||
},
|
||||
DomainName: "myApp",
|
||||
},
|
||||
{
|
||||
Name: "service2",
|
||||
Image: "image2",
|
||||
Ports: []types.ServicePortConfig{
|
||||
{
|
||||
Published: 8080,
|
||||
Target: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, is.Len(*group.Containers, 3))
|
||||
|
||||
groupPorts := *group.IPAddress.Ports
|
||||
assert.Assert(t, is.Len(groupPorts, 2))
|
||||
assert.Equal(t, *groupPorts[0].Port, int32(80))
|
||||
assert.Equal(t, *groupPorts[1].Port, int32(8080))
|
||||
assert.Equal(t, *group.IPAddress.DNSNameLabel, "myApp")
|
||||
}
|
||||
|
||||
func TestComposeContainerGroupToContainerErrorWhenSeveralDomainNames(t *testing.T) {
|
||||
project := types.Project{
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "service1",
|
||||
Image: "image1",
|
||||
DomainName: "myApp",
|
||||
},
|
||||
{
|
||||
Name: "service2",
|
||||
Image: "image2",
|
||||
DomainName: "myApp2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
|
||||
assert.Error(t, err, "ACI integration does not support specifying different domain names on services in the same compose application")
|
||||
}
|
||||
|
||||
// ACI fails if group definition IPAddress has no ports
|
||||
func TestComposeContainerGroupToContainerIgnoreDomainNameWithoutPorts(t *testing.T) {
|
||||
project := types.Project{
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "service1",
|
||||
Image: "image1",
|
||||
DomainName: "myApp",
|
||||
},
|
||||
{
|
||||
Name: "service2",
|
||||
Image: "image2",
|
||||
DomainName: "myApp",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, is.Len(*group.Containers, 3))
|
||||
assert.Assert(t, group.IPAddress == nil)
|
||||
}
|
||||
|
||||
func TestComposeContainerGroupToContainerResourceLimits(t *testing.T) {
|
||||
|
@ -57,6 +57,8 @@ type Container struct {
|
||||
// RuntimeConfig config of a created container
|
||||
type RuntimeConfig struct {
|
||||
Env map[string]string `json:",omitempty"`
|
||||
// FQDN is the fqdn to use
|
||||
FQDN string `json:"fqdn,omitempty"`
|
||||
}
|
||||
|
||||
// Port represents a published port of a container
|
||||
@ -91,6 +93,8 @@ type ContainerConfig struct {
|
||||
Environment []string
|
||||
// Restart policy condition
|
||||
RestartPolicyCondition string
|
||||
// DomainName Container NIS domain name
|
||||
DomainName string
|
||||
}
|
||||
|
||||
// ExecRequest contaiens configuration about an exec request
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
|
||||
type composeOptions struct {
|
||||
Name string
|
||||
DomainName string
|
||||
WorkingDir string
|
||||
ConfigPaths []string
|
||||
Environment []string
|
||||
@ -60,7 +61,7 @@ func (o *composeOptions) toProjectOptions() (*cli.ProjectOptions, error) {
|
||||
}
|
||||
|
||||
// Command returns the compose command with its child commands
|
||||
func Command() *cobra.Command {
|
||||
func Command(contextType string) *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Short: "Docker Compose",
|
||||
Use: "compose",
|
||||
@ -70,7 +71,7 @@ func Command() *cobra.Command {
|
||||
}
|
||||
|
||||
command.AddCommand(
|
||||
upCommand(),
|
||||
upCommand(contextType),
|
||||
downCommand(),
|
||||
psCommand(),
|
||||
listCommand(),
|
||||
|
@ -19,15 +19,16 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
"github.com/docker/compose-cli/context/store"
|
||||
"github.com/docker/compose-cli/progress"
|
||||
)
|
||||
|
||||
func upCommand() *cobra.Command {
|
||||
func upCommand(contextType string) *cobra.Command {
|
||||
opts := composeOptions{}
|
||||
upCmd := &cobra.Command{
|
||||
Use: "up",
|
||||
@ -40,6 +41,11 @@ func upCommand() *cobra.Command {
|
||||
upCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
|
||||
upCmd.Flags().StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables")
|
||||
upCmd.Flags().BoolP("detach", "d", true, " Detached mode: Run containers in the background")
|
||||
|
||||
if contextType == store.AciContextType {
|
||||
upCmd.Flags().StringVar(&opts.DomainName, "domainname", "", "Container NIS domain name")
|
||||
}
|
||||
|
||||
return upCmd
|
||||
}
|
||||
|
||||
@ -55,6 +61,10 @@ func runUp(ctx context.Context, opts composeOptions) error {
|
||||
return "", err
|
||||
}
|
||||
project, err := cli.ProjectFromOptions(options)
|
||||
if opts.DomainName != "" {
|
||||
//arbitrarily set the domain name on the first service ; ACI backend will expose the entire project
|
||||
project.Services[0].DomainName = opts.DomainName
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -23,13 +23,13 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/docker/compose-cli/utils/formatter"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
"github.com/docker/compose-cli/api/containers"
|
||||
formatter2 "github.com/docker/compose-cli/formatter"
|
||||
"github.com/docker/compose-cli/utils/formatter"
|
||||
)
|
||||
|
||||
type psOpts struct {
|
||||
@ -98,9 +98,17 @@ func runPs(ctx context.Context, opts psOpts) error {
|
||||
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
||||
fmt.Fprintf(w, "CONTAINER ID\tIMAGE\tCOMMAND\tSTATUS\tPORTS\n")
|
||||
format := "%s\t%s\t%s\t%s\t%s\n"
|
||||
for _, c := range containers {
|
||||
fmt.Fprintf(w, format, c.ID, c.Image, c.Command, c.Status, strings.Join(formatter.PortsToStrings(c.Ports), ", "))
|
||||
for _, container := range containers {
|
||||
fmt.Fprintf(w, format, container.ID, container.Image, container.Command, container.Status, strings.Join(formatter.PortsToStrings(container.Ports, fqdn(container)), ", "))
|
||||
}
|
||||
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func fqdn(container containers.Container) string {
|
||||
fqdn := ""
|
||||
if container.Config != nil {
|
||||
fqdn = container.Config.FQDN
|
||||
}
|
||||
return fqdn
|
||||
}
|
||||
|
@ -25,15 +25,15 @@ import (
|
||||
"github.com/containerd/console"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose-cli/api/containers"
|
||||
|
||||
"github.com/docker/compose-cli/api/client"
|
||||
"github.com/docker/compose-cli/api/containers"
|
||||
"github.com/docker/compose-cli/cli/options/run"
|
||||
"github.com/docker/compose-cli/context/store"
|
||||
"github.com/docker/compose-cli/progress"
|
||||
)
|
||||
|
||||
// Command runs a container
|
||||
func Command() *cobra.Command {
|
||||
func Command(contextType string) *cobra.Command {
|
||||
var opts run.Opts
|
||||
cmd := &cobra.Command{
|
||||
Use: "run",
|
||||
@ -54,6 +54,10 @@ func Command() *cobra.Command {
|
||||
cmd.Flags().StringArrayVarP(&opts.Environment, "env", "e", []string{}, "Set environment variables")
|
||||
cmd.Flags().StringVarP(&opts.RestartPolicyCondition, "restart", "", containers.RestartPolicyNone, "Restart policy to apply when a container exits")
|
||||
|
||||
if contextType == store.AciContextType {
|
||||
cmd.Flags().StringVar(&opts.DomainName, "domainname", "", "Container NIS domain name")
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -18,15 +18,25 @@ package run
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/golden"
|
||||
)
|
||||
|
||||
func TestHelp(t *testing.T) {
|
||||
var b bytes.Buffer
|
||||
c := Command()
|
||||
c := Command("aci")
|
||||
c.SetOutput(&b)
|
||||
_ = c.Help()
|
||||
golden.Assert(t, b.String(), "run-help.golden")
|
||||
}
|
||||
|
||||
func TestHelpNoDomainFlag(t *testing.T) {
|
||||
var b bytes.Buffer
|
||||
c := Command("default")
|
||||
c.SetOutput(&b)
|
||||
_ = c.Help()
|
||||
assert.Assert(t, !strings.Contains(b.String(), "domainname"))
|
||||
}
|
||||
|
1
cli/cmd/run/testdata/run-help.golden
vendored
1
cli/cmd/run/testdata/run-help.golden
vendored
@ -6,6 +6,7 @@ Usage:
|
||||
Flags:
|
||||
--cpus float Number of CPUs (default 1)
|
||||
-d, --detach Run container in background and print container ID
|
||||
--domainname string Container NIS domain name
|
||||
-e, --env stringArray Set environment variables
|
||||
-l, --label stringArray Set meta data on a container
|
||||
-m, --memory bytes Memory limit
|
||||
|
@ -115,7 +115,6 @@ func main() {
|
||||
contextcmd.Command(),
|
||||
cmd.PsCommand(),
|
||||
cmd.ServeCommand(),
|
||||
run.Command(),
|
||||
cmd.ExecCommand(),
|
||||
cmd.LogsCommand(),
|
||||
cmd.RmCommand(),
|
||||
@ -127,7 +126,6 @@ func main() {
|
||||
cmd.StopCommand(),
|
||||
cmd.KillCommand(),
|
||||
cmd.SecretCommand(),
|
||||
compose.Command(),
|
||||
|
||||
// Place holders
|
||||
cmd.EcsCommand(),
|
||||
@ -180,6 +178,11 @@ func main() {
|
||||
ctype = cc.Type()
|
||||
}
|
||||
|
||||
root.AddCommand(
|
||||
run.Command(ctype),
|
||||
compose.Command(ctype),
|
||||
)
|
||||
|
||||
if ctype == store.AciContextType {
|
||||
// we can also pass ctype as a parameter to the volume command and customize subcommands, flags, etc. when we have other backend implementations
|
||||
root.AddCommand(volume.ACICommand())
|
||||
|
@ -64,7 +64,7 @@ func TestCheckOwnCommand(t *testing.T) {
|
||||
assert.Assert(t, isContextAgnosticCommand(login.Command()))
|
||||
assert.Assert(t, isContextAgnosticCommand(context.Command()))
|
||||
assert.Assert(t, isContextAgnosticCommand(cmd.ServeCommand()))
|
||||
assert.Assert(t, !isContextAgnosticCommand(run.Command()))
|
||||
assert.Assert(t, !isContextAgnosticCommand(run.Command("default")))
|
||||
assert.Assert(t, !isContextAgnosticCommand(cmd.ExecCommand()))
|
||||
assert.Assert(t, !isContextAgnosticCommand(cmd.LogsCommand()))
|
||||
assert.Assert(t, !isContextAgnosticCommand(cmd.PsCommand()))
|
||||
|
@ -41,6 +41,7 @@ type Opts struct {
|
||||
Detach bool
|
||||
Environment []string
|
||||
RestartPolicyCondition string
|
||||
DomainName string
|
||||
}
|
||||
|
||||
// ToContainerConfig convert run options to a container configuration
|
||||
@ -74,6 +75,7 @@ func (r *Opts) ToContainerConfig(image string) (containers.ContainerConfig, erro
|
||||
CPULimit: r.Cpus,
|
||||
Environment: r.Environment,
|
||||
RestartPolicyCondition: restartPolicy,
|
||||
DomainName: r.DomainName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -335,18 +335,19 @@ func lines(output string) []string {
|
||||
|
||||
func TestContainerRunAttached(t *testing.T) {
|
||||
c := NewParallelE2eCLI(t, binDir)
|
||||
_, _ = setupTestResourceGroup(t, c)
|
||||
_, groupID := setupTestResourceGroup(t, c)
|
||||
|
||||
// Used in subtests
|
||||
var (
|
||||
container string
|
||||
endpoint string
|
||||
container string = "test-container"
|
||||
endpoint string
|
||||
followLogsProcess *icmd.Result
|
||||
)
|
||||
|
||||
container = "test-container"
|
||||
|
||||
var followLogsProcess *icmd.Result
|
||||
t.Run("run attached limits", func(t *testing.T) {
|
||||
dnsLabelName := "nginx-" + groupID
|
||||
fqdn := dnsLabelName + "." + location + ".azurecontainer.io"
|
||||
|
||||
cmd := c.NewDockerCmd(
|
||||
"run",
|
||||
"--name", container,
|
||||
@ -354,15 +355,17 @@ func TestContainerRunAttached(t *testing.T) {
|
||||
"--memory", "0.1G", "--cpus", "0.1",
|
||||
"-p", "80:80",
|
||||
"nginx",
|
||||
"--domainname",
|
||||
dnsLabelName,
|
||||
)
|
||||
followLogsProcess = icmd.StartCmd(cmd)
|
||||
|
||||
checkRunning := func(t poll.LogT) poll.Result {
|
||||
res := c.RunDockerOrExitError("inspect", container)
|
||||
if res.ExitCode == 0 {
|
||||
if res.ExitCode == 0 && strings.Contains(res.Stdout(), `"Status": "Running"`) {
|
||||
return poll.Success()
|
||||
}
|
||||
return poll.Continue("waiting for container to be running")
|
||||
return poll.Continue("waiting for container to be running, current inspect result: \n%s", res.Combined())
|
||||
}
|
||||
poll.WaitOn(t, checkRunning, poll.WithDelay(5*time.Second), poll.WithTimeout(60*time.Second))
|
||||
|
||||
@ -380,7 +383,8 @@ func TestContainerRunAttached(t *testing.T) {
|
||||
assert.Assert(t, len(port.HostIP) > 0)
|
||||
assert.Equal(t, port.ContainerPort, uint32(80))
|
||||
assert.Equal(t, port.HostPort, uint32(80))
|
||||
endpoint = fmt.Sprintf("http://%s:%d", port.HostIP, port.HostPort)
|
||||
assert.Equal(t, containerInspect.Config.FQDN, fqdn)
|
||||
endpoint = fmt.Sprintf("http://%s:%d", fqdn, port.HostPort)
|
||||
|
||||
assert.Assert(t, !strings.Contains(followLogsProcess.Stdout(), "/test"))
|
||||
checkRequest := func(t poll.LogT) poll.Result {
|
||||
@ -449,7 +453,7 @@ func TestContainerRunAttached(t *testing.T) {
|
||||
|
||||
func TestComposeUpUpdate(t *testing.T) {
|
||||
c := NewParallelE2eCLI(t, binDir)
|
||||
_, _ = setupTestResourceGroup(t, c)
|
||||
_, groupID := setupTestResourceGroup(t, c)
|
||||
|
||||
const (
|
||||
composeFile = "../composefiles/aci-demo/aci_demo_port.yaml"
|
||||
@ -461,8 +465,11 @@ func TestComposeUpUpdate(t *testing.T) {
|
||||
)
|
||||
|
||||
t.Run("compose up", func(t *testing.T) {
|
||||
dnsLabelName := "nginx-" + groupID
|
||||
fqdn := dnsLabelName + "." + location + ".azurecontainer.io"
|
||||
// Name of Compose project is taken from current folder "acie2e"
|
||||
c.RunDockerCmd("compose", "up", "-f", composeFile)
|
||||
c.RunDockerCmd("compose", "up", "-f", composeFile, "--domainname", dnsLabelName)
|
||||
|
||||
res := c.RunDockerCmd("ps")
|
||||
out := lines(res.Stdout())
|
||||
// Check three containers are running
|
||||
@ -489,6 +496,11 @@ func TestComposeUpUpdate(t *testing.T) {
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, strings.Contains(string(b), `"word":`))
|
||||
|
||||
endpoint = fmt.Sprintf("http://%s:%d", fqdn, containerInspect.Ports[0].HostPort)
|
||||
r, err = HTTPGetWithRetry(endpoint+"/words/noun", 3)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, r.StatusCode, http.StatusOK)
|
||||
})
|
||||
|
||||
t.Run("compose ps", func(t *testing.T) {
|
||||
|
@ -31,7 +31,7 @@ type portGroup struct {
|
||||
}
|
||||
|
||||
// PortsToStrings returns a human readable published ports
|
||||
func PortsToStrings(ports []containers.Port) []string {
|
||||
func PortsToStrings(ports []containers.Port, fqdn string) []string {
|
||||
groupMap := make(map[string]*portGroup)
|
||||
var (
|
||||
result []string
|
||||
@ -49,6 +49,9 @@ func PortsToStrings(ports []containers.Port) []string {
|
||||
if port.HostIP != "" {
|
||||
hostIP = port.HostIP
|
||||
}
|
||||
if fqdn != "" {
|
||||
hostIP = fqdn
|
||||
}
|
||||
|
||||
if port.HostPort != port.ContainerPort {
|
||||
hostMappings = append(hostMappings, fmt.Sprintf("%s:%d->%d/%s", hostIP, port.HostPort, port.ContainerPort, port.Protocol))
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
"github.com/docker/compose-cli/cli/options/run"
|
||||
)
|
||||
|
||||
func TestDisplayPorts(t *testing.T) {
|
||||
func TestDisplayPortsNoDomainname(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
in []string
|
||||
@ -70,8 +70,19 @@ func TestDisplayPorts(t *testing.T) {
|
||||
containerConfig, err := runOpts.ToContainerConfig("test")
|
||||
assert.NilError(t, err)
|
||||
|
||||
out := PortsToStrings(containerConfig.Ports)
|
||||
out := PortsToStrings(containerConfig.Ports, "")
|
||||
assert.DeepEqual(t, testCase.expected, out)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayPortsWithDomainname(t *testing.T) {
|
||||
runOpts := run.Opts{
|
||||
Publish: []string{"80"},
|
||||
}
|
||||
containerConfig, err := runOpts.ToContainerConfig("test")
|
||||
assert.NilError(t, err)
|
||||
|
||||
out := PortsToStrings(containerConfig.Ports, "mydomain.westus.azurecontainner.io")
|
||||
assert.DeepEqual(t, []string{"mydomain.westus.azurecontainner.io:80->80/tcp"}, out)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user