Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(appmesh): add ClientPolicy to VirtualNode, VirtualGateway and VirtualService #11563

Merged
merged 28 commits into from
Dec 7, 2020

Conversation

sshver
Copy link
Contributor

@sshver sshver commented Nov 18, 2020

Adds backend defaults to Virtual Node and Virtual Gateways.
Adds validation context for the backend defined on the Virtual Node.

Before merging (TODO):

  • Update README

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@gitpod-io
Copy link

gitpod-io bot commented Nov 18, 2020

Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @sshver ! Some initial comments / questions.

/**
* TLS Connections with downstream server will always be enforced if True
*/
export interface TLSClientPolicyOptions {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be called TlsClientPolicyOptions.

/**
* TLS enforced if True.
*
* @default - True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @default - True
* @default true

/**
* ACM Trust Properties
*/
export interface ACMTrustOptions {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export interface ACMTrustOptions {
export interface AcmTrustOptions {

/**
* Defines the TLS validation context trust.
*/
export abstract class TLSValidationContext {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export abstract class TLSValidationContext {
export abstract class TlsValidationContext {

@@ -151,6 +159,7 @@ export class VirtualGateway extends VirtualGatewayBase {
public readonly mesh: IMesh;

protected readonly listeners = new Array<VirtualGatewayListenerConfig>();
private readonly backendDefaults: CfnVirtualGateway.VirtualGatewayBackendDefaultsProperty;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to be a field? It's only set and read in the constructor of the class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the PR to use an array here. This gives me an option set omitEmptyArray true and this would remove backendDefault if not defined.

@@ -166,13 +175,20 @@ export class VirtualGateway extends VirtualGatewayBase {
props.listeners.forEach(listener => this.listeners.push(listener.bind(this)));
}

if (props.backendDefaults) {
this.backendDefaults = this.addBackendDefaults(props.backendDefaults);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a good method name. add should only be used if there can be multiples of the thing added. Since this doesn't seem to be the case here, I would go for something like setBackendDefaults.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the function name to setBackendDefaults

clientPolicy: {
tls: {
enforce: clientPolicy.tlsClientPolicy.enforce ?? true,
validation: clientPolicy.tlsClientPolicy.validation.bind(this).tlsValidation,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the tslValidation set in bind(), but the rest are properties of the ClientPolicy? Wondering why the different approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earlier, I was trying to separate out the validation context as it could be used in future for Listener TLS.
But now I have a single file client-policy.ts and now the clientPolicy properties are being set in bind().

This would make the API more intuitive.

new appmesh.VirtualGateway(stack, 'virtual-gateway', {
 virtualGatewayName: 'virtual-gateway',
 mesh: mesh,
 backendDefaults: appmesh.ClientPolicy.fileTrust({
   certificateChain: ['path-to-certificate'],
   enforceTls: false,
 }),
});

@@ -92,6 +99,13 @@ const node3 = mesh.addVirtualNode('node3', {
unhealthyThreshold: 2,
},
})],
backendDefaults: {
tlsClientPolicy: {
validation: appmesh.TLSValidationContext.fileTrust({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're testing fileTrust twice here. Should we use a different static factory method for the second time?

2. Updated bind() to return client policy
@mergify mergify bot dismissed skinny85’s stale review November 24, 2020 01:03

Pull request has been modified.

@sshver sshver requested a review from skinny85 November 24, 2020 16:11
Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @sshver , but I still have some questions 🙂

/**
* Amazon Resource Names (ARN) of trusted ACM Private Certificate Authorities
*/
readonly certificateAuthorityArns: string[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be L2 resources, instead of strings?

If L2s for these don't exist, we should add them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a skeleton L2 for ACMPCA.

/**
* Path to the Certificate Chain file on the file system where the Envoy is deployed.
*/
readonly certificateChain: string[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this an array...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to string

}

class ClientPolicyImpl extends ClientPolicy {
constructor (private readonly enforce: boolean = true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No point in having the default argument here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the default arguments

};
}

private renderValidation(): CfnVirtualNode.TlsValidationContextProperty {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just inline this method, I don't see what value it adds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the method renderValidation and added inline.

},
});
}
}


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary empty line.

Comment on lines 135 to 137
/**
* Client policy for Virtual Service
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need the second comment if the property is coming from an interface.

@@ -96,6 +100,10 @@ const node3 = mesh.addVirtualNode('node3', {
unhealthyThreshold: 2,
},
})],
backendDefaults: appmesh.ClientPolicy.fileTrust({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we test the other static factory method here, acmTrust?

Comment on lines 338 to 361
Listeners: [
{
HealthCheck: {
HealthyThreshold: 2,
IntervalMillis: 5000,
Port: 80,
Protocol: 'tcp',
TimeoutMillis: 3000,
UnhealthyThreshold: 2,
},
PortMapping: {
Port: 80,
Protocol: 'tcp',
},
Timeout: {
TCP: {
Idle: {
Unit: 'ms',
Value: 10000,
},
},
},
},
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this is included in this test? I think we're only interested in the below Backends part, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed Listeners and kept backend part in test.

@mergify mergify bot dismissed skinny85’s stale review November 30, 2020 20:37

Pull request has been modified.

@sshver
Copy link
Contributor Author

sshver commented Nov 30, 2020

In reply to comment,
I have created a shared interface BackendDefaults with clientPolicy as a property. The interface is used in VirtualNode and VirtualGateway to represent the backendDefault property. The ClientPolicy is a part of VirtualServiceBackend object. (Reference). Therefore I have updated the VirtualService and added a property clientPolicy of type ClientPolicy

@sshver sshver requested a review from skinny85 November 30, 2020 21:30
Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great @sshver ! Nice work on adding the skeleton ACM PCA resources, I have a feeling that skeleton will get filled out fast after we merge this 😉.

A few minor comments before I give my "ShipIt".

@@ -0,0 +1,63 @@
import * as cdk from '@aws-cdk/core';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should be called certificate-authority.ts.

/**
* Basic Properties of a Certificate
*/
export interface CertificateAuthorityProps {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove these Props for now. Having fromCertificateAuthorityArn() is good enough for now.

/**
* Interface which all CertificateAuthority based class must implement
*/
export abstract class CertificateAuthorityBase extends cdk.Resource implements ICertificateAuthority {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a feeling this class is superfluous at this stage, and can be safely removed once you incorporate my remaining comments for this file.

But even if you decide to keep it, it should be module-private (remove the export modifier here).

*/
public readonly certificateAuthorityArn: string;

constructor(scope: Construct, id: string, props: CertificateAuthorityProps) {
Copy link
Contributor

@skinny85 skinny85 Nov 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just make this constructor the same as for cdk.Resource, and make it private. Just having fromCertificateAuthorityArn() is good enough at this stage (and passing an ARN to a resource managed by the CDK doesn't really make much sense anyway).


constructor(scope: Construct, id: string, props: CertificateAuthorityProps) {
super(scope, id, {
physicalName: props.certificateAuthorityArn || cdk.Lazy.string({ produce: () => cdk.Names.uniqueId(this) }),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, that's not how this property should be used (it should be the actual name, not the ARN).

Just leave this out for now.

@@ -1,7 +1,7 @@
import { expect, haveResourceLike } from '@aws-cdk/assert';
import { CertificateAuthority } from '@aws-cdk/aws-acmpca';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - no unqualified imports outside your module.

Suggested change
import { CertificateAuthority } from '@aws-cdk/aws-acmpca';
import * as acmpca from '@aws-cdk/aws-acmpca';

Validation: {
Trust: {
ACM: {
CertificateAuthorityArns: ['arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the variable from above here instead of repeating the value?

meshName: 'test-mesh',
});

const arn = 'arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/12345678-1234-1234-1234-123456789012';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to certificateAuthorityArn?

@skinny85
Copy link
Contributor

Also, the ReadMe changes are missing 🙂.

@sshver sshver requested a review from skinny85 December 4, 2020 01:35
@skinny85 skinny85 changed the title feat(appmesh): adds Backend Defaults and Backend Validation Context feat(appmesh): add ClientPolicy to VirtualNode, VirtualGateway and VirtualService Dec 4, 2020
@github-actions github-actions bot added the @aws-cdk/aws-appmesh Related to AWS App Mesh label Dec 4, 2020
Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're almost there! A few minor changes before we merge this in, mainly concerning naming and documentation.

}),
});

node.addBackend(virtualService);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a weird example, right? Because if I understand correctly, because the VirtualService defines its own clientPolicy, the backendDefaultsClientPolicy on VirtualNode will be ignored?

If my understanding is correct, I would re-structure the example to be less artificial.

@@ -159,8 +159,22 @@ const node = mesh.addVirtualNode('virtual-node', {
unhealthyThreshold: 2,
},
})],
backendDefaultsClientPolicy: appmesh.ClientPolicy.fileTrust({
certificateChain: 'path-to-certificate',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we put a more realistic path to the certificate here? Where are they usually stored? Right now, this looks kind of weird as an example (it's not even a file path...).

virtualServiceName: 'service1.domain.local',
mesh,
clientPolicy: appmesh.ClientPolicy.fileTrust({
certificateChain: 'path-to-certificate',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment for this path as above.

packages/@aws-cdk/aws-appmesh/README.md Show resolved Hide resolved
});
```

The listeners field can be omitted which will default to an HTTP Listener on port 8080.
A gateway route can be added using the `gateway.addGatewayRoute()` method.

The `backendDefaults` property are added to the node while creating the virtual gateway. These are virtual gateway's service backends client policy defaults.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old name used here (it should be backendDefaultsClientPolicy, not backendDefaults).

@@ -165,14 +172,14 @@ export class VirtualGateway extends VirtualGatewayBase {
} else {
props.listeners.forEach(listener => this.listeners.push(listener.bind(this)));
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave this as-is.

*
* @default - No Config
*/
readonly backendDefaultsClientPolicy?: ClientPolicy;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this name is slightly incorrect. There are multiple 'backends', but a single 'default'. So I would call this backendsDefaultClientPolicy.

*
* @default - No Config
*/
readonly backendDefaultsClientPolicy?: ClientPolicy;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here as for VirtualGateway.

Suggested change
readonly backendDefaultsClientPolicy?: ClientPolicy;
readonly backendsDefaultClientPolicy?: ClientPolicy;

@@ -187,14 +195,16 @@ export class VirtualNode extends VirtualNodeBase {

props.backends?.forEach(backend => this.addBackend(backend));
props.listeners?.forEach(listener => this.addListener(listener));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave this as-is.

},
},
}));
test.done();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually like to leave an empty line before the last test.done(), makes the tests read slightly better in my view.

public bind(_scope: cdk.Construct): ClientPolicyConfig {
if (this.certificateType === CertificateType.ACMPCA && this.certificateAuthorityArns?.map(certificateArn =>
certificateArn.certificateAuthorityArn).length === 0) {
throw new Error('Certificate Authority ARN is required but empty');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this a little more clearer what and where was the problem:

Suggested change
throw new Error('Certificate Authority ARN is required but empty');
throw new Error('You must provide at least one Certificate Authority when creating an ACM Trust ClientPolicy');

@mergify mergify bot dismissed skinny85’s stale review December 7, 2020 17:10

Pull request has been modified.

@sshver sshver requested a review from skinny85 December 7, 2020 17:41
Copy link
Contributor

@skinny85 skinny85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great @sshver , thanks! 😊

@mergify
Copy link
Contributor

mergify bot commented Dec 7, 2020

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject6AEA49D1-qxepHUsryhcu
  • Commit ID: de939c3
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@mergify
Copy link
Contributor

mergify bot commented Dec 7, 2020

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify mergify bot merged commit bfee58c into aws:master Dec 7, 2020
flochaz pushed a commit to flochaz/aws-cdk that referenced this pull request Jan 5, 2021
…rtualService (aws#11563)

Adds backend defaults to Virtual Node and Virtual Gateways.
Adds validation context for the backend defined on the Virtual Node.

Before merging (TODO):

- [ ] Update README

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-appmesh Related to AWS App Mesh
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants