aws-cdk-lib
Version:
Version 2 of the AWS Cloud Development Kit library
2 lines (1 loc) • 15.1 kB
JavaScript
"use strict";var _a,_b,_c;Object.defineProperty(exports,"__esModule",{value:!0}),exports.RepositoryEncryption=exports.TagMutability=exports.Repository=exports.RepositoryBase=void 0;const jsiiDeprecationWarnings=require("../../.warnings.jsii.js"),JSII_RTTI_SYMBOL_1=Symbol.for("jsii.rtti"),os_1=require("os"),path=require("path"),events=require("../../aws-events"),iam=require("../../aws-iam"),core_1=require("../../core"),ecr_generated_1=require("./ecr.generated"),lifecycle_1=require("./lifecycle"),AUTO_DELETE_IMAGES_RESOURCE_TYPE="Custom::ECRAutoDeleteImages",AUTO_DELETE_IMAGES_TAG="aws-cdk:auto-delete-images";class RepositoryBase extends core_1.Resource{get repositoryUri(){return this.repositoryUriForTag()}repositoryUriForTag(tag){const tagSuffix=tag?`:${tag}`:"";return this.repositoryUriWithSuffix(tagSuffix)}repositoryUriForDigest(digest){const digestSuffix=digest?`@${digest}`:"";return this.repositoryUriWithSuffix(digestSuffix)}repositoryUriForTagOrDigest(tagOrDigest){return tagOrDigest?.startsWith("sha256:")?this.repositoryUriForDigest(tagOrDigest):this.repositoryUriForTag(tagOrDigest)}repositoryUriWithSuffix(suffix){const parts=this.stack.splitArn(this.repositoryArn,core_1.ArnFormat.SLASH_RESOURCE_NAME);return`${parts.account}.dkr.ecr.${parts.region}.${this.stack.urlSuffix}/${this.repositoryName}${suffix}`}onCloudTrailEvent(id,options={}){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_events_OnEventOptions(options)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.onCloudTrailEvent),error}const rule=new events.Rule(this,id,options);return rule.addTarget(options.target),rule.addEventPattern({source:["aws.ecr"],detailType:["AWS API Call via CloudTrail"],detail:{requestParameters:{repositoryName:[this.repositoryName]}}}),rule}onCloudTrailImagePushed(id,options={}){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_ecr_OnCloudTrailImagePushedOptions(options)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.onCloudTrailImagePushed),error}const rule=this.onCloudTrailEvent(id,options);return rule.addEventPattern({detail:{eventName:["PutImage"],requestParameters:{imageTag:options.imageTag?[options.imageTag]:void 0}}}),rule}onImageScanCompleted(id,options={}){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_ecr_OnImageScanCompletedOptions(options)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.onImageScanCompleted),error}const rule=new events.Rule(this,id,options);return rule.addTarget(options.target),rule.addEventPattern({source:["aws.ecr"],detailType:["ECR Image Scan"],detail:{"repository-name":[this.repositoryName],"scan-status":["COMPLETE"],"image-tags":options.imageTags??void 0}}),rule}onEvent(id,options={}){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_events_OnEventOptions(options)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.onEvent),error}const rule=new events.Rule(this,id,options);return rule.addEventPattern({source:["aws.ecr"],resources:[this.repositoryArn]}),rule.addTarget(options.target),rule}grant(grantee,...actions){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_iam_IGrantable(grantee)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.grant),error}const crossAccountPrincipal=this.unsafeCrossAccountResourcePolicyPrincipal(grantee);if(crossAccountPrincipal){const crossAccountPrincipalStack=core_1.Stack.of(crossAccountPrincipal),roleTag=`${crossAccountPrincipalStack.stackName}_${crossAccountPrincipal.node.addr}`;return core_1.Tags.of(crossAccountPrincipal).add("aws-cdk:id",roleTag),this.addToResourcePolicy(new iam.PolicyStatement({actions,principals:[new iam.AccountPrincipal(crossAccountPrincipalStack.account)],conditions:{StringEquals:{"aws:PrincipalTag/aws-cdk:id":roleTag}}})),iam.Grant.addToPrincipal({grantee,actions,resourceArns:[this.repositoryArn],scope:this})}else return iam.Grant.addToPrincipalOrResource({grantee,actions,resourceArns:[this.repositoryArn],resourceSelfArns:[],resource:this})}grantPull(grantee){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_iam_IGrantable(grantee)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.grantPull),error}const ret=this.grant(grantee,"ecr:BatchCheckLayerAvailability","ecr:GetDownloadUrlForLayer","ecr:BatchGetImage");return iam.Grant.addToPrincipal({grantee,actions:["ecr:GetAuthorizationToken"],resourceArns:["*"],scope:this}),ret}grantPullPush(grantee){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_iam_IGrantable(grantee)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.grantPullPush),error}return this.grantPull(grantee),this.grant(grantee,"ecr:PutImage","ecr:InitiateLayerUpload","ecr:UploadLayerPart","ecr:CompleteLayerUpload")}unsafeCrossAccountResourcePolicyPrincipal(grantee){const principal=grantee.grantPrincipal,principalAccount=principal.principalAccount;if(!principalAccount)return;const repoAndPrincipalAccountCompare=core_1.Token.compareStrings(this.env.account,principalAccount);if(repoAndPrincipalAccountCompare===core_1.TokenComparison.BOTH_UNRESOLVED||repoAndPrincipalAccountCompare===core_1.TokenComparison.SAME||!iam.principalIsOwnedResource(principal))return;const principalStack=core_1.Stack.of(principal);if(!this.stack.dependencies.includes(principalStack))return principal}}exports.RepositoryBase=RepositoryBase,_a=JSII_RTTI_SYMBOL_1,RepositoryBase[_a]={fqn:"aws-cdk-lib.aws_ecr.RepositoryBase",version:"2.70.0"};class Repository extends RepositoryBase{constructor(scope,id,props={}){super(scope,id,{physicalName:props.repositoryName}),this.lifecycleRules=new Array;try{jsiiDeprecationWarnings.aws_cdk_lib_aws_ecr_RepositoryProps(props)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,Repository),error}Repository.validateRepositoryName(this.physicalName);const resource=new ecr_generated_1.CfnRepository(this,"Resource",{repositoryName:this.physicalName,repositoryPolicyText:core_1.Lazy.any({produce:()=>this.policyDocument}),lifecyclePolicy:core_1.Lazy.any({produce:()=>this.renderLifecyclePolicy()}),imageScanningConfiguration:props.imageScanOnPush!==void 0?{scanOnPush:props.imageScanOnPush}:void 0,imageTagMutability:props.imageTagMutability||void 0,encryptionConfiguration:this.parseEncryption(props)});if(this._resource=resource,props.autoDeleteImages){if(props.removalPolicy!==core_1.RemovalPolicy.DESTROY)throw new Error("Cannot use 'autoDeleteImages' property on a repository without setting removal policy to 'DESTROY'.");this.enableAutoDeleteImages()}resource.applyRemovalPolicy(props.removalPolicy),this.registryId=props.lifecycleRegistryId,props.lifecycleRules&&props.lifecycleRules.forEach(this.addLifecycleRule.bind(this)),this.repositoryName=this.getResourceNameAttribute(resource.ref),this.repositoryArn=this.getResourceArnAttribute(resource.attrArn,{service:"ecr",resource:"repository",resourceName:this.physicalName}),this.node.addValidation({validate:()=>this.policyDocument?.validateForResourcePolicy()??[]})}static fromRepositoryAttributes(scope,id,attrs){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_ecr_RepositoryAttributes(attrs)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.fromRepositoryAttributes),error}class Import extends RepositoryBase{constructor(){super(...arguments),this.repositoryName=attrs.repositoryName,this.repositoryArn=attrs.repositoryArn}addToResourcePolicy(_statement){return{statementAdded:!1}}}return new Import(scope,id)}static fromRepositoryArn(scope,id,repositoryArn){if(core_1.Token.isUnresolved(repositoryArn))throw new Error('"repositoryArn" is a late-bound value, and therefore "repositoryName" is required. Use `fromRepositoryAttributes` instead');const repositoryName=repositoryArn.split("/").slice(1).join("/");class Import extends RepositoryBase{constructor(){super(...arguments),this.repositoryName=repositoryName,this.repositoryArn=repositoryArn}addToResourcePolicy(_statement){return{statementAdded:!1}}}return new Import(scope,id,{environmentFromArn:repositoryArn})}static fromRepositoryName(scope,id,repositoryName){class Import extends RepositoryBase{constructor(){super(...arguments),this.repositoryName=repositoryName,this.repositoryArn=Repository.arnForLocalRepository(repositoryName,scope)}addToResourcePolicy(_statement){return{statementAdded:!1}}}return new Import(scope,id)}static arnForLocalRepository(repositoryName,scope,account){return core_1.Stack.of(scope).formatArn({account,service:"ecr",resource:"repository",resourceName:repositoryName})}static validateRepositoryName(physicalName){const repositoryName=physicalName;if(!repositoryName||core_1.Token.isUnresolved(repositoryName))return;const errors=[];if((repositoryName.length<2||repositoryName.length>256)&&errors.push("Repository name must be at least 2 and no more than 256 characters"),/^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*\/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(repositoryName)||errors.push("Repository name must follow the specified pattern: (?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*"),errors.length>0)throw new Error(`Invalid ECR repository name (value: ${repositoryName})${os_1.EOL}${errors.join(os_1.EOL)}`)}addToResourcePolicy(statement){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_iam_PolicyStatement(statement)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.addToResourcePolicy),error}return this.policyDocument===void 0&&(this.policyDocument=new iam.PolicyDocument),this.policyDocument.addStatements(statement),{statementAdded:!0,policyDependable:this.policyDocument}}addLifecycleRule(rule){try{jsiiDeprecationWarnings.aws_cdk_lib_aws_ecr_LifecycleRule(rule)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.addLifecycleRule),error}if(rule.tagStatus===void 0&&(rule={...rule,tagStatus:rule.tagPrefixList===void 0?lifecycle_1.TagStatus.ANY:lifecycle_1.TagStatus.TAGGED}),rule.tagStatus===lifecycle_1.TagStatus.TAGGED&&(rule.tagPrefixList===void 0||rule.tagPrefixList.length===0))throw new Error("TagStatus.Tagged requires the specification of a tagPrefixList");if(rule.tagStatus!==lifecycle_1.TagStatus.TAGGED&&rule.tagPrefixList!==void 0)throw new Error("tagPrefixList can only be specified when tagStatus is set to Tagged");if(rule.maxImageAge!==void 0==(rule.maxImageCount!==void 0))throw new Error(`Life cycle rule must contain exactly one of 'maxImageAge' and 'maxImageCount', got: ${JSON.stringify(rule)}`);if(rule.tagStatus===lifecycle_1.TagStatus.ANY&&this.lifecycleRules.filter(r=>r.tagStatus===lifecycle_1.TagStatus.ANY).length>0)throw new Error("Life cycle can only have one TagStatus.Any rule");this.lifecycleRules.push({...rule})}renderLifecyclePolicy(){const stack=core_1.Stack.of(this);let lifecyclePolicyText;if(!(this.lifecycleRules.length===0&&!this.registryId))return this.lifecycleRules.length>0&&(lifecyclePolicyText=JSON.stringify(stack.resolve({rules:this.orderedLifecycleRules().map(renderLifecycleRule)}))),{lifecyclePolicyText,registryId:this.registryId}}orderedLifecycleRules(){if(this.lifecycleRules.length===0)return[];const prioritizedRules=this.lifecycleRules.filter(r=>r.rulePriority!==void 0&&r.tagStatus!==lifecycle_1.TagStatus.ANY),autoPrioritizedRules=this.lifecycleRules.filter(r=>r.rulePriority===void 0&&r.tagStatus!==lifecycle_1.TagStatus.ANY),anyRules=this.lifecycleRules.filter(r=>r.tagStatus===lifecycle_1.TagStatus.ANY);if(anyRules.length>0&&anyRules[0].rulePriority!==void 0&&autoPrioritizedRules.length>0)throw new Error("Cannot combine prioritized TagStatus.Any rule with unprioritized rules. Remove rulePriority from the 'Any' rule.");const prios=prioritizedRules.map(r=>r.rulePriority);let autoPrio=(prios.length>0?Math.max(...prios):0)+1;const ret=new Array;for(const rule of prioritizedRules.concat(autoPrioritizedRules).concat(anyRules))ret.push({...rule,rulePriority:rule.rulePriority??autoPrio++});return validateAnyRuleLast(ret),ret}parseEncryption(props){const encryptionType=props.encryption??(props.encryptionKey?RepositoryEncryption.KMS:RepositoryEncryption.AES_256);if(encryptionType!==RepositoryEncryption.KMS&&props.encryptionKey)throw new Error(`encryptionKey is specified, so 'encryption' must be set to KMS (value: ${encryptionType.value})`);if(encryptionType!==RepositoryEncryption.AES_256){if(encryptionType===RepositoryEncryption.KMS)return{encryptionType:"KMS",kmsKey:props.encryptionKey?.keyArn};throw new Error(`Unexpected 'encryptionType': ${encryptionType}`)}}enableAutoDeleteImages(){const provider=core_1.CustomResourceProvider.getOrCreateProvider(this,AUTO_DELETE_IMAGES_RESOURCE_TYPE,{codeDirectory:path.join(__dirname,"auto-delete-images-handler"),runtime:core_1.CustomResourceProviderRuntime.NODEJS_14_X,description:`Lambda function for auto-deleting images in ${this.repositoryName} repository.`,policyStatements:[{Effect:"Allow",Action:["ecr:BatchDeleteImage","ecr:DescribeRepositories","ecr:ListImages","ecr:ListTagsForResource"],Resource:[this._resource.attrArn]}]});new core_1.CustomResource(this,"AutoDeleteImagesCustomResource",{resourceType:AUTO_DELETE_IMAGES_RESOURCE_TYPE,serviceToken:provider.serviceToken,properties:{RepositoryName:core_1.Lazy.any({produce:()=>this.repositoryName})}}).node.addDependency(this),core_1.Tags.of(this._resource).add(AUTO_DELETE_IMAGES_TAG,"true")}}exports.Repository=Repository,_b=JSII_RTTI_SYMBOL_1,Repository[_b]={fqn:"aws-cdk-lib.aws_ecr.Repository",version:"2.70.0"};function validateAnyRuleLast(rules){const anyRules=rules.filter(r=>r.tagStatus===lifecycle_1.TagStatus.ANY);if(anyRules.length===1){const maxPrio=Math.max(...rules.map(r=>r.rulePriority));if(anyRules[0].rulePriority!==maxPrio)throw new Error(`TagStatus.Any rule must have highest priority, has ${anyRules[0].rulePriority} which is smaller than ${maxPrio}`)}}function renderLifecycleRule(rule){return{rulePriority:rule.rulePriority,description:rule.description,selection:{tagStatus:rule.tagStatus||lifecycle_1.TagStatus.ANY,tagPrefixList:rule.tagPrefixList,countType:rule.maxImageAge!==void 0?"sinceImagePushed":"imageCountMoreThan",countNumber:rule.maxImageAge?.toDays()??rule.maxImageCount,countUnit:rule.maxImageAge!==void 0?"days":void 0},action:{type:"expire"}}}var TagMutability;(function(TagMutability2){TagMutability2.MUTABLE="MUTABLE",TagMutability2.IMMUTABLE="IMMUTABLE"})(TagMutability=exports.TagMutability||(exports.TagMutability={}));class RepositoryEncryption{constructor(value){this.value=value}}exports.RepositoryEncryption=RepositoryEncryption,_c=JSII_RTTI_SYMBOL_1,RepositoryEncryption[_c]={fqn:"aws-cdk-lib.aws_ecr.RepositoryEncryption",version:"2.70.0"},RepositoryEncryption.AES_256=new RepositoryEncryption("AES256"),RepositoryEncryption.KMS=new RepositoryEncryption("KMS");