From 19203132f469195e1216812514ad32f6db179b3c Mon Sep 17 00:00:00 2001 From: John Mortlock Date: Tue, 16 Aug 2022 19:32:19 +0930 Subject: [PATCH] feat(servicediscovery): add support for API only services within a DNS namespace (#21494) ---- Closes: #21490 DNS namespaces are currently setup in a way which only allow for discoverability via DNS or API, this means it is not currently possible to register non-ip instances within a DNS based namespace. After this change you can create a DNS based service with an optional ```discoveryType``` If this ```discoveryType``` is set to ```DiscoveryType.API``` then you can register non IP based instances to this service using the ```registerNonIpInstance``` function If no DiscoveryType is passed than the default from the namespace is used, so for an HTTP namespace this is ```API``` and for DNS derived namespaces this is ```DNS_AND_API``` This means DNS type namespaces can have services registered with a combination of discovery types. ---- ### All Submissions: * [✓] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [✗] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [✓] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [✓] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-servicediscovery/README.md | 5 +- .../lib/non-ip-instance.ts | 7 +- .../aws-servicediscovery/lib/private/utils.ts | 6 ++ .../aws-servicediscovery/lib/service.ts | 68 ++++++++++++++++--- .../aws-servicediscovery/package.json | 2 + .../test/instance.test.ts | 35 +++++++++- ....service-with-private-dns-namespace.lit.ts | 8 +++ .../aws-servicediscovery-integ.assets.json | 19 ++++++ .../aws-servicediscovery-integ.template.json | 27 ++++++++ .../cdk.out | 2 +- .../manifest.json | 14 +++- .../tree.json | 67 +++++++++++++++++- .../aws-servicediscovery/test/service.test.ts | 55 +++++++++++++++ 13 files changed, 297 insertions(+), 18 deletions(-) create mode 100644 packages/@aws-cdk/aws-servicediscovery/lib/private/utils.ts create mode 100644 packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/aws-servicediscovery-integ.assets.json diff --git a/packages/@aws-cdk/aws-servicediscovery/README.md b/packages/@aws-cdk/aws-servicediscovery/README.md index 888d3af744a37..6b20e6a6fb115 100644 --- a/packages/@aws-cdk/aws-servicediscovery/README.md +++ b/packages/@aws-cdk/aws-servicediscovery/README.md @@ -35,7 +35,10 @@ registers an instance to it: The following example creates an AWS Cloud Map namespace that supports both API calls and DNS queries within a vpc, creates a service in that namespace, and registers a loadbalancer as an -instance: +instance. + +A secondary service is also configured which only supports API based discovery, a +non ip based resource is registered to this service: [Creating a Cloud Map service within a Private DNS namespace](test/integ.service-with-private-dns-namespace.lit.ts) diff --git a/packages/@aws-cdk/aws-servicediscovery/lib/non-ip-instance.ts b/packages/@aws-cdk/aws-servicediscovery/lib/non-ip-instance.ts index 7c51770155893..d75c5d011444b 100644 --- a/packages/@aws-cdk/aws-servicediscovery/lib/non-ip-instance.ts +++ b/packages/@aws-cdk/aws-servicediscovery/lib/non-ip-instance.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { BaseInstanceProps, InstanceBase } from './instance'; -import { NamespaceType } from './namespace'; -import { IService } from './service'; +import { defaultDiscoveryType } from './private/utils'; +import { IService, DiscoveryType } from './service'; import { CfnInstance } from './servicediscovery.generated'; export interface NonIpInstanceBaseProps extends BaseInstanceProps { @@ -37,7 +37,8 @@ export class NonIpInstance extends InstanceBase { constructor(scope: Construct, id: string, props: NonIpInstanceProps) { super(scope, id); - if (props.service.namespace.type !== NamespaceType.HTTP) { + const discoveryType = props.service.discoveryType || defaultDiscoveryType(props.service.namespace); + if (discoveryType !== DiscoveryType.API) { throw new Error('This type of instance can only be registered for HTTP namespaces.'); } diff --git a/packages/@aws-cdk/aws-servicediscovery/lib/private/utils.ts b/packages/@aws-cdk/aws-servicediscovery/lib/private/utils.ts new file mode 100644 index 0000000000000..b42f7a5c2d69c --- /dev/null +++ b/packages/@aws-cdk/aws-servicediscovery/lib/private/utils.ts @@ -0,0 +1,6 @@ +import { INamespace, NamespaceType } from '../namespace'; +import { DiscoveryType } from '../service'; + +export function defaultDiscoveryType(namespace : INamespace): DiscoveryType { + return namespace.type == NamespaceType.HTTP ? DiscoveryType.API: DiscoveryType.DNS_AND_API; +} diff --git a/packages/@aws-cdk/aws-servicediscovery/lib/service.ts b/packages/@aws-cdk/aws-servicediscovery/lib/service.ts index b0dce1b20c882..f61ce38ca5f1b 100644 --- a/packages/@aws-cdk/aws-servicediscovery/lib/service.ts +++ b/packages/@aws-cdk/aws-servicediscovery/lib/service.ts @@ -7,6 +7,7 @@ import { IInstance } from './instance'; import { IpInstance, IpInstanceBaseProps } from './ip-instance'; import { INamespace, NamespaceType } from './namespace'; import { NonIpInstance, NonIpInstanceBaseProps } from './non-ip-instance'; +import { defaultDiscoveryType } from './private/utils'; import { CfnService } from './servicediscovery.generated'; export interface IService extends IResource { @@ -42,6 +43,11 @@ export interface IService extends IResource { * The Routing Policy used by the service */ readonly routingPolicy: RoutingPolicy; + + /** + * The discovery type used by the service + */ + readonly discoveryType: DiscoveryType; } /** @@ -87,6 +93,13 @@ export interface BaseServiceProps { * PublicDnsNamespace */ export interface DnsServiceProps extends BaseServiceProps { + /** + * Controls how instances within this service can be discovered + * + * @default DNS_AND_API + */ + readonly discoveryType?: DiscoveryType; + /** * The DNS type of the record that you want AWS Cloud Map to create. Supported record types * include A, AAAA, A and AAAA (A_AAAA), CNAME, and SRV. @@ -136,6 +149,7 @@ abstract class ServiceBase extends Resource implements IService { public abstract dnsRecordType: DnsRecordType; public abstract routingPolicy: RoutingPolicy; public abstract readonly serviceName: string; + public abstract discoveryType: DiscoveryType; } export interface ServiceAttributes { @@ -145,6 +159,7 @@ export interface ServiceAttributes { readonly serviceArn: string; readonly dnsRecordType: DnsRecordType; readonly routingPolicy: RoutingPolicy; + readonly discoveryType?: DiscoveryType; } /** @@ -160,6 +175,7 @@ export class Service extends ServiceBase { public dnsRecordType = attrs.dnsRecordType; public routingPolicy = attrs.routingPolicy; public serviceName = attrs.serviceName; + public discoveryType = attrs.discoveryType || defaultDiscoveryType(attrs.namespace); } return new Import(scope, id); @@ -195,14 +211,31 @@ export class Service extends ServiceBase { */ public readonly routingPolicy: RoutingPolicy; + /** + * The discovery type used by this service. + */ + public readonly discoveryType: DiscoveryType; + constructor(scope: Construct, id: string, props: ServiceProps) { super(scope, id); const namespaceType = props.namespace.type; + const discoveryType = props.discoveryType || defaultDiscoveryType(props.namespace); + + if (namespaceType == NamespaceType.HTTP && discoveryType == DiscoveryType.DNS_AND_API) { + throw new Error( + 'Cannot specify `discoveryType` of DNS_AND_API when using an HTTP namespace.', + ); + } // Validations - if (namespaceType === NamespaceType.HTTP && (props.routingPolicy || props.dnsRecordType)) { - throw new Error('Cannot specify `routingPolicy` or `dnsRecord` when using an HTTP namespace.'); + if ( + discoveryType === DiscoveryType.API && + (props.routingPolicy || props.dnsRecordType) + ) { + throw new Error( + 'Cannot specify `routingPolicy` or `dnsRecord` when using an HTTP namespace.', + ); } if (props.healthCheck && props.customHealthCheck) { @@ -247,13 +280,14 @@ export class Service extends ServiceBase { throw new Error('Must support `A` or `AAAA` records to register loadbalancers.'); } - const dnsConfig: CfnService.DnsConfigProperty | undefined = props.namespace.type === NamespaceType.HTTP - ? undefined - : { - dnsRecords: renderDnsRecords(dnsRecordType, props.dnsTtl), - namespaceId: props.namespace.namespaceId, - routingPolicy, - }; + const dnsConfig: CfnService.DnsConfigProperty | undefined = + discoveryType === DiscoveryType.API + ? undefined + : { + dnsRecords: renderDnsRecords(dnsRecordType, props.dnsTtl), + namespaceId: props.namespace.namespaceId, + routingPolicy, + }; const healthCheckConfigDefaults = { type: HealthCheckType.HTTP, @@ -274,6 +308,7 @@ export class Service extends ServiceBase { healthCheckConfig, healthCheckCustomConfig, namespaceId: props.namespace.namespaceId, + type: props.discoveryType == DiscoveryType.API ? 'HTTP' : undefined, }); this.serviceName = service.attrName; @@ -282,6 +317,7 @@ export class Service extends ServiceBase { this.namespace = props.namespace; this.dnsRecordType = dnsRecordType; this.routingPolicy = routingPolicy; + this.discoveryType = discoveryType; } /** @@ -384,6 +420,20 @@ export interface HealthCheckCustomConfig { readonly failureThreshold?: number; } +/** + * Specifies information about the discovery type of a service + */ +export enum DiscoveryType { + /** + * Instances are discoverable via API only + */ + API = 'API', + /** + * Instances are discoverable via DNS or API + */ + DNS_AND_API = 'DNS_AND_API' +} + export enum DnsRecordType { /** * An A record diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index 331b4f29aec68..eaf6f46e877a6 100644 --- a/packages/@aws-cdk/aws-servicediscovery/package.json +++ b/packages/@aws-cdk/aws-servicediscovery/package.json @@ -119,6 +119,7 @@ "props-physical-name:@aws-cdk/aws-servicediscovery.IpInstanceProps", "props-physical-name:@aws-cdk/aws-servicediscovery.NonIpInstanceProps", "props-physical-name:@aws-cdk/aws-servicediscovery.ServiceProps", + "props-default-doc:@aws-cdk/aws-servicediscovery.ServiceAttributes.discoveryType", "docs-public-apis:@aws-cdk/aws-servicediscovery.CnameInstanceProps", "docs-public-apis:@aws-cdk/aws-servicediscovery.RoutingPolicy", "docs-public-apis:@aws-cdk/aws-servicediscovery.NamespaceType", @@ -131,6 +132,7 @@ "docs-public-apis:@aws-cdk/aws-servicediscovery.ServiceAttributes.routingPolicy", "docs-public-apis:@aws-cdk/aws-servicediscovery.ServiceAttributes.namespace", "docs-public-apis:@aws-cdk/aws-servicediscovery.ServiceAttributes.dnsRecordType", + "docs-public-apis:@aws-cdk/aws-servicediscovery.ServiceAttributes.discoveryType", "docs-public-apis:@aws-cdk/aws-servicediscovery.HttpNamespace.httpNamespaceArn", "docs-public-apis:@aws-cdk/aws-servicediscovery.HttpNamespace.httpNamespaceId", "docs-public-apis:@aws-cdk/aws-servicediscovery.HttpNamespace.httpNamespaceName", diff --git a/packages/@aws-cdk/aws-servicediscovery/test/instance.test.ts b/packages/@aws-cdk/aws-servicediscovery/test/instance.test.ts index 0fb472c1b1130..00f8498e14606 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/instance.test.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/instance.test.ts @@ -413,7 +413,40 @@ describe('instance', () => { }); - test('Throws when registering NonIpInstance for an Public namespace', () => { + test('Register NonIpInstance, DNS Namespace, API Only service', () => { + // GIVEN + const stack = new cdk.Stack(); + + const namespace = new servicediscovery.PublicDnsNamespace( + stack, + 'MyNamespace', + { + name: 'http', + }, + ); + + const service = namespace.createService('MyService', { discoveryType: servicediscovery.DiscoveryType.API } ); + + service.registerNonIpInstance('NonIpInstance', { + customAttributes: { dogs: 'good' }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties( + 'AWS::ServiceDiscovery::Instance', + { + InstanceAttributes: { + dogs: 'good', + }, + ServiceId: { + 'Fn::GetAtt': ['MyNamespaceMyService365E2470', 'Id'], + }, + InstanceId: 'MyNamespaceMyServiceNonIpInstance7EFD703A', + }, + ); + }); + + test('Throws when registering NonIpInstance for an DNS discoverable service', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-private-dns-namespace.lit.ts b/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-private-dns-namespace.lit.ts index 78c16999d8036..391b253cd6152 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-private-dns-namespace.lit.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/integ.service-with-private-dns-namespace.lit.ts @@ -23,4 +23,12 @@ const loadbalancer = new elbv2.ApplicationLoadBalancer(stack, 'LB', { vpc, inter service.registerLoadBalancer('Loadbalancer', loadbalancer); +const arnService = namespace.createService('ArnService', { + discoveryType: servicediscovery.DiscoveryType.API, +}); + +arnService.registerNonIpInstance('NonIpInstance', { + customAttributes: { arn: 'arn://' }, +}); + app.synth(); diff --git a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/aws-servicediscovery-integ.assets.json b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/aws-servicediscovery-integ.assets.json new file mode 100644 index 0000000000000..9607a10e26627 --- /dev/null +++ b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/aws-servicediscovery-integ.assets.json @@ -0,0 +1,19 @@ +{ + "version": "20.0.0", + "files": { + "d0b994983cc3cd6c314558cfe207b6dee3d75c2b30ac611f4cf4e2a657af5375": { + "source": { + "path": "aws-servicediscovery-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d0b994983cc3cd6c314558cfe207b6dee3d75c2b30ac611f4cf4e2a657af5375.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/aws-servicediscovery-integ.template.json b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/aws-servicediscovery-integ.template.json index 5438daf4c4c0f..d174d1c7a8a7d 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/aws-servicediscovery-integ.template.json +++ b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/aws-servicediscovery-integ.template.json @@ -442,6 +442,33 @@ "InstanceId": "awsservicediscoveryintegNamespaceServiceLoadbalancerA3D252B2" } }, + "NamespaceArnService1F427735": { + "Type": "AWS::ServiceDiscovery::Service", + "Properties": { + "NamespaceId": { + "Fn::GetAtt": [ + "Namespace9B63B8C8", + "Id" + ] + }, + "Type": "HTTP" + } + }, + "NamespaceArnServiceNonIpInstance91BB57CE": { + "Type": "AWS::ServiceDiscovery::Instance", + "Properties": { + "InstanceAttributes": { + "arn": "arn://" + }, + "ServiceId": { + "Fn::GetAtt": [ + "NamespaceArnService1F427735", + "Id" + ] + }, + "InstanceId": "awsservicediscoveryintegNamespaceArnServiceNonIpInstance80DE1A4E" + } + }, "LB8A12904C": { "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer", "Properties": { diff --git a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/cdk.out index 90bef2e09ad39..588d7b269d34f 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/manifest.json index 2b20c50ec3cb2..c6fbd78c0da80 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "20.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -171,6 +171,18 @@ "data": "NamespaceServiceLoadbalancerD271391A" } ], + "/aws-servicediscovery-integ/Namespace/ArnService/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NamespaceArnService1F427735" + } + ], + "/aws-servicediscovery-integ/Namespace/ArnService/NonIpInstance/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "NamespaceArnServiceNonIpInstance91BB57CE" + } + ], "/aws-servicediscovery-integ/LB/Resource": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/tree.json b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/tree.json index 5bd5728cea159..90a484efeb14f 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-servicediscovery/test/service-with-private-dns-namespace.lit.integ.snapshot/tree.json @@ -8,8 +8,8 @@ "id": "Tree", "path": "Tree", "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.65" } }, "aws-servicediscovery-integ": { @@ -765,6 +765,69 @@ "fqn": "@aws-cdk/aws-servicediscovery.Service", "version": "0.0.0" } + }, + "ArnService": { + "id": "ArnService", + "path": "aws-servicediscovery-integ/Namespace/ArnService", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-servicediscovery-integ/Namespace/ArnService/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ServiceDiscovery::Service", + "aws:cdk:cloudformation:props": { + "namespaceId": { + "Fn::GetAtt": [ + "Namespace9B63B8C8", + "Id" + ] + }, + "type": "HTTP" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicediscovery.CfnService", + "version": "0.0.0" + } + }, + "NonIpInstance": { + "id": "NonIpInstance", + "path": "aws-servicediscovery-integ/Namespace/ArnService/NonIpInstance", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-servicediscovery-integ/Namespace/ArnService/NonIpInstance/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ServiceDiscovery::Instance", + "aws:cdk:cloudformation:props": { + "instanceAttributes": { + "arn": "arn://" + }, + "serviceId": { + "Fn::GetAtt": [ + "NamespaceArnService1F427735", + "Id" + ] + }, + "instanceId": "awsservicediscoveryintegNamespaceArnServiceNonIpInstance80DE1A4E" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicediscovery.CfnInstance", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicediscovery.NonIpInstance", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-servicediscovery.Service", + "version": "0.0.0" + } } }, "constructInfo": { diff --git a/packages/@aws-cdk/aws-servicediscovery/test/service.test.ts b/packages/@aws-cdk/aws-servicediscovery/test/service.test.ts index 556bc6c2c7260..989b20b44a418 100644 --- a/packages/@aws-cdk/aws-servicediscovery/test/service.test.ts +++ b/packages/@aws-cdk/aws-servicediscovery/test/service.test.ts @@ -2,6 +2,7 @@ import { Template } from '@aws-cdk/assertions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as cdk from '@aws-cdk/core'; import * as servicediscovery from '../lib'; +import { DiscoveryType } from '../lib'; describe('service', () => { test('Service for HTTP namespace with custom health check', () => { @@ -399,6 +400,25 @@ describe('service', () => { }).toThrow(/Cannot register loadbalancers when routing policy is `Multivalue`./); + }); + + test('Throws when specifying discovery type of DNS within a HttpNamespace', () => { + // GIVEN + const stack = new cdk.Stack(); + + const namespace = new servicediscovery.HttpNamespace(stack, 'MyNamespace', { + name: 'http', + }); + + // THEN + expect(() => { + new servicediscovery.Service(stack, 'Service', { + namespace, + discoveryType: DiscoveryType.DNS_AND_API, + }); + }).toThrow(/Cannot specify `discoveryType` of DNS_AND_API when using an HTTP namespace./); + + }); test('Service for Private DNS namespace', () => { @@ -449,4 +469,39 @@ describe('service', () => { }); + + test('Service for DNS namespace with API only discovery', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc'); + + const namespace = new servicediscovery.PrivateDnsNamespace(stack, 'MyNamespace', { + name: 'private', + vpc, + }); + + namespace.createService('MyService', { + name: 'service', + description: 'service description', + discoveryType: DiscoveryType.API, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::PrivateDnsNamespace', { + Name: 'private', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::ServiceDiscovery::Service', { + Description: 'service description', + Name: 'service', + NamespaceId: { + 'Fn::GetAtt': [ + 'MyNamespaceD0BB8558', + 'Id', + ], + }, + Type: 'HTTP', + }); + + }); });