1
1
import { EOL } from 'os' ;
2
+ import * as path from 'path' ;
2
3
import * as events from '@aws-cdk/aws-events' ;
3
4
import * as iam from '@aws-cdk/aws-iam' ;
4
5
import * as kms from '@aws-cdk/aws-kms' ;
5
- import { ArnFormat , IResource , Lazy , RemovalPolicy , Resource , Stack , Tags , Token , TokenComparison } from '@aws-cdk/core' ;
6
+ import {
7
+ ArnFormat ,
8
+ IResource ,
9
+ Lazy ,
10
+ RemovalPolicy ,
11
+ Resource ,
12
+ Stack ,
13
+ Tags ,
14
+ Token ,
15
+ TokenComparison ,
16
+ CustomResource ,
17
+ CustomResourceProvider ,
18
+ CustomResourceProviderRuntime ,
19
+ } from '@aws-cdk/core' ;
6
20
import { IConstruct , Construct } from 'constructs' ;
7
21
import { CfnRepository } from './ecr.generated' ;
8
22
import { LifecycleRule , TagStatus } from './lifecycle' ;
9
23
24
+ const AUTO_DELETE_IMAGES_RESOURCE_TYPE = 'Custom::ECRAutoDeleteImages' ;
25
+ const AUTO_DELETE_IMAGES_TAG = 'aws-cdk:auto-delete-images' ;
26
+
10
27
/**
11
28
* Represents an ECR repository.
12
29
*/
@@ -479,6 +496,16 @@ export interface RepositoryProps {
479
496
* @default TagMutability.MUTABLE
480
497
*/
481
498
readonly imageTagMutability ?: TagMutability ;
499
+
500
+ /**
501
+ * Whether all images should be automatically deleted when the repository is
502
+ * removed from the stack or when the stack is deleted.
503
+ *
504
+ * Requires the `removalPolicy` to be set to `RemovalPolicy.DESTROY`.
505
+ *
506
+ * @default false
507
+ */
508
+ readonly autoDeleteImages ?: boolean ;
482
509
}
483
510
484
511
export interface RepositoryAttributes {
@@ -589,6 +616,7 @@ export class Repository extends RepositoryBase {
589
616
private readonly lifecycleRules = new Array < LifecycleRule > ( ) ;
590
617
private readonly registryId ?: string ;
591
618
private policyDocument ?: iam . PolicyDocument ;
619
+ private readonly _resource : CfnRepository ;
592
620
593
621
constructor ( scope : Construct , id : string , props : RepositoryProps = { } ) {
594
622
super ( scope , id , {
@@ -606,6 +634,14 @@ export class Repository extends RepositoryBase {
606
634
imageTagMutability : props . imageTagMutability || undefined ,
607
635
encryptionConfiguration : this . parseEncryption ( props ) ,
608
636
} ) ;
637
+ this . _resource = resource ;
638
+
639
+ if ( props . autoDeleteImages ) {
640
+ if ( props . removalPolicy !== RemovalPolicy . DESTROY ) {
641
+ throw new Error ( 'Cannot use \'autoDeleteImages\' property on a repository without setting removal policy to \'DESTROY\'.' ) ;
642
+ }
643
+ this . enableAutoDeleteImages ( ) ;
644
+ }
609
645
610
646
resource . applyRemovalPolicy ( props . removalPolicy ) ;
611
647
@@ -741,6 +777,44 @@ export class Repository extends RepositoryBase {
741
777
742
778
throw new Error ( `Unexpected 'encryptionType': ${ encryptionType } ` ) ;
743
779
}
780
+
781
+ private enableAutoDeleteImages ( ) {
782
+ // Use a iam policy to allow the custom resource to list & delete
783
+ // images in the repository and the ability to get all repositories to find the arn needed on delete.
784
+ const provider = CustomResourceProvider . getOrCreateProvider ( this , AUTO_DELETE_IMAGES_RESOURCE_TYPE , {
785
+ codeDirectory : path . join ( __dirname , 'auto-delete-images-handler' ) ,
786
+ runtime : CustomResourceProviderRuntime . NODEJS_14_X ,
787
+ description : `Lambda function for auto-deleting images in ${ this . repositoryName } repository.` ,
788
+ policyStatements : [
789
+ {
790
+ Effect : 'Allow' ,
791
+ Action : [
792
+ 'ecr:BatchDeleteImage' ,
793
+ 'ecr:DescribeRepositories' ,
794
+ 'ecr:ListImages' ,
795
+ 'ecr:ListTagsForResource' ,
796
+ ] ,
797
+ Resource : [ this . _resource . attrArn ] ,
798
+ } ,
799
+ ] ,
800
+ } ) ;
801
+
802
+ const customResource = new CustomResource ( this , 'AutoDeleteImagesCustomResource' , {
803
+ resourceType : AUTO_DELETE_IMAGES_RESOURCE_TYPE ,
804
+ serviceToken : provider . serviceToken ,
805
+ properties : {
806
+ RepositoryName : Lazy . any ( { produce : ( ) => this . repositoryName } ) ,
807
+ } ,
808
+ } ) ;
809
+ customResource . node . addDependency ( this ) ;
810
+
811
+ // We also tag the repository to record the fact that we want it autodeleted.
812
+ // The custom resource will check this tag before actually doing the delete.
813
+ // Because tagging and untagging will ALWAYS happen before the CR is deleted,
814
+ // we can set `autoDeleteImages: false` without the removal of the CR emptying
815
+ // the repository as a side effect.
816
+ Tags . of ( this . _resource ) . add ( AUTO_DELETE_IMAGES_TAG , 'true' ) ;
817
+ }
744
818
}
745
819
746
820
function validateAnyRuleLast ( rules : LifecycleRule [ ] ) {
0 commit comments