Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit d42a931

Browse files
authored
Merge pull request #631 from docker/aci_domainname
ACI: allow users to set DNSLabelName and deploy containers with fqdn like `myapp.eastus.azurecontainers.io`
2 parents 7abdb08 + cf3bb18 commit d42a931

File tree

19 files changed

+264
-65
lines changed

19 files changed

+264
-65
lines changed

aci/compose.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func (cs *aciComposeService) Ps(ctx context.Context, project string) ([]compose.
8989
if isContainerVisible(container, group, false) {
9090
continue
9191
}
92-
res = append(res, convert.ContainerGroupToServiceStatus(getContainerID(group, container), group, container))
92+
res = append(res, convert.ContainerGroupToServiceStatus(getContainerID(group, container), group, container, cs.ctx.Location))
9393
}
9494
return res, nil
9595
}

aci/containers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func (cs *aciContainerService) List(ctx context.Context, all bool) ([]containers
6363
if isContainerVisible(container, group, all) {
6464
continue
6565
}
66-
c := convert.ContainerGroupToContainer(getContainerID(group, container), group, container)
66+
c := convert.ContainerGroupToContainer(getContainerID(group, container), group, container, cs.ctx.Location)
6767
res = append(res, c)
6868
}
6969
}
@@ -257,5 +257,5 @@ func (cs *aciContainerService) Inspect(ctx context.Context, containerID string)
257257
return containers.Container{}, errdefs.ErrNotFound
258258
}
259259

260-
return convert.ContainerGroupToContainer(containerID, cg, cc), nil
260+
return convert.ContainerGroupToContainer(containerID, cg, cc, cs.ctx.Location), nil
261261
}

