@@ -16,11 +16,12 @@ package servicediscovery
16
16
import (
17
17
"fmt"
18
18
19
+ "github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/context"
19
20
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/cloudformation"
20
21
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/commands/flags"
21
22
"github.com/aws/amazon-ecs-cli/ecs-cli/modules/config"
23
+ utils "github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils/compose"
22
24
"github.com/aws/aws-sdk-go/aws"
23
- "github.com/aws/aws-sdk-go/service/ecs"
24
25
"github.com/sirupsen/logrus"
25
26
"github.com/urfave/cli"
26
27
)
@@ -30,138 +31,160 @@ const (
30
31
serviceDiscoveryServiceStackNameFormat = "amazon-ecs-cli-setup-%s-%s-service-discovery-service"
31
32
)
32
33
33
- // CloudFormation template parameters
34
34
const (
35
- parameterKeyNamespaceDescription = "NamespaceDescription"
36
- parameterKeyVPCID = "VPCID"
37
- parameterKeyNamespaceName = "NamespaceName"
35
+ cfnTemplateOutputPrivateNamespaceID = "PrivateDNSNamespaceID"
36
+ cfnTemplateOutputSDSARN = "ServiceDiscoveryServiceARN"
38
37
)
39
38
40
- const (
41
- parameterKeySDSDescription = "SDSDescription"
42
- parameterKeySDSName = "SDSName"
43
- parameterKeyNamespaceID = "NamespaceID"
44
- parameterKeyDNSType = "DNSType"
45
- parameterKeyDNSTTL = "DNSTTL"
46
- parameterKeyHealthCheckCustomConfigFailureThreshold = "FailureThreshold"
47
- )
39
+ // Create creates a DNS namespace (or uses an existing one) and creates a Service Discovery Service
40
+ // The Service Discovery ARN is returned so that it can be used to enable ECS Service Discovery
41
+ func Create (networkMode , serviceName string , c * context.ECSContext ) (* string , error ) {
42
+ cfnClient := cloudformation .NewCloudformationClient (c .CommandConfig )
48
43
49
- const (
50
- CFNTemplateOutputPrivateNamespaceID = "PrivateDNSNamespaceID"
51
- CFNTemplateOutputSDSARN = "ServiceDiscoveryServiceARN"
52
- )
53
-
54
- const (
55
- dnsRecordTypeA = "A"
56
- dnsRecordTypeSRV = "SRV"
57
- )
44
+ var ecsParamsSD * utils.ServiceDiscovery
45
+ if c .ECSParams != nil {
46
+ ecsParamsSD = & c .ECSParams .RunParams .ServiceDiscovery
47
+ } else {
48
+ ecsParamsSD = & utils.ServiceDiscovery {}
49
+ }
58
50
59
- var requiredParamsSDS = [] string { parameterKeyNamespaceID , parameterKeySDSName , parameterKeyDNSType }
60
- var requiredParamsNamespace = [] string { parameterKeyVPCID , parameterKeyNamespaceName }
51
+ return create ( c . CLIContext , networkMode , serviceName , cfnClient , ecsParamsSD , c . CommandConfig )
52
+ }
61
53
62
- // Create creates resources for service discovery and returns the ID of the Service Discovery Service
63
- func Create (c * cli.Context , networkMode , serviceName , clusterName string ) (* string , error ) {
64
- rdwr , err := config .NewReadWriter ()
54
+ func create (c * cli.Context , networkMode , serviceName string , cfnClient cloudformation.CloudformationClient , ecsParamsSD * utils.ServiceDiscovery , config * config.CommandConfig ) (* string , error ) {
55
+ err := validateNameAndIdExclusive (c , ecsParamsSD )
65
56
if err != nil {
66
57
return nil , err
67
58
}
68
-
69
- commandConfig , err := config .NewCommandConfig (c , rdwr )
59
+ input , err := mergeSDFlagsAndInput (c , ecsParamsSD )
70
60
if err != nil {
71
61
return nil , err
72
62
}
73
-
74
- cfnClient := cloudformation .NewCloudformationClient (commandConfig )
75
-
76
- return create (c , networkMode , serviceName , clusterName , cfnClient )
77
- }
78
-
79
- func create (c * cli.Context , networkMode , serviceName , clusterName string , cfnClient cloudformation.CloudformationClient ) (* string , error ) {
80
- // create namespace
81
- namespaceParams := namespaceCFNParams (c )
82
- if err := namespaceParams .Validate (); err != nil {
83
- return nil , err
84
- }
85
-
86
- namespaceStackName := cfnStackName (privateDNSNamespaceStackNameFormat , clusterName , serviceName )
87
- if _ , err := cfnClient .CreateStack (cloudformation .GetPrivateNamespaceTemplate (), namespaceStackName , false , namespaceParams ); err != nil {
63
+ err = validateMergedSDInputFields (input , networkMode )
64
+ if err != nil {
88
65
return nil , err
89
66
}
67
+ namespaceWarningsWhenIDSpecified (input )
90
68
91
- logrus .Info ("Waiting for the private DNS namespace to be created..." )
92
- // Wait for stack creation
93
- cfnClient .WaitUntilCreateComplete (namespaceStackName )
94
-
95
- // Get the ID of the namespace we just created
96
- namespaceID , err := getOutputIDFromStack (cfnClient , namespaceStackName , CFNTemplateOutputPrivateNamespaceID )
69
+ namespaceID , err := getOrCreateNamespace (c , networkMode , serviceName , cfnClient , input , config )
97
70
if err != nil {
98
71
return nil , err
99
72
}
100
73
101
74
// create SDS
102
- sdsParams := sdsCFNParams (aws .StringValue (namespaceID ), serviceName , networkMode )
75
+ sdsParams := getSDSCFNParams (aws .StringValue (namespaceID ), serviceName , networkMode , input )
103
76
if err := sdsParams .Validate (); err != nil {
104
77
return nil , err
105
78
}
106
79
107
- sdsStackName := cfnStackName (serviceDiscoveryServiceStackNameFormat , clusterName , serviceName )
80
+ sdsStackName := cfnStackName (serviceDiscoveryServiceStackNameFormat , config . Cluster , serviceName )
108
81
if _ , err := cfnClient .CreateStack (cloudformation .GetSDSTemplate (), sdsStackName , false , sdsParams ); err != nil {
109
82
return nil , err
110
83
}
111
84
112
85
logrus .Info ("Waiting for the Service Discovery Service to be created..." )
113
- // Wait for stack creation
114
86
cfnClient .WaitUntilCreateComplete (sdsStackName )
115
87
116
88
// Return the ID of the SDS we just created
117
- return getOutputIDFromStack (cfnClient , sdsStackName , CFNTemplateOutputSDSARN )
89
+ return getOutputIDFromStack (cfnClient , sdsStackName , cfnTemplateOutputSDSARN )
118
90
}
119
91
120
- func getOutputIDFromStack ( cfnClient cloudformation. CloudformationClient , stackName , outputKey string ) (* string , error ) {
121
- response , err := cfnClient . DescribeStacks ( stackName )
122
- if err != nil {
92
+ func createNamespace ( c * cli. Context , networkMode , serviceName , clusterName string , cfnClient cloudformation. CloudformationClient , input * utils. ServiceDiscovery ) (* string , error ) {
93
+ namespaceParams := getNamespaceCFNParams ( input )
94
+ if err := namespaceParams . Validate (); err != nil {
123
95
return nil , err
124
96
}
125
- if len (response .Stacks ) == 0 {
126
- return nil , fmt .Errorf ("Could not find CloudFormation stack: %s" , stackName )
127
- }
128
97
129
- for _ , output := range response .Stacks [0 ].Outputs {
130
- if aws .StringValue (output .OutputKey ) == outputKey {
131
- return output .OutputValue , nil
132
- }
98
+ namespaceStackName := cfnStackName (privateDNSNamespaceStackNameFormat , clusterName , serviceName )
99
+ if _ , err := cfnClient .CreateStack (cloudformation .GetPrivateNamespaceTemplate (), namespaceStackName , false , namespaceParams ); err != nil {
100
+ return nil , err
133
101
}
134
- return nil , fmt .Errorf ("Failed to find output %s in stack %s" , outputKey , stackName )
135
102
136
- }
103
+ logrus .Info ("Waiting for the private DNS namespace to be created..." )
104
+ cfnClient .WaitUntilCreateComplete (namespaceStackName )
137
105
138
- func cfnStackName ( stackName , cluster , service string ) string {
139
- return fmt . Sprintf ( stackName , cluster , service )
106
+ // Get the ID of the namespace we just created
107
+ return getOutputIDFromStack ( cfnClient , namespaceStackName , cfnTemplateOutputPrivateNamespaceID )
140
108
}
141
109
142
- func sdsCFNParams (namespaceID , sdsName , networkMode string ) * cloudformation.CfnStackParams {
143
- cfnParams := cloudformation .NewCfnStackParams (requiredParamsSDS )
144
-
145
- cfnParams .Add (parameterKeyNamespaceID , namespaceID )
146
- cfnParams .Add (parameterKeySDSName , sdsName )
147
-
148
- dnsType := dnsRecordTypeSRV
149
- if networkMode == ecs .NetworkModeAwsvpc {
150
- dnsType = dnsRecordTypeA
110
+ func getOrCreateNamespace (c * cli.Context , networkMode , serviceName string , cfnClient cloudformation.CloudformationClient , input * utils.ServiceDiscovery , config * config.CommandConfig ) (* string , error ) {
111
+ namespace , err := getExistingNamespace (input , config )
112
+ if err != nil {
113
+ return nil , err
151
114
}
152
- cfnParams .Add (parameterKeyDNSType , dnsType )
115
+ if namespace == nil {
116
+ namespace , err = createNamespace (c , networkMode , serviceName , config .Cluster , cfnClient , input )
117
+ } else {
118
+ logrus .Infof ("Using existing namespace %s" , * namespace )
119
+ }
120
+ return namespace , err
121
+ }
153
122
154
- return cfnParams
123
+ func getExistingNamespace (input * utils.ServiceDiscovery , config * config.CommandConfig ) (* string , error ) {
124
+ switch {
125
+ case input .PrivateDNSNamespace .ID != "" :
126
+ return aws .String (input .PrivateDNSNamespace .ID ), nil
127
+ case input .PublicDNSNamespace .ID != "" :
128
+ return aws .String (input .PublicDNSNamespace .ID ), nil
129
+ case input .PrivateDNSNamespace .Name != "" :
130
+ return findPrivateNamespace (input .PrivateDNSNamespace .Name , input .PrivateDNSNamespace .VPC , config )
131
+ case input .PublicDNSNamespace .Name != "" :
132
+ return getPublicNamespaceSpecifiedByName (input .PublicDNSNamespace .Name , config )
133
+ default :
134
+ return nil , nil
135
+ }
155
136
}
156
137
157
- func namespaceCFNParams (context * cli.Context ) * cloudformation.CfnStackParams {
158
- cfnParams := cloudformation .NewCfnStackParams (requiredParamsNamespace )
138
+ func getPublicNamespaceSpecifiedByName (name string , config * config.CommandConfig ) (* string , error ) {
139
+ namespace , err := findPublicNamespace (name , config )
140
+ if err != nil {
141
+ return nil , err
142
+ }
143
+ if namespace == nil {
144
+ // we do not create public namespaces, so failing to find it is in an error case
145
+ return nil , fmt .Errorf ("Failed to find public namespace %s" , name )
146
+ }
147
+ return namespace , err
148
+ }
159
149
160
- namespaceName := context .String (flags .PrivateDNSNamespaceNameFlag )
161
- cfnParams .Add (parameterKeyNamespaceName , namespaceName )
150
+ // Flags override the values specified in ECS Params
151
+ // Merges fields for Namespace and SDS
152
+ // This function just merges fields; it doesn't validate them
153
+ func mergeSDFlagsAndInput (c * cli.Context , ecsParamsSD * utils.ServiceDiscovery ) (* utils.ServiceDiscovery , error ) {
154
+ // Private DNS Namespace fields
155
+ privNamespace := ecsParamsSD .PrivateDNSNamespace
156
+ privNamespace .Namespace = resolveNamespaceOverride (c .String (flags .PrivateDNSNamespaceNameFlag ), c .String (flags .PrivateDNSNamespaceIDFlag ), "private" , privNamespace .Namespace )
157
+ privNamespace .VPC = resolveStringFieldOverride (c , flags .VpcIdFlag , privNamespace .VPC , "private_dns_namespace.vpc" )
158
+ ecsParamsSD .PrivateDNSNamespace = privNamespace
159
+
160
+ // Public DNS Namespace fields
161
+ pubNamespace := ecsParamsSD .PublicDNSNamespace
162
+ pubNamespace .Namespace = resolveNamespaceOverride (c .String (flags .PublicDNSNamespaceNameFlag ), c .String (flags .PublicDNSNamespaceIDFlag ), "public" , pubNamespace .Namespace )
163
+ ecsParamsSD .PublicDNSNamespace = pubNamespace
164
+
165
+ // SDS fields
166
+ sds := ecsParamsSD .ServiceDiscoveryService
167
+ sds .DNSConfig .Type = resolveStringFieldOverride (c , flags .DNSTypeFlag , sds .DNSConfig .Type , "dns_config.type" )
168
+ ttl , err := resolveIntPointerFieldOverride (c , flags .DNSTTLFlag , sds .DNSConfig .TTL , "dns_config.ttl" )
169
+ if err != nil {
170
+ return nil , err
171
+ }
172
+ sds .DNSConfig .TTL = ttl
173
+ threshold , err := resolveIntPointerFieldOverride (c , flags .HealthcheckCustomConfigFailureThresholdFlag , sds .HealthCheckCustomConfig .FailureThreshold , "failure_threshold" )
174
+ if err != nil {
175
+ return nil , err
176
+ }
177
+ sds .HealthCheckCustomConfig .FailureThreshold = threshold
178
+ ecsParamsSD .ServiceDiscoveryService = sds
162
179
163
- vpcID := context .String (flags .VpcIdFlag )
164
- cfnParams .Add (parameterKeyVPCID , vpcID )
180
+ // top level fields
181
+ // these container fields aren't used when creating Route53 resources in this package, but we aggregate them here so that we can do error checking- SRV records require these.
182
+ ecsParamsSD .ContainerName = resolveStringFieldOverride (c , flags .ServiceDiscoveryContainerNameFlag , ecsParamsSD .ContainerName , "container_name" )
183
+ port , err := resolveIntPointerFieldOverride (c , flags .ServiceDiscoveryContainerPortFlag , ecsParamsSD .ContainerPort , "container_port" )
184
+ if err != nil {
185
+ return nil , err
186
+ }
187
+ ecsParamsSD .ContainerPort = port
165
188
166
- return cfnParams
189
+ return ecsParamsSD , nil
167
190
}
0 commit comments