@@ -21,12 +21,14 @@ import { FunctionConfigError } from './errors';
2121import {
2222 getAnnotation ,
2323 addAnnotation ,
24+ removeAnnotation ,
2425 SOURCE_PATH_ANNOTATION ,
2526 SOURCE_INDEX_ANNOTATION ,
2627 ID_ANNOTATION ,
2728 LEGACY_SOURCE_PATH_ANNOTATION ,
2829 LEGACY_SOURCE_INDEX_ANNOTATION ,
2930 LEGACY_ID_ANNOTATION ,
31+ ANNOTATION_PREFIX ,
3032} from './metadata' ;
3133
3234const INVOCATIONS = `
@@ -65,6 +67,9 @@ Example invocations:
6567 This is a convenient way to populate the functionConfig if it's a ConfigMap.
6668` ;
6769
70+ const RESOURCE_ID_ANNOTATION =
71+ 'internal.kubernetes.io/annotations-migration-resource-id' ;
72+
6873enum ExitCode {
6974 RESULT_ERROR = 1 ,
7075 EXCEPTION_ERROR ,
@@ -169,7 +174,7 @@ Use this ONLY if the function accepts a ConfigMap.`,
169174
170175export async function runFnWithConfigs ( fn : KptFunc , configs : Configs ) {
171176 // Save the original annotation values.
172- const m = recordOriginalAnnotation ( configs ) ;
177+ const m = preprocessResourceForInternalAnnotationsMigration ( configs ) ;
173178
174179 // Run the function.
175180 await fn ( configs ) ;
@@ -178,96 +183,249 @@ export async function runFnWithConfigs(fn: KptFunc, configs: Configs) {
178183 reconcileAnnotations ( configs , m ) ;
179184}
180185
181- class filePathAndIndex {
182- constructor ( path : string | undefined , index : string | undefined ) {
183- this . path = path ;
184- this . index = index ;
186+ function getInternalAnnotations ( obj : KubernetesObject ) : {
187+ [ key : string ] : string ;
188+ } {
189+ const m : { [ index : string ] : string } = { } ;
190+ if ( ! obj . metadata ) {
191+ return m ;
185192 }
186-
187- path : string | undefined ;
188- index : string | undefined ;
193+ if ( ! obj . metadata . annotations ) {
194+ return m ;
195+ }
196+ for ( const key in obj . metadata . annotations ) {
197+ if (
198+ key . startsWith ( ANNOTATION_PREFIX ) ||
199+ key === LEGACY_SOURCE_PATH_ANNOTATION ||
200+ key === LEGACY_SOURCE_INDEX_ANNOTATION ||
201+ key === LEGACY_ID_ANNOTATION
202+ ) {
203+ m [ key ] = obj . metadata . annotations [ key ] ;
204+ }
205+ }
206+ return m ;
189207}
190208
191- function recordOriginalAnnotation (
209+ function preprocessResourceForInternalAnnotationsMigration (
192210 configs : Configs
193- ) : Map < string , filePathAndIndex > {
211+ ) : Map < string , { [ key : string ] : string } > {
194212 const m = new Map ( ) ;
195- for ( var obj of configs . getAll ( ) ) {
196- let idAnno = getAnnotation ( obj , ID_ANNOTATION ) ;
197- if ( ! idAnno ) {
198- idAnno = getAnnotation ( obj , LEGACY_ID_ANNOTATION ) ;
199- }
200- if ( ! idAnno ) {
213+ let id = 0 ;
214+ for ( const obj of configs . getAll ( ) ) {
215+ const idStr = id . toString ( ) ;
216+ addAnnotation ( obj , RESOURCE_ID_ANNOTATION , idStr ) ;
217+ m . set ( idStr , getInternalAnnotations ( obj ) ) ;
218+ id ++ ;
219+
220+ checkMismatchAnnos ( obj . metadata . annotations ) ;
221+ }
222+ return m ;
223+ }
224+
225+ function checkMismatchAnnos (
226+ annotations : { [ key : string ] : string } | undefined
227+ ) {
228+ if ( ! annotations ) {
229+ return ;
230+ }
231+ const path = annotations [ SOURCE_PATH_ANNOTATION ] ;
232+ const legacyPath = annotations [ LEGACY_SOURCE_PATH_ANNOTATION ] ;
233+ if ( path && legacyPath && path !== legacyPath ) {
234+ throw new AnnotationsValueMismatchError ( ) ;
235+ }
236+
237+ const index = annotations [ SOURCE_INDEX_ANNOTATION ] ;
238+ const legacyIndex = annotations [ LEGACY_SOURCE_INDEX_ANNOTATION ] ;
239+ if ( index && legacyIndex && index !== legacyIndex ) {
240+ throw new AnnotationsValueMismatchError ( ) ;
241+ }
242+
243+ const id = annotations [ ID_ANNOTATION ] ;
244+ const legacyId = annotations [ LEGACY_ID_ANNOTATION ] ;
245+ if ( id && legacyId && id !== legacyId ) {
246+ throw new AnnotationsValueMismatchError ( ) ;
247+ }
248+ }
249+
250+ // determineAnnotationFormat returns 2 values:
251+ // - if the internal format should be used.
252+ // - if the legacy format should be used.
253+ function determineAnnotationFormat (
254+ m : Map < string , { [ key : string ] : string } >
255+ ) : [ boolean , boolean ] {
256+ if ( m . size === 0 ) {
257+ return [ true , true ] ;
258+ }
259+ let internal : boolean | undefined , legacy : boolean | undefined ;
260+ for ( const [ , annotations ] of m ) {
261+ const path = annotations [ SOURCE_PATH_ANNOTATION ] ;
262+ const index = annotations [ SOURCE_INDEX_ANNOTATION ] ;
263+ const id = annotations [ ID_ANNOTATION ] ;
264+ const legacyPath = annotations [ LEGACY_SOURCE_PATH_ANNOTATION ] ;
265+ const legacyIndex = annotations [ LEGACY_SOURCE_INDEX_ANNOTATION ] ;
266+ const legacyId = annotations [ LEGACY_ID_ANNOTATION ] ;
267+
268+ if ( ! ( path || index || id || legacyPath || legacyIndex || legacyId ) ) {
201269 continue ;
202270 }
203- addAnnotation ( obj , ID_ANNOTATION , idAnno ) ;
204- addAnnotation ( obj , LEGACY_ID_ANNOTATION , idAnno ) ;
205271
206- let pathAnno = getAnnotation ( obj , SOURCE_PATH_ANNOTATION ) ;
207- if ( ! pathAnno ) {
208- pathAnno = getAnnotation ( obj , LEGACY_SOURCE_PATH_ANNOTATION ) ;
272+ const foundOneOf =
273+ path !== undefined || index !== undefined || id !== undefined ;
274+ if ( ! internal ) {
275+ internal = foundOneOf ;
209276 }
210- if ( pathAnno ) {
211- addAnnotation ( obj , SOURCE_PATH_ANNOTATION , pathAnno ) ;
212- addAnnotation ( obj , LEGACY_SOURCE_PATH_ANNOTATION , pathAnno ) ;
277+ if ( ( foundOneOf && ! internal ) || ( ! foundOneOf && internal ) ) {
278+ throw new AnnotationsFormatMismatchError ( ) ;
213279 }
214280
215- let indexAnno = getAnnotation ( obj , SOURCE_INDEX_ANNOTATION ) ;
216- if ( ! indexAnno ) {
217- indexAnno = getAnnotation ( obj , LEGACY_SOURCE_INDEX_ANNOTATION ) ;
281+ const foundOneOfLegacy =
282+ legacyPath !== undefined ||
283+ legacyIndex !== undefined ||
284+ legacyId !== undefined ;
285+ if ( ! legacy ) {
286+ legacy = foundOneOfLegacy ;
218287 }
219- if ( indexAnno ) {
220- addAnnotation ( obj , SOURCE_INDEX_ANNOTATION , indexAnno ) ;
221- addAnnotation ( obj , LEGACY_SOURCE_INDEX_ANNOTATION , indexAnno ) ;
288+ if ( ( foundOneOfLegacy && ! legacy ) || ( ! foundOneOfLegacy && legacy ) ) {
289+ throw new AnnotationsFormatMismatchError ( ) ;
222290 }
291+ }
292+ if ( internal !== undefined && legacy !== undefined ) {
293+ return [ internal , legacy ] ;
294+ }
295+ return [ true , true ] ;
296+ }
297+
298+ function setMissingAnnotations ( obj : KubernetesObject ) {
299+ setMissingAnnotation (
300+ obj ,
301+ SOURCE_PATH_ANNOTATION ,
302+ LEGACY_SOURCE_PATH_ANNOTATION
303+ ) ;
304+ setMissingAnnotation (
305+ obj ,
306+ SOURCE_INDEX_ANNOTATION ,
307+ LEGACY_SOURCE_INDEX_ANNOTATION
308+ ) ;
309+ setMissingAnnotation ( obj , ID_ANNOTATION , LEGACY_ID_ANNOTATION ) ;
310+ }
223311
224- m . set ( idAnno , new filePathAndIndex ( pathAnno , indexAnno ) ) ;
312+ function setMissingAnnotation (
313+ obj : KubernetesObject ,
314+ internalKey : string ,
315+ legacyKey : string
316+ ) {
317+ const internalVal = getAnnotation ( obj , internalKey ) ;
318+ const legacyVal = getAnnotation ( obj , legacyKey ) ;
319+ if ( ! internalVal && ! legacyVal ) {
320+ return ;
321+ } else if ( ! internalVal && legacyVal ) {
322+ addAnnotation ( obj , internalKey , legacyVal ) ;
323+ } else if ( ! legacyVal && internalVal ) {
324+ addAnnotation ( obj , legacyKey , internalVal ) ;
225325 }
226- return m ;
227326}
228327
229- function reconcileAnnotations (
230- configs : Configs ,
231- m : Map < string , filePathAndIndex >
328+ function checkAnnotationsAltered (
329+ obj : KubernetesObject ,
330+ idToAnnos : Map < string , { [ key : string ] : string } >
232331) {
233- // for (var obj of configs.getAll()) {
234- configs . getAll ( ) . forEach ( function ( obj ) {
235- let idAnno = getAnnotation ( obj , ID_ANNOTATION ) ;
236- if ( ! idAnno ) {
237- idAnno = getAnnotation ( obj , LEGACY_ID_ANNOTATION ) ;
238- }
239- if ( ! idAnno ) {
240- return ;
241- }
332+ const path = getAnnotation ( obj , SOURCE_PATH_ANNOTATION ) ;
333+ const index = getAnnotation ( obj , SOURCE_INDEX_ANNOTATION ) ;
334+ const legacyPath = getAnnotation ( obj , LEGACY_SOURCE_PATH_ANNOTATION ) ;
335+ const legacyIndex = getAnnotation ( obj , LEGACY_SOURCE_INDEX_ANNOTATION ) ;
336+
337+ const rid = getAnnotation ( obj , RESOURCE_ID_ANNOTATION ) ;
338+ if ( ! rid ) {
339+ return ;
340+ }
341+ const originalAnnotations = idToAnnos . get ( rid ) ;
342+ if ( ! originalAnnotations ) {
343+ return ;
344+ }
242345
243- const originalPathIndex = m . get ( idAnno ) ;
244- if ( ! originalPathIndex ) {
245- return ;
246- }
247- const origPath = originalPathIndex . path ;
248- const origIndex = originalPathIndex . index ;
249-
250- // Infer the user's intend by comparing if there are changes to either the
251- // new annotation or the legacy annotation.
252- const pathAnno = getAnnotation ( obj , SOURCE_PATH_ANNOTATION ) ;
253- const legacyPathAnno = getAnnotation ( obj , LEGACY_SOURCE_PATH_ANNOTATION ) ;
254- if ( pathAnno && ( pathAnno != origPath || ! legacyPathAnno ) ) {
255- addAnnotation ( obj , LEGACY_SOURCE_PATH_ANNOTATION , pathAnno ) ;
256- } else if ( legacyPathAnno && ( legacyPathAnno != origPath || ! pathAnno ) ) {
257- addAnnotation ( obj , SOURCE_PATH_ANNOTATION , legacyPathAnno ) ;
346+ let originalPath = originalAnnotations [ SOURCE_PATH_ANNOTATION ] ;
347+ if ( ! originalPath ) {
348+ originalPath = originalAnnotations [ LEGACY_SOURCE_PATH_ANNOTATION ] ;
349+ }
350+ if ( originalPath ) {
351+ if (
352+ path &&
353+ legacyPath &&
354+ originalPath !== path &&
355+ originalPath !== legacyPath &&
356+ path !== legacyPath
357+ ) {
358+ throw new AnnotationsValueMismatchError ( ) ;
359+ } else if ( path && originalPath !== path ) {
360+ addAnnotation ( obj , LEGACY_SOURCE_PATH_ANNOTATION , path ) ;
361+ } else if ( legacyPath && originalPath !== legacyPath ) {
362+ addAnnotation ( obj , SOURCE_PATH_ANNOTATION , legacyPath ) ;
258363 }
364+ }
259365
260- const indexAnno = getAnnotation ( obj , SOURCE_INDEX_ANNOTATION ) ;
261- const legacyIndexAnno = getAnnotation ( obj , LEGACY_SOURCE_INDEX_ANNOTATION ) ;
262- if ( indexAnno && ( indexAnno != origIndex || ! legacyIndexAnno ) ) {
263- addAnnotation ( obj , LEGACY_SOURCE_INDEX_ANNOTATION , indexAnno ) ;
264- } else if (
265- legacyIndexAnno &&
266- ( legacyIndexAnno != origIndex || ! indexAnno )
366+ let originalIndex = originalAnnotations [ SOURCE_INDEX_ANNOTATION ] ;
367+ if ( ! originalIndex ) {
368+ originalIndex = originalAnnotations [ LEGACY_SOURCE_INDEX_ANNOTATION ] ;
369+ }
370+ if ( originalIndex ) {
371+ if (
372+ index &&
373+ legacyIndex &&
374+ originalIndex !== index &&
375+ originalIndex !== legacyIndex &&
376+ index !== legacyIndex
267377 ) {
268- addAnnotation ( obj , SOURCE_INDEX_ANNOTATION , legacyIndexAnno ) ;
378+ throw new AnnotationsValueMismatchError ( ) ;
379+ } else if ( index && originalIndex !== index ) {
380+ addAnnotation ( obj , LEGACY_SOURCE_INDEX_ANNOTATION , index ) ;
381+ } else if ( legacyIndex && originalIndex !== legacyIndex ) {
382+ addAnnotation ( obj , SOURCE_INDEX_ANNOTATION , legacyIndex ) ;
269383 }
270- } ) ;
384+ }
385+ }
386+
387+ function formatInternalAnnotations (
388+ obj : KubernetesObject ,
389+ useInternal : boolean ,
390+ useLegacy : boolean
391+ ) {
392+ if ( ! useInternal ) {
393+ removeAnnotation ( obj , SOURCE_PATH_ANNOTATION ) ;
394+ removeAnnotation ( obj , SOURCE_INDEX_ANNOTATION ) ;
395+ removeAnnotation ( obj , ID_ANNOTATION ) ;
396+ }
397+ if ( ! useLegacy ) {
398+ removeAnnotation ( obj , LEGACY_SOURCE_PATH_ANNOTATION ) ;
399+ removeAnnotation ( obj , LEGACY_SOURCE_INDEX_ANNOTATION ) ;
400+ removeAnnotation ( obj , LEGACY_ID_ANNOTATION ) ;
401+ }
402+ }
403+
404+ function reconcileAnnotations (
405+ configs : Configs ,
406+ m : Map < string , { [ key : string ] : string } >
407+ ) {
408+ const [ useInternal , useLegacy ] = determineAnnotationFormat ( m ) ;
409+
410+ for ( const obj of configs . getAll ( ) ) {
411+ setMissingAnnotations ( obj ) ;
412+ checkAnnotationsAltered ( obj , m ) ;
413+ formatInternalAnnotations ( obj , useInternal , useLegacy ) ;
414+ checkMismatchAnnos ( obj . metadata . annotations ) ;
415+ removeAnnotation ( obj , RESOURCE_ID_ANNOTATION ) ;
416+ }
417+ }
418+
419+ class AnnotationsValueMismatchError extends Error {
420+ constructor ( ) {
421+ super ( 'the legacy and internal annotation values mismatch' ) ;
422+ }
423+ }
424+
425+ class AnnotationsFormatMismatchError extends Error {
426+ constructor ( ) {
427+ super ( 'the legacy and internal annotation formats mismatch' ) ;
428+ }
271429}
272430
273431class ResultError extends Error {
0 commit comments