aci/convert/container.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func ContainerToComposeProject(r containers.ContainerConfig) (types.Project, err
4949
Ports: ports,
5050
Labels: r.Labels,
5151
Volumes: serviceConfigVolumes,
52+
DomainName: r.DomainName,
5253
Environment: toComposeEnvs(r.Environment),
5354
Deploy: &types.DeployConfig{
5455
Resources: types.Resources{

aci/convert/container_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ func TestConvertRestartPolicy(t *testing.T) {
5454
assert.Equal(t, service1.Deploy.RestartPolicy.Condition, "none")
5555
}
5656

57+
func TestConvertDomainName(t *testing.T) {
58+
container := containers.ContainerConfig{
59+
ID: "container1",
60+
DomainName: "myapp",
61+
}
62+
project, err := ContainerToComposeProject(container)
63+
assert.NilError(t, err)
64+
service1 := project.Services[0]
65+
assert.Equal(t, service1.Name, container.ID)
66+
assert.Equal(t, service1.DomainName, "myapp")
67+
}
68+
5769
func TestConvertEnvVariables(t *testing.T) {
5870
container := containers.ContainerConfig{
5971
ID: "container1",

aci/convert/convert.go

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ const (
4343
StatusRunning = "Running"
4444
// ComposeDNSSidecarName name of the dns sidecar container
4545
ComposeDNSSidecarName = "aci--dns--sidecar"
46-
dnsSidecarImage = "busybox:1.31.1"
4746

47+
dnsSidecarImage = "busybox:1.31.1"
4848
azureFileDriverName = "azure_file"
4949
volumeDriveroptsShareNameKey = "share_name"
5050
volumeDriveroptsAccountNameKey = "storage_account_name"
@@ -93,6 +93,7 @@ func ToContainerGroup(ctx context.Context, aciContext store.AciContext, p types.
9393
}
9494

9595
var groupPorts []containerinstance.Port
96+
var dnsLabelName *string
9697
for _, s := range project.Services {
9798
service := serviceConfigAciHelper(s)
9899
containerDefinition, err := service.getAciContainer(volumesCache)
@@ -102,32 +103,29 @@ func ToContainerGroup(ctx context.Context, aciContext store.AciContext, p types.
102103
if service.Labels != nil && len(service.Labels) > 0 {
103104
return containerinstance.ContainerGroup{}, errors.New("ACI integration does not support labels in compose applications")
104105
}
105-
if service.Ports != nil {
106-
var containerPorts []containerinstance.ContainerPort
107-
for _, portConfig := range service.Ports {
108-
if portConfig.Published != 0 && portConfig.Published != portConfig.Target {
109-
msg := fmt.Sprintf("Port mapping is not supported with ACI, cannot map port %d to %d for container %s",
110-
portConfig.Published, portConfig.Target, service.Name)
111-
return groupDefinition, errors.New(msg)
112-
}
113-
portNumber := int32(portConfig.Target)
114-
containerPorts = append(containerPorts, containerinstance.ContainerPort{
115-
Port: to.Int32Ptr(portNumber),
116-
})
117-
groupPorts = append(groupPorts, containerinstance.Port{
118-
Port: to.Int32Ptr(portNumber),
119-
Protocol: containerinstance.TCP,
120-
})
121-
}
122-
containerDefinition.ContainerProperties.Ports = &containerPorts
123-
groupDefinition.ContainerGroupProperties.IPAddress = &containerinstance.IPAddress{
124-
Type: containerinstance.Public,
125-
Ports: &groupPorts,
106+
107+
containerPorts, serviceGroupPorts, serviceDomainName, err := convertPortsToAci(service)
108+
if err != nil {
109+
return groupDefinition, err
110+
}
111+
containerDefinition.ContainerProperties.Ports = &containerPorts
112+
groupPorts = append(groupPorts, serviceGroupPorts...)
113+
if serviceDomainName != nil {
114+
if dnsLabelName != nil && *serviceDomainName != *dnsLabelName {
115+
return containerinstance.ContainerGroup{}, fmt.Errorf("ACI integration does not support specifying different domain names on services in the same compose application")
126116
}
117+
dnsLabelName = serviceDomainName
127118
}
128119

129120
containers = append(containers, containerDefinition)
130121
}
122+
if len(groupPorts) > 0 {
123+
groupDefinition.ContainerGroupProperties.IPAddress = &containerinstance.IPAddress{
124+
Type: containerinstance.Public,
125+
Ports: &groupPorts,
126+
DNSNameLabel: dnsLabelName,
127+
}
128+
}
131129
if len(containers) > 1 {
132130
dnsSideCar := getDNSSidecar(containers)
133131
containers = append(containers, dnsSideCar)
@@ -137,6 +135,31 @@ func ToContainerGroup(ctx context.Context, aciContext store.AciContext, p types.
137135
return groupDefinition, nil
138136
}
139137

138+
func convertPortsToAci(service serviceConfigAciHelper) ([]containerinstance.ContainerPort, []containerinstance.Port, *string, error) {
139+
var groupPorts []containerinstance.Port
140+
var containerPorts []containerinstance.ContainerPort
141+
for _, portConfig := range service.Ports {
142+
if portConfig.Published != 0 && portConfig.Published != portConfig.Target {
143+
msg := fmt.Sprintf("Port mapping is not supported with ACI, cannot map port %d to %d for container %s",
144+
portConfig.Published, portConfig.Target, service.Name)
145+
return nil, nil, nil, errors.New(msg)
146+
}
147+
portNumber := int32(portConfig.Target)
148+
containerPorts = append(containerPorts, containerinstance.ContainerPort{
149+
Port: to.Int32Ptr(portNumber),
150+
})
151+
groupPorts = append(groupPorts, containerinstance.Port{
152+
Port: to.Int32Ptr(portNumber),
153+
Protocol: containerinstance.TCP,
154+
})
155+
}
156+
var dnsLabelName *string = nil
157+
if service.DomainName != "" {
158+
dnsLabelName = &service.DomainName
159+
}
160+
return containerPorts, groupPorts, dnsLabelName, nil
161+
}
162+
140163
func getDNSSidecar(containers []containerinstance.Container) containerinstance.Container {
141164
var commands []string
142165
for _, container := range containers {
@@ -249,7 +272,7 @@ func (p projectAciHelper) getRestartPolicy() (containerinstance.ContainerGroupRe
249272
restartPolicyCondition = toAciRestartPolicy(service.Deploy.RestartPolicy.Condition)
250273
}
251274
if alreadySpecified && restartPolicyCondition != toAciRestartPolicy(service.Deploy.RestartPolicy.Condition) {
252-
return "", errors.New("ACI integration does not support specifying different restart policies on containers in the same compose application")
275+
return "", errors.New("ACI integration does not support specifying different restart policies on services in the same compose application")
253276
}
254277

255278
}
@@ -390,22 +413,30 @@ func bytesToGb(b types.UnitBytes) float64 {
390413
}
391414

392415
// ContainerGroupToServiceStatus convert from an ACI container definition to service status
393-
func ContainerGroupToServiceStatus(containerID string, group containerinstance.ContainerGroup, container containerinstance.Container) compose.ServiceStatus {
416+
func ContainerGroupToServiceStatus(containerID string, group containerinstance.ContainerGroup, container containerinstance.Container, region string) compose.ServiceStatus {
394417
var replicas = 1
395418
if GetStatus(container, group) != StatusRunning {
396419
replicas = 0
397420
}
398421
return compose.ServiceStatus{
399422
ID: containerID,
400423
Name: *container.Name,
401-
Ports: formatter.PortsToStrings(ToPorts(group.IPAddress, *container.Ports)),
424+
Ports: formatter.PortsToStrings(ToPorts(group.IPAddress, *container.Ports), fqdn(group, region)),
402425
Replicas: replicas,
403426
Desired: 1,
404427
}
405428
}
406429

430+
func fqdn(group containerinstance.ContainerGroup, region string) string {
431+
fqdn := ""
432+
if group.IPAddress != nil && group.IPAddress.DNSNameLabel != nil && *group.IPAddress.DNSNameLabel != "" {
433+
fqdn = *group.IPAddress.DNSNameLabel + "." + region + ".azurecontainer.io"
434+
}
435+
return fqdn
436+
}
437+
407438
// ContainerGroupToContainer composes a Container from an ACI container definition
408-
func ContainerGroupToContainer(containerID string, cg containerinstance.ContainerGroup, cc containerinstance.Container) containers.Container {
439+
func ContainerGroupToContainer(containerID string, cg containerinstance.ContainerGroup, cc containerinstance.Container, region string) containers.Container {
409440
memLimits := 0.
410441
if cc.Resources != nil &&
411442
cc.Resources.Limits != nil &&
@@ -436,9 +467,9 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
436467
}
437468
}
438469

439-
var config *containers.RuntimeConfig = nil
470+
var config *containers.RuntimeConfig = &containers.RuntimeConfig{FQDN: fqdn(cg, region)}
440471
if envVars != nil {
441-
config = &containers.RuntimeConfig{Env: envVars}
472+
config.Env = envVars
442473
}
443474
c := containers.Container{
444475
ID: containerID,

aci/convert/convert_test.go

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ func TestContainerGroupToContainer(t *testing.T) {
5959
Ports: &[]containerinstance.Port{{
6060
Port: to.Int32Ptr(80),
6161
}},
62-
IP: to.StringPtr("42.42.42.42"),
62+
IP: to.StringPtr("42.42.42.42"),
63+
DNSNameLabel: to.StringPtr("myapp"),
6364
},
6465
OsType: "Linux",
6566
},
@@ -102,10 +103,13 @@ func TestContainerGroupToContainer(t *testing.T) {
102103
Protocol: "tcp",
103104
HostIP: "42.42.42.42",
104105
}},
106+
Config: &containers.RuntimeConfig{
107+
FQDN: "myapp.eastus.azurecontainer.io",
108+
},
105109
RestartPolicyCondition: "any",
106110
}
107111

108-
container := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer)
112+
container := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer, "eastus")
109113
assert.DeepEqual(t, container, expectedContainer)
110114
}
111115

@@ -143,7 +147,7 @@ func TestContainerGroupToServiceStatus(t *testing.T) {
143147
Desired: 1,
144148
}
145149

146-
container := ContainerGroupToServiceStatus("myContainerID", myContainerGroup, myContainer)
150+
container := ContainerGroupToServiceStatus("myContainerID", myContainerGroup, myContainer, "eastus")
147151
assert.DeepEqual(t, container, expectedService)
148152
}
149153

@@ -366,7 +370,7 @@ func TestComposeInconsistentMultiContainerRestartPolicy(t *testing.T) {
366370
}
367371

368372
_, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
369-
assert.Error(t, err, "ACI integration does not support specifying different restart policies on containers in the same compose application")
373+
assert.Error(t, err, "ACI integration does not support specifying different restart policies on services in the same compose application")
370374
}
371375

372376
func TestLabelsErrorMessage(t *testing.T) {
@@ -448,6 +452,88 @@ func TestComposeContainerGroupToContainerMultiplePorts(t *testing.T) {
448452
assert.Assert(t, is.Len(groupPorts, 2))
449453
assert.Equal(t, *groupPorts[0].Port, int32(80))
450454
assert.Equal(t, *groupPorts[1].Port, int32(8080))
455+
assert.Assert(t, group.IPAddress.DNSNameLabel == nil)
456+
}
457+
458+
func TestComposeContainerGroupToContainerWithDomainName(t *testing.T) {
459+
project := types.Project{
460+
Services: []types.ServiceConfig{
461+
{
462+
Name: "service1",
463+
Image: "image1",
464+
Ports: []types.ServicePortConfig{
465+
{
466+
Published: 80,
467+
Target: 80,
468+
},
469+
},
470+
DomainName: "myApp",
471+
},
472+
{
473+
Name: "service2",
474+
Image: "image2",
475+
Ports: []types.ServicePortConfig{
476+
{
477+
Published: 8080,
478+
Target: 8080,
479+
},
480+
},
481+
},
482+
},
483+
}
484+
485+
group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
486+
assert.NilError(t, err)
487+
assert.Assert(t, is.Len(*group.Containers, 3))
488+
489+
groupPorts := *group.IPAddress.Ports
490+
assert.Assert(t, is.Len(groupPorts, 2))
491+
assert.Equal(t, *groupPorts[0].Port, int32(80))
492+
assert.Equal(t, *groupPorts[1].Port, int32(8080))
493+
assert.Equal(t, *group.IPAddress.DNSNameLabel, "myApp")
494+
}
495+
496+
func TestComposeContainerGroupToContainerErrorWhenSeveralDomainNames(t *testing.T) {
497+
project := types.Project{
498+
Services: []types.ServiceConfig{
499+
{
500+
Name: "service1",
501+
Image: "image1",
502+
DomainName: "myApp",
503+
},
504+
{
505+
Name: "service2",
506+
Image: "image2",
507+
DomainName: "myApp2",
508+
},
509+
},
510+
}
511+
512+
_, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
513+
assert.Error(t, err, "ACI integration does not support specifying different domain names on services in the same compose application")
514+
}
515+
516+
// ACI fails if group definition IPAddress has no ports
517+
func TestComposeContainerGroupToContainerIgnoreDomainNameWithoutPorts(t *testing.T) {
518+
project := types.Project{
519+
Services: []types.ServiceConfig{
520+
{
521+
Name: "service1",
522+
Image: "image1",
523+
DomainName: "myApp",
524+
},
525+
{
526+
Name: "service2",
527+
Image: "image2",
528+
DomainName: "myApp",
529+
},
530+
},
531+
}
532+
533+
group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
534+
assert.NilError(t, err)
535+
assert.Assert(t, is.Len(*group.Containers, 3))
536+
assert.Assert(t, group.IPAddress == nil)
451537
}
452538

453539
func TestComposeContainerGroupToContainerResourceLimits(t *testing.T) {

api/containers/api.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ type Container struct {
5757
// RuntimeConfig config of a created container
5858
type RuntimeConfig struct {
5959
Env map[string]string `json:",omitempty"`
60+
// FQDN is the fqdn to use
61+
FQDN string `json:"fqdn,omitempty"`
6062
}
6163

6264
// Port represents a published port of a container
@@ -91,6 +93,8 @@ type ContainerConfig struct {
9193
Environment []string
9294
// Restart policy condition
9395
RestartPolicyCondition string
96+
// DomainName Container NIS domain name
97+
DomainName string
9498
}
9599

96100
// ExecRequest contaiens configuration about an exec request

cli/cmd/compose/compose.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
type composeOptions struct {
3131
Name string
32+
DomainName string
3233
WorkingDir string
3334
ConfigPaths []string
3435
Environment []string
@@ -60,7 +61,7 @@ func (o *composeOptions) toProjectOptions() (*cli.ProjectOptions, error) {
6061
}
6162

6263
// Command returns the compose command with its child commands
63-
func Command() *cobra.Command {
64+
func Command(contextType string) *cobra.Command {
6465
command := &cobra.Command{
6566
Short: "Docker Compose",
6667
Use: "compose",
@@ -70,7 +71,7 @@ func Command() *cobra.Command {
7071
}
7172

7273
command.AddCommand(
73-
upCommand(),
74+
upCommand(contextType),
7475
downCommand(),
7576
psCommand(),
7677
listCommand(),

0 commit comments

Comments
 (0)