diff --git a/docs/java.md b/docs/java.md index 8706be4608..ec5850e394 100644 --- a/docs/java.md +++ b/docs/java.md @@ -205,6 +205,7 @@ import org.cdk8s.App; App.Builder.create() // .outdir(java.lang.String) // .outputFileExtension(java.lang.String) +// .recordConstructMetadata(java.lang.Boolean) // .yamlOutputType(YamlOutputType) .build(); ``` @@ -227,6 +228,15 @@ The file extension to use for rendered YAML files. --- +##### `recordConstructMetadata`Optional + +- *Type:* `java.lang.Boolean` +- *Default:* false + +When set to true, the output directory will contain a `construct-metadata.json` file that holds construct related metadata on every resource in the app. + +--- + ##### `yamlOutputType`Optional - *Type:* [`org.cdk8s.YamlOutputType`](#org.cdk8s.YamlOutputType) @@ -818,6 +828,7 @@ import org.cdk8s.AppProps; AppProps.builder() // .outdir(java.lang.String) // .outputFileExtension(java.lang.String) +// .recordConstructMetadata(java.lang.Boolean) // .yamlOutputType(YamlOutputType) .build(); ``` @@ -848,6 +859,19 @@ The file extension to use for rendered YAML files. --- +##### `recordConstructMetadata`Optional + +```java +public java.lang.Boolean getRecordConstructMetadata(); +``` + +- *Type:* `java.lang.Boolean` +- *Default:* false + +When set to true, the output directory will contain a `construct-metadata.json` file that holds construct related metadata on every resource in the app. + +--- + ##### `yamlOutputType`Optional ```java diff --git a/docs/python.md b/docs/python.md index 4dee7ce439..2832aabe7e 100644 --- a/docs/python.md +++ b/docs/python.md @@ -213,6 +213,7 @@ import cdk8s cdk8s.App( outdir: str = None, output_file_extension: str = None, + record_construct_metadata: bool = None, yaml_output_type: YamlOutputType = None ) ``` @@ -235,6 +236,15 @@ The file extension to use for rendered YAML files. --- +##### `record_construct_metadata`Optional + +- *Type:* `bool` +- *Default:* false + +When set to true, the output directory will contain a `construct-metadata.json` file that holds construct related metadata on every resource in the app. + +--- + ##### `yaml_output_type`Optional - *Type:* [`cdk8s.YamlOutputType`](#cdk8s.YamlOutputType) @@ -840,6 +850,7 @@ import cdk8s cdk8s.AppProps( outdir: str = None, output_file_extension: str = None, + record_construct_metadata: bool = None, yaml_output_type: YamlOutputType = None ) ``` @@ -870,6 +881,19 @@ The file extension to use for rendered YAML files. --- +##### `record_construct_metadata`Optional + +```python +record_construct_metadata: bool +``` + +- *Type:* `bool` +- *Default:* false + +When set to true, the output directory will contain a `construct-metadata.json` file that holds construct related metadata on every resource in the app. + +--- + ##### `yaml_output_type`Optional ```python @@ -2818,6 +2842,7 @@ import cdk8s cdk8s.Testing.app( outdir: str = None, output_file_extension: str = None, + record_construct_metadata: bool = None, yaml_output_type: YamlOutputType = None ) ``` @@ -2840,6 +2865,15 @@ The file extension to use for rendered YAML files. --- +###### `record_construct_metadata`Optional + +- *Type:* `bool` +- *Default:* false + +When set to true, the output directory will contain a `construct-metadata.json` file that holds construct related metadata on every resource in the app. + +--- + ###### `yaml_output_type`Optional - *Type:* [`cdk8s.YamlOutputType`](#cdk8s.YamlOutputType) diff --git a/docs/typescript.md b/docs/typescript.md index 432c5faeaa..117a43e12c 100644 --- a/docs/typescript.md +++ b/docs/typescript.md @@ -717,6 +717,19 @@ The file extension to use for rendered YAML files. --- +##### `recordConstructMetadata`Optional + +```typescript +public readonly recordConstructMetadata: boolean; +``` + +- *Type:* `boolean` +- *Default:* false + +When set to true, the output directory will contain a `construct-metadata.json` file that holds construct related metadata on every resource in the app. + +--- + ##### `yamlOutputType`Optional ```typescript diff --git a/src/api-object.ts b/src/api-object.ts index 48ae38e2ef..10640830e9 100644 --- a/src/api-object.ts +++ b/src/api-object.ts @@ -136,6 +136,7 @@ export class ApiObject extends Construct { ...props.metadata?.labels, }, }); + } /** diff --git a/src/app.ts b/src/app.ts index 7f26bde13f..04ea890a30 100644 --- a/src/app.ts +++ b/src/app.ts @@ -36,6 +36,14 @@ export interface AppProps { * @default YamlOutputType.FILE_PER_CHART */ readonly yamlOutputType?: YamlOutputType; + + /** + * When set to true, the output directory will contain a `construct-metadata.json` file + * that holds construct related metadata on every resource in the app. + * + * @default false + */ + readonly recordConstructMetadata?: boolean; } /** @@ -99,6 +107,8 @@ export class App extends Construct { */ public readonly yamlOutputType: YamlOutputType; + private readonly recordConstructMetadata: boolean; + /** * Returns all the charts in this app, sorted topologically. */ @@ -118,6 +128,9 @@ export class App extends Construct { this.outdir = props.outdir ?? process.env.CDK8S_OUTDIR ?? 'dist'; this.outputFileExtension = props.outputFileExtension ?? '.k8s.yaml'; this.yamlOutputType = props.yamlOutputType ?? YamlOutputType.FILE_PER_CHART; + + this.recordConstructMetadata = props.recordConstructMetadata ?? (process.env.CDK8S_RECORD_CONSTRUCT_METADATA === 'true' ? true : false); + } /** @@ -154,11 +167,9 @@ export class App extends Construct { case YamlOutputType.FILE_PER_CHART: const namer: ChartNamer = hasDependantCharts ? new IndexedChartNamer() : new SimpleChartNamer(); - for (const chart of charts) { const chartName = namer.name(chart); const objects = chartToKube(chart); - Yaml.save(path.join(this.outdir, chartName+this.outputFileExtension), objects.map(obj => obj.toJson())); } break; @@ -199,6 +210,11 @@ export class App extends Construct { break; } + if (this.recordConstructMetadata) { + const allObjects = this.charts.flatMap(chartToKube); + this.writeConstructMetadata(allObjects); + } + } /** @@ -219,6 +235,17 @@ export class App extends Construct { return Yaml.stringify(...docs); } + + private writeConstructMetadata(apiObjects: ApiObject[]) { + const resources: { [key: string]: any } = {}; + for (const apiObject of apiObjects) { + resources[apiObject.name] = { path: Node.of(apiObject).path }; + } + fs.writeFileSync(path.join(this.outdir, 'construct-metadata.json'), JSON.stringify({ + version: '1.0.0', + resources: resources, + })); + } } function validate(app: App) { diff --git a/src/chart.ts b/src/chart.ts index 8027972374..e4adb3b35c 100644 --- a/src/chart.ts +++ b/src/chart.ts @@ -21,6 +21,7 @@ export interface ChartProps { * @default - no common labels */ readonly labels?: { [name: string]: string }; + } export class Chart extends Construct { diff --git a/test/chart.test.ts b/test/chart.test.ts index 154b9928ce..f4fe5d3c8e 100644 --- a/test/chart.test.ts +++ b/test/chart.test.ts @@ -1,3 +1,5 @@ +import * as fs from 'fs'; +import * as path from 'path'; import { Construct, Node, Dependency } from 'constructs'; import { Chart, ApiObject, Testing } from '../src'; import { Lazy } from '../src/lazy'; @@ -313,6 +315,76 @@ describe('toJson', () => { }); +test('construct metadata is recorded when requested by api', () => { + + const app = Testing.app({ recordConstructMetadata: true }); + const chart = new Chart(app, 'chart1'); + + new ApiObject(chart, 'obj1', { + kind: 'Deployment', + apiVersion: 'v1', + }); + + app.synth(); + + const constructMetadata = JSON.parse(fs.readFileSync(path.join(app.outdir, 'construct-metadata.json'), { encoding: 'utf-8' })); + expect(constructMetadata).toEqual({ + version: '1.0.0', + resources: { + 'chart1-obj1-c818e77f': { + path: 'chart1/obj1', + }, + }, + }); + + +}); + +test('construct metadata is recoreded when requested by env variable', () => { + + try { + process.env.CDK8S_RECORD_CONSTRUCT_METADATA = 'true'; + const app = Testing.app(); + const chart = new Chart(app, 'chart1'); + + new ApiObject(chart, 'obj1', { + kind: 'Deployment', + apiVersion: 'v1', + }); + + app.synth(); + + const constructMetadata = JSON.parse(fs.readFileSync(path.join(app.outdir, 'construct-metadata.json'), { encoding: 'utf-8' })); + expect(constructMetadata).toEqual({ + version: '1.0.0', + resources: { + 'chart1-obj1-c818e77f': { + path: 'chart1/obj1', + }, + }, + }); + } finally { + delete process.env.CDK8S_RECORD_CONSTRUCT_METADATA; + } + +}); + +test('construct metadata is not recorded when not requested', () => { + + const app = Testing.app(); + const chart = new Chart(app, 'chart1'); + + new ApiObject(chart, 'obj1', { + kind: 'Deployment', + apiVersion: 'v1', + }); + + app.synth(); + + expect(fs.existsSync(path.join(app.outdir, 'construct-metadata.json'))).toBeFalsy(); + +}); + function createImplictToken(value: any) { const implicit = {}; Object.defineProperty(implicit, 'resolve', { value: () => value });