Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: helm import #1202

Merged
merged 40 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
dca862e
feat: helm import
vinayak-kukreja Jul 31, 2023
b0ac49a
Merge branch 'cdk8s-team:2.x' into vkukreja/helm-import
vinayak-kukreja Aug 1, 2023
63be194
Merge branch '2.x' into vkukreja/helm-import
vinayak-kukreja Aug 8, 2023
eee2666
update pr
vinayak-kukreja Aug 8, 2023
75d1648
update regex
vinayak-kukreja Aug 8, 2023
f6cf8c9
add support for subchart and global
vinayak-kukreja Aug 9, 2023
3516843
update PR
vinayak-kukreja Aug 9, 2023
f7ebbbe
update with correct interfaces
vinayak-kukreja Aug 9, 2023
79218e6
Merge branch '2.x' into vkukreja/helm-import
iliapolo Aug 24, 2023
4ca1861
address feedback
vinayak-kukreja Sep 26, 2023
4fb10d1
update PR
vinayak-kukreja Sep 26, 2023
7171f97
update PR
vinayak-kukreja Sep 27, 2023
bf2e5a5
update PR
vinayak-kukreja Sep 27, 2023
3b21416
remove comments
vinayak-kukreja Sep 27, 2023
1d332bb
Merge branch '2.x' into vkukreja/helm-import
vinayak-kukreja Sep 27, 2023
94020bb
update PR
vinayak-kukreja Sep 27, 2023
17feb41
investigate build failure
vinayak-kukreja Sep 27, 2023
0ade12e
investigate build failure
vinayak-kukreja Sep 27, 2023
af24dc7
investigate build failure
vinayak-kukreja Sep 27, 2023
8bd5102
investigate build failure
vinayak-kukreja Sep 27, 2023
372e87b
fixing build
vinayak-kukreja Sep 27, 2023
c457d9b
fixing build
vinayak-kukreja Sep 27, 2023
292df3b
update PR
vinayak-kukreja Sep 27, 2023
c2558df
update PR
vinayak-kukreja Sep 28, 2023
be67d5d
update PR
vinayak-kukreja Sep 28, 2023
814cbac
Merge branch '2.x' into vkukreja/helm-import
iliapolo Sep 28, 2023
9e6f60f
update PR
vinayak-kukreja Sep 28, 2023
343d1b1
update PR
vinayak-kukreja Sep 28, 2023
dcd019d
update PR
vinayak-kukreja Sep 29, 2023
6787b6b
update PR
vinayak-kukreja Sep 29, 2023
eee43e7
Merge branch '2.x' into vkukreja/helm-import
vinayak-kukreja Sep 29, 2023
2e28390
Merge branch '2.x' into vkukreja/helm-import
iliapolo Oct 2, 2023
c2e7513
update PR
vinayak-kukreja Oct 3, 2023
ef82b6f
Merge branch '2.x' into vkukreja/helm-import
vinayak-kukreja Oct 3, 2023
50d14bd
update PR
vinayak-kukreja Oct 3, 2023
ef166f2
update PR
vinayak-kukreja Oct 3, 2023
e4ee20a
Merge branch '2.x' into vkukreja/helm-import
iliapolo Oct 3, 2023
2995e26
update PR
vinayak-kukreja Oct 3, 2023
7016ab4
update PR
vinayak-kukreja Oct 4, 2023
a28a3f6
Merge branch '2.x' into vkukreja/helm-import
iliapolo Oct 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ const project = new Cdk8sTeamTypeScriptProject({
],
backport: true,
backportBranches: ['1.x'],

});

project.tsconfig?.addInclude('src/schemas/*.json');
Expand Down
28 changes: 3 additions & 25 deletions src/cli/cmds/import.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as yargs from 'yargs';
import { readConfigSync, ImportSpec } from '../../config';
import { PREFIX_DELIM, importDispatch } from '../../import/dispatch';
import { importDispatch } from '../../import/dispatch';
import { DEFAULT_API_VERSION } from '../../import/k8s';
import { parseImports } from '../../util';

const config = readConfigSync();

Expand All @@ -21,6 +22,7 @@ class Command implements yargs.CommandModule {
.example('cdk8s import jenkins.io_jenkins_crd.yaml', 'Imports constructs for the Jenkins custom resource definition from a file')
.example('cdk8s import mattermost:=mattermost_crd.yaml', 'Imports constructs for the mattermost cluster custom resource definition using a custom module name')
.example('cdk8s import github:crossplane/[email protected]', 'Imports constructs for a GitHub repo using doc.crds.dev')
.example('cdk8s import helm:https://charts.bitnami.com/bitnami/[email protected]', 'Imports the specified version of helm chart')

.option('save', { type: 'boolean', required: false, default: true, desc: "Dont save the import URL in the 'imports' section of the cdk8s.yaml configuration file.", alias: 's' })
.option('output', { default: DEFAULT_OUTDIR, type: 'string', desc: 'Output directory', alias: 'o' })
Expand All @@ -42,28 +44,4 @@ class Command implements yargs.CommandModule {
}
}

function parseImports(spec: string): ImportSpec {
const splitImport = spec.split(PREFIX_DELIM);

// [email protected]
// crd.yaml
// url.com/crd.yaml
if (splitImport.length === 1) {
return {
source: spec,
};
}

// crd=crd.yaml
// crd=url.com/crd.yaml
if (splitImport.length === 2) {
return {
moduleNamePrefix: splitImport[0],
source: splitImport[1],
};
}

throw new Error('Unable to parse import specification. Syntax is [NAME:=]SPEC');
}

