-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Conversation
…hapes that support validation context
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @default - True | |
* @default true |
/** | ||
* ACM Trust Properties | ||
*/ | ||
export interface ACMTrustOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export interface ACMTrustOptions { | |
export interface AcmTrustOptions { |
/** | ||
* Defines the TLS validation context trust. | ||
*/ | ||
export abstract class TLSValidationContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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({ |
There was a problem hiding this comment.
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
Pull request has been modified.
There was a problem hiding this 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[]; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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[]; |
There was a problem hiding this comment.
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...?
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
}, | ||
}); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary empty line.
/** | ||
* Client policy for Virtual Service | ||
*/ |
There was a problem hiding this comment.
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({ |
There was a problem hiding this comment.
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
?
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, | ||
}, | ||
}, | ||
}, | ||
}, | ||
], |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Pull request has been modified.
In reply to comment, |
There was a problem hiding this 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'; |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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) }), |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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.
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'], |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to certificateAuthorityArn
?
Also, the ReadMe changes are missing 🙂. |
Pull request has been modified.
There was a problem hiding this 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); |
There was a problem hiding this comment.
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', |
There was a problem hiding this comment.
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', |
There was a problem hiding this comment.
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.
}); | ||
``` | ||
|
||
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. |
There was a problem hiding this comment.
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))); | |||
} | |||
|
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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
.
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)); | |||
|
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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:
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'); |
Pull request has been modified.
There was a problem hiding this 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! 😊
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 CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
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). |
…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*
Adds backend defaults to Virtual Node and Virtual Gateways.
Adds validation context for the backend defined on the Virtual Node.
Before merging (TODO):
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license