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(aws-ecr): add public repository construct #16229

Closed
wants to merge 10 commits into from
Closed

feat(aws-ecr): add public repository construct #16229

wants to merge 10 commits into from

Conversation

yerzhan7
Copy link
Contributor

@yerzhan7 yerzhan7 commented Aug 25, 2021

Add L2 construct for AWS::ECR::PublicRepository.

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-publicrepository.html

Closes #12162


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 Aug 25, 2021

@yerzhan7 yerzhan7 marked this pull request as ready for review August 30, 2021 11:43
@peterwoodworth peterwoodworth changed the title feat(aws-ecr): add public repository construct feat(aws-ecr): add public repository construct Oct 21, 2021
@github-actions github-actions bot added the @aws-cdk/aws-ecr Related to Amazon Elastic Container Registry label Oct 21, 2021
@falnyr
Copy link

falnyr commented Oct 26, 2021

@madeline-k any update on this?

@kornicameister
Copy link
Contributor

@skinny85 can we get some assistance here?

Copy link
Contributor

@madeline-k madeline-k left a comment

Choose a reason for hiding this comment

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

Thanks for submitting this PR @yerzhan7! After a first pass, my biggest feedback is that there is a bunch of similarities between PublicRepository and Repository. I think there is an opportunity to use a base class to share some logic here. I will make another pass with more detailed feedback soon, but thought you might want to get started on that!

@yerzhan7
Copy link
Contributor Author

Thanks for reviewing @madeline-k

there is a bunch of similarities between PublicRepository and Repository. I think there is an opportunity to use a base class to share some logic here.

I'll refactor into separate class in a few days.

Copy link
Contributor

@madeline-k madeline-k left a comment

Choose a reason for hiding this comment

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

There are still more things that can be shared between RepositoryBase and PublicRepositoryBase. I've commented on a few. Introducing a shared Interface for the public and private repos is a step in the right direction! We also need a shared base class.

Comment on lines +59 to +70
const rule = new events.Rule(this, id, options);
rule.addTarget(options.target);
rule.addEventPattern({
source: ['aws.ecr-public'],
detailType: ['AWS API Call via CloudTrail'],
detail: {
requestParameters: {
repositoryName: [this.publicRepositoryName],
},
},
});
return rule;
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 the exact same logic as onCloudTrailEvent in RepositoryBase.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See argument below.

Comment on lines +14 to +24
/**
* The name of the repository.
* @attribute
*/
readonly publicRepositoryName: string;

/**
* The ARN of the repository.
* @attribute
*/
readonly publicRepositoryArn: 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 not move these up to IBaseRepository and use repositoryName and repositoryArn for both public and private repositories?

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 cannot do that because linter requires resource name to be of form ${CfnResource.name}Name.

Comment on lines +73 to +84
public onCloudTrailImagePushed(id: string, options: OnCloudTrailImagePushedOptions = {}): events.Rule {
const rule = this.onCloudTrailEvent(id, options);
rule.addEventPattern({
detail: {
eventName: ['PutImage'],
requestParameters: {
imageTag: options.imageTag ? [options.imageTag] : undefined,
},
},
});
return rule;
}
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 the same implementation as RepositoryBase.onCloudTrailImagePushed. This should move to a shared base 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.

See argument below.

Comment on lines +90 to +96
const rule = new events.Rule(this, id, options);
rule.addEventPattern({
source: ['aws.ecr-public'],
resources: [this.publicRepositoryArn],
});
rule.addTarget(options.target);
return rule;
Copy link
Contributor

Choose a reason for hiding this comment

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

Also the same as RepositoryBase

Copy link
Contributor

Choose a reason for hiding this comment

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

To still differentiate the validation errors needed for public repos, you can use the template pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same argument as below: I cannot make onEvent method to be defined in parent class as Typescript does not support multiple inheritance.

Comment on lines +99 to +107
public grant(grantee: iam.IGrantable, ...actions: string[]) {
return iam.Grant.addToPrincipalAndResource({
grantee,
actions,
resourceArns: [this.publicRepositoryArn],
resourceSelfArns: [],
resource: this,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can be shared with RepositoryBase

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 cannot make grantmethod to be defined in parent class because PublicRepositoryBase class already extends Resource class, and Typescript does not support multiple inheritance.

If you have alternative ideas let me know.

*
* @default Automatically generated name.
*/
readonly publicRepositoryName?: string;
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 move up to BaseRepositoryProps

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 cannot do that because linter requires resource name to be of form ${CfnResource.name}Name.

Comment on lines +181 to +195
/**
* ECR Public Repository attributes.
*/
export interface PublicRepositoryAttributes {

/**
* The name of the repository.
*/
readonly publicRepositoryName: string;

/**
* The ARN of the repository.
*/
readonly publicRepositoryArn: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We can re-use the RepositoryAttributes interface since they are the same.

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 cannot do this because of linter rule violation.

Comment on lines +275 to +297
private static validateRepositoryName(physicalName: string) {
const repositoryName = physicalName;
if (!repositoryName || Token.isUnresolved(repositoryName)) {
// the name is a late-bound value, not a defined string,
// so skip validation
return;
}

const errors: string[] = [];

// Rules codified from https://docs.aws.amazon.com/AmazonECRPublic/latest/APIReference/API_CreateRepository.html#ecrpublic-CreateRepository-request-repositoryName
if (repositoryName.length < 2 || repositoryName.length > 205) {
errors.push('Repository name must be at least 2 and no more than 205 characters');
}
const isPatternMatch = /^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*\/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(repositoryName);
if (!isPatternMatch) {
errors.push('Repository name must follow the specified pattern: (?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*');
}

if (errors.length > 0) {
throw new Error(`Invalid ECR repository name (value: ${repositoryName})${EOL}${errors.join(EOL)}`);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This validation is really similar to the Repository.validateRepositoryName. I think we should use the template pattern here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is similar. Same argument about multiple inheritance.

I can extract it into separate base-repository.ts method if you want.

Comment on lines +352 to +368
/**
* Add a policy statement to the repository's resource policy
*/
public addToResourcePolicy(statement: iam.PolicyStatement): iam.AddToResourcePolicyResult {
if (this.resourcePolicy === undefined) {
this.resourcePolicy = new iam.PolicyDocument();
}
this.resourcePolicy.addStatements(statement);
return { statementAdded: true, policyDependable: this.resourcePolicy };
}

protected validate(): string[] {
const errors = super.validate();
errors.push(...this.resourcePolicy?.validateForResourcePolicy() || []);
return errors;
}
}
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 all the same as Repository as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as above.

@madeline-k madeline-k removed their assignment Feb 10, 2022
@yerzhan7
Copy link
Contributor Author

Thanks for reviewing!

We also need a shared base class.

I cannot do that due to Typescript not supporting multiple inheritance.

@mergify mergify bot dismissed madeline-k’s stale review February 13, 2022 11:56

Pull request has been modified.

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject89A8053A-LhjRyN9kxr8o
  • Commit ID: c4666b2
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

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

@kornicameister
Copy link
Contributor

@madeline-k would it be able to just live, for a while, with a little bit of duplication and change that in follow-up?

@rix0rrr rix0rrr added feature-request A feature should be added or improved. p2 and removed @aws-cdk/aws-ecr Related to Amazon Elastic Container Registry labels Mar 4, 2022
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request A feature should be added or improved. p2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(ecr): support ecr public repository
6 participants