module.exports = new Command();
142 changes: 141 additions & 1 deletion src/import/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function generateConstruct(typegen: TypeGenerator, def: ApiObjectDefiniti
// `propsTypeName` could also be "any" if we can't parse the schema for some reason
const propsTypeName = emitPropsStruct();
const groupPrefix = def.group ? `${def.group}/` : '';
const hasRequired = schema?.required && Array.isArray(schema.required) && schema.required.length > 0;
const hasRequired = hasRequiredProps(schema);
const defaultProps = hasRequired ? '' : ' = {}';
emitConstruct();

Expand Down Expand Up @@ -213,3 +213,143 @@ export function generateConstruct(typegen: TypeGenerator, def: ApiObjectDefiniti
}
});
}

/**
* Emit imports for generated helm construct
* @param code CodeMaker istance
*/
export function emitHelmHeader(code: CodeMaker) {
code.line('// generated by cdk8s');
code.line('import { Helm } from \'cdk8s\';');
code.line('import { Construct } from \'constructs\';');
code.line();
}

/**
* Helm Object Definition
*/
export interface HelmObjectDefinition {
/**
* `values.schema.json` for the helm chart
*/
readonly schema: JSONSchema4 | undefined;
/**
* Chart name
*/
readonly chartName: string;
/**
* Chart url
*/
readonly chartUrl: string;
/**
* Chart version
*/
readonly chartVersion: string;
/**
* Chart dependencies
*/
readonly chartDependencies: string[];
/**
* Fully qualified name for the construct
*/
readonly fqn?: string;
}

export function generateHelmConstruct(typegen: TypeGenerator, def: HelmObjectDefinition) {
const noSpecialChars = def.chartName.replace(/([^\w ]|_)/g, '');
const chartName = TypeGenerator.normalizeTypeName(noSpecialChars);
const schema = def.schema;
const repoUrl = def.chartUrl;
const chartVersion = def.chartVersion;

// Create custom type
typegen.emitCustomType(chartName, code => {

const valuesInterface = `${chartName}Values`;
if (schema !== undefined) {
// Creating values interface
emitValuesInterface();

function emitValuesInterface() {
const copyOfSchema = schema;

if (copyOfSchema && copyOfSchema.properties) {
// Sub charts or dependencies
for (const dependency of def.chartDependencies) {
copyOfSchema.properties[dependency] = { type: 'object', additionalProperties: { type: 'object' } };
}

copyOfSchema.properties.global = { type: 'object', additionalProperties: { type: 'object' } };
copyOfSchema.properties.additionalValues = { type: 'object', additionalProperties: { type: 'object' } };
}

typegen.emitType(valuesInterface, copyOfSchema, def.fqn);
}
}

// Creating construct properties
emitPropsInterface();

code.line();

// Creating construct for helm chart
emitConstruct();

function emitPropsInterface() {
code.openBlock(`export interface ${chartName}Props`);

code.line('readonly namespace?: string;');
code.line('readonly releaseName?: string;');
code.line('readonly helmExecutable?: string;');
code.line('readonly helmFlags?: string[];');

if (schema === undefined) {
code.line('readonly values?: { [key: string]: any };');
iliapolo marked this conversation as resolved.
Show resolved Hide resolved
} else {
const doValuesHaveReqProps = hasRequiredProps(schema) ? '' : '?';
code.line(`readonly values${doValuesHaveReqProps}: ${valuesInterface};`);
}

code.closeBlock();
}

function emitConstruct() {
code.openBlock(`export class ${chartName} extends Construct`);

emitInitializer();

code.closeBlock();
}

function emitInitializer() {
code.openBlock(`public constructor(scope: Construct, id: string, props: ${chartName}Props = {})`);

code.line('let updatedProps = undefined;');
code.line();
code.openBlock('if (props.values && \'additionalValues\' in props.values)');
code.line('const { additionalValues, ...withoutAdditionalValues } = props.values;');
code.open('updatedProps = {');
code.line('...withoutAdditionalValues,');
code.line('...additionalValues,');
code.close('};');
code.closeBlock();
code.line();

code.open('const finalProps = {');
code.line(`chart: \'${def.chartName}\',`);
code.line(`repo: \'${repoUrl}\',`);
code.line(`version: \'${chartVersion}\',`);
code.line('values: (updatedProps ?? props),');
code.close('};');

code.line();
code.line('super(scope, id)');
code.line('new Helm(scope, \'Helm\', finalProps)');
code.closeBlock();
}
});
}

function hasRequiredProps(schema: JSONSchema4):boolean | undefined {
return schema?.required && Array.isArray(schema.required) && schema.required.length > 0;
}
7 changes: 7 additions & 0 deletions src/import/dispatch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ImportBase, ImportOptions } from './base';
import { ImportCustomResourceDefinition } from './crd';
import { matchCrdsDevUrl } from './crds-dev';
import { ImportHelm } from './helm';
import { ImportKubernetesApi } from './k8s';
import { ImportSpec, addImportToConfig } from '../config';

Expand Down Expand Up @@ -36,6 +37,12 @@ async function matchImporter(importSpec: ImportSpec, argv: any): Promise<ImportB
return new ImportKubernetesApi(k8s);
}

const prefix = importSpec.source.split(':')[0];

if (prefix === 'helm') {
return ImportHelm.fromSpec(importSpec);
}

// now check if its a crds.dev import
const crdsDevUrl = matchCrdsDevUrl(importSpec.source);
if (crdsDevUrl) {
Expand Down
Loading