@@ -26,32 +26,31 @@ function getAllImages(ecr, registryId, repoName) {
2626 maxResults : 100
2727 } ;
2828
29- /**
30- * 'Follows' the result pagination in a sort of reduce method in which the results are continuely added to an array
31- */
32- function followPages ( ecrSvc , allImages , data ) {
33- const combinedImages = allImages . length === 0 ? [ ...data . imageDetails ] : [ ...allImages , ...data . imageDetails ] ;
34-
35- if ( data . nextToken ) {
36- params . nextToken = data . nextToken ;
37- return ecrSvc . describeImages ( params )
38- . promise ( )
39- . then ( res => followPages ( ecrSvc , combinedImages , res ) ) ;
40- }
41-
42- return Promise . resolve ( combinedImages ) ;
43- }
44-
4529 return ecr . describeImages ( params ) . promise ( )
46- . then ( data => followPages ( ecr , [ ] , data ) ) ;
30+ . then ( data => Promise . resolve ( data . imageDetails ) ) ;
4731}
4832
49- function buildReport ( isDryRun , reposNotFound , reposWithUntaggedImages , reposWithDeletedImages ) {
33+ function buildReport (
34+ isDryRun ,
35+ reposNotFound ,
36+ reposWithUntaggedImages ,
37+ reposWithDeletedImagesDryRun ,
38+ reposWithDeletedImages ,
39+ reposWithImagesThatFailedToDelete
40+ ) {
5041 const untaggedRepoKeys = Object . keys ( reposWithUntaggedImages ) ;
42+ const deletedDryRunRepoKeys = Object . keys ( reposWithDeletedImagesDryRun ) ;
5143 const deletedRepoKeys = Object . keys ( reposWithDeletedImages ) ;
52-
53- if ( reposNotFound . length === 0 && untaggedRepoKeys . length === 0 && deletedRepoKeys . length === 0 ) {
54- return Promise . resolve ( 'Robin ran but there was no vigilamnte justice was needed' ) ;
44+ const failedToDeletedRepoKeys = Object . keys ( reposWithImagesThatFailedToDelete ) ;
45+
46+ if (
47+ reposNotFound . length === 0
48+ && untaggedRepoKeys . length === 0
49+ && deletedDryRunRepoKeys === 0
50+ && deletedRepoKeys . length === 0
51+ && failedToDeletedRepoKeys === 0
52+ ) {
53+ return 'Robin ran but there no vigilamnte justice was needed' ;
5554 }
5655
5756 const backticks = str => ( `\`${ str } \`` ) ;
@@ -60,32 +59,59 @@ function buildReport(isDryRun, reposNotFound, reposWithUntaggedImages, reposWith
6059 let text = 'Robin has attempted to clean up the streets!' ;
6160
6261 if ( reposNotFound . length !== 0 ) {
63- text += '\n\n\n====================================' ;
62+ text += '\n\n\n=================================================== ' ;
6463 text += `\nRepositories not found (${ reposNotFound . length } )${ dryRunText } ` ;
65- text += '\n====================================' ;
64+ text += '\n=================================================== ' ;
6665 reposNotFound . forEach ( repoName => {
6766 text += `\n${ backticks ( repoName ) } ` ;
6867 } ) ;
6968 }
7069
7170 if ( untaggedRepoKeys . length !== 0 ) {
72- text += '\n\n\n====================================' ;
71+ text += '\n\n\n=================================================== ' ;
7372 text += `\nRepositories with untagged images (${ untaggedRepoKeys . length } )${ dryRunText } ` ;
74- text += '\n====================================' ;
73+ text += '\n=================================================== ' ;
7574 untaggedRepoKeys . forEach ( repoName => {
7675 text += `\n${ backticks ( repoName ) } - ${ reposWithUntaggedImages [ repoName ] } image${ reposWithUntaggedImages [ repoName ] > 1 ? 's' : '' } ` ;
7776 } ) ;
7877 }
7978
80- if ( deletedRepoKeys . length !== 0 ) {
81- text += '\n\n\n====================================' ;
82- text += `\nRepositories with images deleted (${ deletedRepoKeys . length } )${ dryRunText } ` ;
83- text += '\n====================================' ;
84- deletedRepoKeys . forEach ( repoName => {
85- text += `\n${ backticks ( repoName ) } (${ reposWithDeletedImages [ repoName ] . length } tags): ${ reposWithDeletedImages [ repoName ] . join ( ', ' ) } ` ;
86- } ) ;
79+ if ( isDryRun ) {
80+ text += '\n\n\n===================================================' ;
81+ text += `\nRepositories with images deleted (${ deletedDryRunRepoKeys . length } )${ dryRunText } ` ;
82+ text += '\n===================================================' ;
83+ if ( deletedDryRunRepoKeys . length === 0 ) {
84+ text += '\nNo images deleted' ;
85+ } else {
86+ deletedDryRunRepoKeys . forEach ( repoName => {
87+ // eslint-disable-next-line max-len
88+ text += `\n${ backticks ( repoName ) } (${ reposWithDeletedImagesDryRun [ repoName ] . length } tags): ${ reposWithDeletedImagesDryRun [ repoName ] . join ( ', ' ) } ` ;
89+ } ) ;
90+ }
91+ } else {
92+ text += '\n\n\n===================================================' ;
93+ text += `\nRepositories with images deleted (${ deletedRepoKeys . length } )` ;
94+ text += '\n===================================================' ;
95+ if ( deletedRepoKeys . length === 0 ) {
96+ text += '\nNo images deleted' ;
97+ } else {
98+ deletedRepoKeys . forEach ( repoName => {
99+ text += `\n${ backticks ( repoName ) } (${ reposWithDeletedImages [ repoName ] . length } tags): ${ reposWithDeletedImages [ repoName ] . join ( ', ' ) } ` ;
100+ } ) ;
101+ }
102+
103+ if ( failedToDeletedRepoKeys . length !== 0 ) {
104+ text += '\n\n\n===================================================' ;
105+ text += `\nRepositories with images that failed deleted (${ failedToDeletedRepoKeys . length } )` ;
106+ text += '\n===================================================' ;
107+ deletedRepoKeys . forEach ( repoName => {
108+ // eslint-disable-next-line max-len
109+ text += `\n${ backticks ( repoName ) } (${ reposWithImagesThatFailedToDelete [ repoName ] . length } tags): ${ reposWithImagesThatFailedToDelete [ repoName ] . join ( ', ' ) } ` ;
110+ } ) ;
111+ }
87112 }
88113
114+
89115 return text ;
90116}
91117
@@ -107,6 +133,8 @@ module.exports.cleanupImages = (event, context, callback) => {
107133 const reposNotFound = [ ] ;
108134 const reposWithUntaggedImages = { } ;
109135 const reposWithDeletedImages = { } ;
136+ const reposWithDeletedImagesDryRun = { } ;
137+ const reposWithImagesThatFailedToDelete = { } ;
110138
111139 console . log ( 'Robin is dealing out some of his own justice...' ) ;
112140 console . log ( 'Robin is using ECR Region: ' , ecrRegion ) ;
@@ -147,28 +175,47 @@ module.exports.cleanupImages = (event, context, callback) => {
147175 console . log ( 'Images to delete: ' , toDelete ) ;
148176
149177 const convertedToDelete = toDelete . map ( image => {
150- if ( typeof reposWithDeletedImages [ repoName ] === 'undefined' ) {
151- reposWithDeletedImages [ repoName ] = [ ] ;
178+ if ( isDryRun ) {
179+ image . imageTags . forEach ( tag => {
180+ if ( typeof reposWithDeletedImagesDryRun [ repoName ] === 'undefined' ) {
181+ reposWithDeletedImagesDryRun [ repoName ] = [ ] ;
182+ }
183+ reposWithDeletedImagesDryRun [ repoName ] . push ( tag ) ;
184+ } ) ;
152185 }
153186
154- image . imageTags . forEach ( tag => {
155- reposWithDeletedImages [ repoName ] . push ( tag ) ;
156- } ) ;
157-
158187 return { imageDigest : image . imageDigest } ;
159188 } ) ;
160189
190+ if ( isDryRun ) {
191+ return Promise . resolve ( { imageIds : [ ] , failures : [ ] } ) ;
192+ }
193+
161194 const deleteParams = {
162195 registryId : registry ,
163196 repositoryName : repoName ,
164197 imageIds : convertedToDelete
165198 } ;
166199
167- if ( isDryRun ) {
168- return Promise . resolve ( { imageIds : [ ] , failures : [ ] } ) ;
169- }
170-
171- return ecr . batchDeleteImage ( deleteParams ) . promise ( ) ;
200+ return ecr . batchDeleteImage ( deleteParams ) . promise ( )
201+ . then ( ( { failures, imageIds } ) => {
202+ console . log ( 'failures: ' , failures ) ;
203+ console . log ( 'imageIds: ' , imageIds ) ;
204+
205+ failures . forEach ( ( { imageId } ) => {
206+ if ( typeof reposWithImagesThatFailedToDelete [ repoName ] === 'undefined' ) {
207+ reposWithImagesThatFailedToDelete [ repoName ] = [ ] ;
208+ }
209+ reposWithImagesThatFailedToDelete [ repoName ] . push ( imageId . imageTag ) ;
210+ } ) ;
211+
212+ imageIds . forEach ( ( { imageTag } ) => {
213+ if ( typeof reposWithDeletedImages [ repoName ] === 'undefined' ) {
214+ reposWithDeletedImages [ repoName ] = [ ] ;
215+ }
216+ reposWithDeletedImages [ repoName ] . push ( imageTag ) ;
217+ } ) ;
218+ } ) ;
172219 } )
173220 . catch ( err => {
174221 if ( err . code === 'RepositoryNotFoundException' && reposNotFound . indexOf ( repoName ) === - 1 ) {
@@ -181,7 +228,14 @@ module.exports.cleanupImages = (event, context, callback) => {
181228
182229 return Promise . all ( promises )
183230 . then ( ( ) => {
184- const reportText = buildReport ( isDryRun , reposNotFound , reposWithUntaggedImages , reposWithDeletedImages ) ;
231+ const reportText = buildReport (
232+ isDryRun ,
233+ reposNotFound ,
234+ reposWithUntaggedImages ,
235+ reposWithDeletedImagesDryRun ,
236+ reposWithDeletedImages ,
237+ reposWithImagesThatFailedToDelete
238+ ) ;
185239
186240 // Log Results
187241 console . log ( reportText . replace ( / ` / g, '' ) ) ; // strip backticks when logging to CloudWatch (backticks are for Slack!)
0 commit comments