Skip to content

Commit

Permalink
Input<T> in Helm
Browse files Browse the repository at this point in the history
  • Loading branch information
pgavlin committed Nov 9, 2018
1 parent d54c5fd commit 584a062
Show file tree
Hide file tree
Showing 7 changed files with 584 additions and 691 deletions.
181 changes: 106 additions & 75 deletions pkg/gen/nodejs-templates/helm.ts.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ import * as nodepath from "path";

export namespace v2 {
interface BaseChartOpts {
namespace?: string;
values?: any;
namespace?: pulumi.Input<string>;
values?: pulumi.Inputs;
transformations?: ((o: any) => void)[];
}

export interface ChartOpts extends BaseChartOpts {
repo: string;
chart: string;
version: string;
repo: pulumi.Input<string>;
chart: pulumi.Input<string>;
version: pulumi.Input<string>;
fetchOpts?: FetchOpts;
fetchOpts?: pulumi.Input<FetchOpts>;
}

function isChartOpts(o: any): o is ChartOpts {
Expand Down Expand Up @@ -63,63 +63,76 @@ export namespace v2 {
) {
super("kubernetes:helm.sh/v2:Chart", releaseName, config, opts);
// Create temporary directories and files to hold chart data and override values.
const overrides = tmp.fileSync({ postfix: ".yaml" });
const chartDir = tmp.dirSync({ unsafeCleanup: true });

try {
let chart: string;
let defaultValues: string;
if (isChartOpts(config)) {
// Fetch chart.
fetch(`${config.repo}/${config.chart}`, {
destination: chartDir.name,
version: config.version
});
chart = path.quotePath(nodepath.join(chartDir.name, config.chart));
defaultValues = path.quotePath(
nodepath.join(chartDir.name, config.chart, "values.yaml")
);
} else {
chart = path.quotePath(config.path);
defaultValues = path.quotePath(nodepath.join(chart, "values.yaml"));
}

// Write overrides file.
const data = JSON.stringify(config.values || {}, undefined, " ");
fs.writeFileSync(overrides.name, data);

// Does not require Tiller. From the `helm template` documentation:
//
// > Render chart templates locally and display the output.
// >
// > This does not require Tiller. However, any values that would normally be
// > looked up or retrieved in-cluster will be faked locally. Additionally, none
// > of the server-side testing of chart validity (e.g. whether an API is supported)
// > is done.
const release = shell.quote([releaseName]);
const values = path.quotePath(overrides.name);
const namespaceArg = config.namespace
? `--namespace ${shell.quote([config.namespace])}`
: "";
const yamlStream = execSync(
`helm template ${chart} --name ${release} --values ${defaultValues} --values ${values} ${namespaceArg}`
).toString();
this.resources = this.parseTemplate(yamlStream, config.transformations);
} catch (e) {
// Shed stack trace, only emit the error.
throw new pulumi.RunError(e.toString());
} finally {
// Clean up temporary files and directories.
chartDir.removeCallback();
overrides.removeCallback();
}
const allConfig = pulumi.output(config);
const configDeps = Array.from(<Set<pulumi.Resource>>(<any>allConfig).resources());
(<any>allConfig).isKnown.then((isKnown: boolean) => {
if (!isKnown) {
// Note that this can only happen during a preview.
pulumi.log.info("Note: some chart inputs are unknown; the chart's resources will not be displayed.");
}
});

this.resources = allConfig.apply(cfg => {
// Create temporary directories and files to hold chart data and override values.
const overrides = tmp.fileSync({ postfix: ".yaml" });
const chartDir = tmp.dirSync({ unsafeCleanup: true });

try {
let chart: string;
let defaultValues: string;
if (isChartOpts(cfg)) {
// Fetch chart.
fetch(`${cfg.repo}/${cfg.chart}`, {
destination: chartDir.name,
version: cfg.version
});
chart = path.quotePath(nodepath.join(chartDir.name, cfg.chart));
defaultValues = path.quotePath(
nodepath.join(chartDir.name, cfg.chart, "values.yaml")
);
} else {
chart = path.quotePath(cfg.path);
defaultValues = path.quotePath(nodepath.join(chart, "values.yaml"));
}

// Write overrides file.
const data = JSON.stringify(cfg.values || {}, undefined, " ");
fs.writeFileSync(overrides.name, data);

// Does not require Tiller. From the `helm template` documentation:
//
// > Render chart templates locally and display the output.
// >
// > This does not require Tiller. However, any values that would normally be
// > looked up or retrieved in-cluster will be faked locally. Additionally, none
// > of the server-side testing of chart validity (e.g. whether an API is supported)
// > is done.
const release = shell.quote([releaseName]);
const values = path.quotePath(overrides.name);
const namespaceArg = cfg.namespace
? `--namespace ${shell.quote([cfg.namespace])}`
: "";
const yamlStream = execSync(
`helm template ${chart} --name ${release} --values ${defaultValues} --values ${values} ${namespaceArg}`
).toString();
return this.parseTemplate(yamlStream, cfg.transformations, configDeps);
} catch (e) {
// Shed stack trace, only emit the error.
throw new pulumi.RunError(e.toString());
} finally {
// Clean up temporary files and directories.
chartDir.removeCallback();
overrides.removeCallback();
}
});
}

parseTemplate(
yamlStream: string,
transformations?: ((o: any) => void)[]
): { [key: string]: pulumi.CustomResource } {
transformations: ((o: any) => void)[] | undefined,
dependsOn: pulumi.Resource[]
): pulumi.Output<{ [key: string]: pulumi.CustomResource }> {
const objs = jsyaml
.safeLoadAll(yamlStream)
.filter(a => a != null && "kind" in a)
Expand All @@ -129,7 +142,7 @@ export namespace v2 {
yaml: objs.map(o => jsyaml.safeDump(o)),
transformations: transformations || []
},
{ parent: this }
{ parent: this, dependsOn: dependsOn }
);
}
}
Expand Down Expand Up @@ -195,51 +208,69 @@ export namespace v2 {

export interface FetchOpts {
// Specific version of a chart. Without this, the latest version is fetched.
version?: string;
version?: pulumi.Input<string>;
// Verify certificates of HTTPS-enabled servers using this CA bundle.
caFile?: string;
caFile?: pulumi.Input<string>;
// Identify HTTPS client using this SSL certificate file.
certFile?: string;
certFile?: pulumi.Input<string>;
// Identify HTTPS client using this SSL key file.
keyFile?: string;
keyFile?: pulumi.Input<string>;
// Location to write the chart. If this and tardir are specified, tardir is appended to this
// (default ".").
destination?: string;
destination?: pulumi.Input<string>;
// Keyring containing public keys (default "/Users/alex/.gnupg/pubring.gpg").
keyring?: string;
keyring?: pulumi.Input<string>;
// Chart repository password.
password?: string;
password?: pulumi.Input<string>;
// Chart repository url where to locate the requested chart.
repo?: string;
repo?: pulumi.Input<string>;
// If untar is specified, this flag specifies the name of the directory into which the chart is
// expanded (default ".").
untardir?: string;
untardir?: pulumi.Input<string>;
// Chart repository username.
username?: string;
username?: pulumi.Input<string>;
// Location of your Helm config. Overrides $HELM_HOME (default "/Users/alex/.helm").
home?: string;
home?: pulumi.Input<string>;
// Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is
// ignored.
devel?: boolean;
devel?: pulumi.Input<boolean>;
// Fetch the provenance file, but don't perform verification.
prov?: boolean;
prov?: pulumi.Input<boolean>;
// If set to false, will leave the chart as a tarball after downloading.
untar?: boolean;
untar?: pulumi.Input<boolean>;
// Verify the package against its signature.
verify?: pulumi.Input<boolean>;
}
interface ResolvedFetchOpts {
version?: string;
caFile?: string;
certFile?: string;
keyFile?: string;
destination?: string;
keyring?: string;
password?: string;
repo?: string;
untardir?: string;
username?: string;
home?: string;
devel?: boolean;
prov?: boolean;
untar?: boolean;
verify?: boolean;
}
Expand All @@ -254,7 +285,7 @@ export interface FetchOpts {
// If the `verify` option is specified, the requested chart MUST have a provenance file, and MUST
// pass the verification process. Failure in any part of this will result in an error, and the chart
// will not be saved locally.
export function fetch(chart: string, opts?: FetchOpts) {
export function fetch(chart: string, opts?: ResolvedFetchOpts) {
const flags: string[] = [];
if (opts !== undefined) {
// Untar by default.
Expand Down
2 changes: 1 addition & 1 deletion pkg/gen/nodejs-templates/package.json.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"build": "tsc"
},
"dependencies": {
"@pulumi/pulumi": "^0.15.1",
"@pulumi/pulumi": "^0.16.3",
"@types/js-yaml": "^3.11.2",
"js-yaml": "^3.12.0",
"shell-quote": "^1.6.1",
Expand Down
45 changes: 20 additions & 25 deletions pkg/gen/nodejs-templates/provider.ts.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export namespace yaml {

export function parse(
config: ConfigGroupOpts, opts?: pulumi.CustomResourceOptions
): {[key: string]: pulumi.CustomResource} {
let resources: {[key: string]: pulumi.CustomResource} = {};
): pulumi.Output<{[key: string]: pulumi.CustomResource}> {
let resources = pulumi.output<{[key: string]: pulumi.CustomResource}>({});

if (config.files !== undefined) {
let files: string[] = [];
Expand All @@ -43,11 +43,8 @@ export namespace yaml {
}

for (const file of files) {
const cf = new ConfigFile(file,
{file: file, transformations: config.transformations}, opts);
if (cf != null) {
resources = {...resources, ...cf.resources}
}
const cf = new ConfigFile(file, {file: file, transformations: config.transformations}, opts);
resources = pulumi.all([resources, cf.resources]).apply(([rs, cfrs]) => ({...rs, ...cfrs}));
}
}

Expand All @@ -61,24 +58,22 @@ export namespace yaml {

for (const text of yamlTexts) {
const objs = jsyaml.safeLoadAll(text);
const docResources = parseYamlDocument(
{objs: objs, transformations: config.transformations}, opts);
resources = {...resources, ...docResources};
const docResources = parseYamlDocument({objs: objs, transformations: config.transformations}, opts);
resources = pulumi.all([resources, docResources]).apply(([rs, drs]) => ({...rs, ...drs}));
}
}

if (config.objs !== undefined) {
const objs= Array.isArray(config.objs) ? config.objs: [config.objs];
const docResources = parseYamlDocument(
{objs: objs, transformations: config.transformations}, opts);
resources = {...resources, ...docResources};
const docResources = parseYamlDocument({objs: objs, transformations: config.transformations}, opts);
resources = pulumi.all([resources, docResources]).apply(([rs, drs]) => ({...rs, ...drs}));
}

return resources;
}

export abstract class CollectionComponentResource extends pulumi.ComponentResource {
resources: { [key: string]: pulumi.CustomResource };
resources: pulumi.Output<{ [key: string]: pulumi.CustomResource }>;

constructor(
resourceType: string, name: string, config: any, opts?: pulumi.ComponentResourceOptions,
Expand All @@ -95,12 +90,12 @@ export namespace yaml {
{{#Groups}}
{{#Versions}}
{{#Kinds}}
public getResource(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", name: string): {{Group}}.{{Version}}.{{Kind}};
public getResource(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", namespace: string, name: string): {{Group}}.{{Version}}.{{Kind}};
public getResource(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", name: string): pulumi.Output<{{Group}}.{{Version}}.{{Kind}}>;
public getResource(groupVersionKind: "{{RawAPIVersion}}/{{Kind}}", namespace: string, name: string): pulumi.Output<{{Group}}.{{Version}}.{{Kind}}>;
{{/Kinds}}
{{/Versions}}
{{/Groups}}
public getResource(groupVersionKind: string, namespaceOrName: string, name?: string): pulumi.CustomResource {
public getResource(groupVersionKind: string, namespaceOrName: string, name?: string): pulumi.Output<pulumi.CustomResource> {
return this.getResourceImpl(groupVersionKind, namespaceOrName, name);
}

Expand All @@ -110,20 +105,20 @@ export namespace yaml {
* For example:
* getCustomResource("monitoring.coreos.com/v1/ServiceMonitor", "kube-prometheus-exporter-kubernetes")
*/
public getCustomResource<T extends pulumi.CustomResource>(groupVersionKind: string, namespace: string): T;
public getCustomResource<T extends pulumi.CustomResource>(groupVersionKind: string, namespace: string, name: string): T;
public getCustomResource(groupVersionKind: string, namespaceOrName: string, name?: string): pulumi.CustomResource {
public getCustomResource<T extends pulumi.CustomResource>(groupVersionKind: string, namespace: string): pulumi.Output<T>;
public getCustomResource<T extends pulumi.CustomResource>(groupVersionKind: string, namespace: string, name: string): pulumi.Output<T>;
public getCustomResource(groupVersionKind: string, namespaceOrName: string, name?: string): pulumi.Output<pulumi.CustomResource> {
return this.getResourceImpl(groupVersionKind, namespaceOrName, name);
}

private getResourceImpl(groupVersionKind: string, namespaceOrName: string, name?: string): pulumi.CustomResource {
// `id` will either be `${name}` or `${namespace}/${name}`.
private getResourceImpl(groupVersionKind: string, namespaceOrName: string, name?: string): pulumi.Output<pulumi.CustomResource> {
// `id` will either be `${name}` or `${namespace}/${name}`.
let id = namespaceOrName;
if (name !== undefined) {
id = `${namespaceOrName}/${name}`;
}

return this.resources[`${groupVersionKind}::${id}`];
return this.resources.apply(resources => resources[`${groupVersionKind}::${id}`]);
}
}

Expand Down Expand Up @@ -159,10 +154,10 @@ export namespace yaml {
super("kubernetes:yaml:ConfigFile", name, config, opts);
const text = fs.readFileSync(config && config.file || name).toString();
const objs = jsyaml.safeLoadAll(text);
this.resources = parseYamlDocument({
this.resources = pulumi.output(parseYamlDocument({
objs: objs,
transformations: config && config.transformations || []
}, {parent: this});
}, {parent: this}));
}
}

Expand Down
Loading

0 comments on commit 584a062

Please sign in to comment.