Skip to content

Instantly share code, notes, and snippets.

@skorfmann
Last active February 3, 2021 11:05
Show Gist options
  • Save skorfmann/e399bbd85be6e5a8a5a021adcb570dd4 to your computer and use it in GitHub Desktop.
Save skorfmann/e399bbd85be6e5a8a5a021adcb570dd4 to your computer and use it in GitHub Desktop.
This uses the external Terraform provider (https://registry.terraform.io/providers/hashicorp/external) to build ad hoc data sources for cdktf.
import { Construct, } from "constructs";
import { Resource, TerraformResource } from 'cdktf';
import * as fs from "fs";
import * as path from 'path'
import { DataExternal } from "../.gen/providers/external"
export interface CustomDataSourceConfig<Inputs, Outputs> {
code(input: Inputs): Promise<Outputs>
inputs: Inputs;
dependsOn?: TerraformResource[];
}
class OutputProxy {
constructor(protected data: DataExternal) {
return new Proxy(this, this);
}
public get(_target: any, prop: string) {
return this.data.result(prop);
}
}
export class CustomDataSource<Inputs,Outputs> extends Resource {
public readonly outputs: OutputProxy;
public readonly data: DataExternal;
constructor(scope: Construct, name: string, config: CustomDataSourceConfig<Inputs, Outputs>) {
super(scope, name);
const { code } = config;
const filePath = path.join(process.cwd(), 'cdktf.out', `${name}-data-source.js`)
fs.writeFileSync(filePath, `
const handler = ${code.toString()}
const stdin = process.stdin,
stdout = process.stdout,
inputChunks = [];
stdin.setEncoding('utf8');
stdin.on('data', function (chunk) {
inputChunks.push(chunk)
});
stdin.on('end', async function () {
const input = JSON.parse(inputChunks.join(''))
try {
const output = await handler(input)
stdout.write(JSON.stringify(output));
stdout.write('\\n');
} catch(e) {
console.error("error", e)
}
})
`)
this.data = new DataExternal(this, 'external', {
program: ['node', filePath],
query: config.inputs as any,
workingDir: path.join(process.cwd()),
dependsOn: config.dependsOn
})
this.outputs = new OutputProxy(this.data)
this.outputs
}
public result(key: keyof Outputs): string {
return this.data.result(key as string)
}
}
import { CustomDataSource } from '../custom-data-source'
interface CustomDockerImageInput {
repositoryUrl: string;
username: string;
password: string;
}
interface CustomDockerImageOutput {
sha256Digest: string;
}
class CustomDockerImage extends CustomDataSource<CustomDockerImageInput, CustomDockerImageOutput> {};
const image = new CustomDockerImage(this, 'fetchImage', {
inputs: {
repositoryUrl: repository.url,
username: repository.authorizationUser,
password: repository.authorizationPassword
},
code: async (args) => {
const drc = require('docker-registry-client')
return new Promise((resolve, reject) => {
var rar = drc.parseRepoAndRef((args as any).repository_url);
var client = drc.createClientV2({
repo: rar,
insecure: false,
username: args.username,
password: args.password,
maxSchemaVersion: 2
});
var tagOrDigest = rar.tag || rar.digest;
client.getManifest({ref: tagOrDigest}, function (err:any, _manifest:any, res:any, manifestStr:any) {
client.close();
if (err) {
reject(err)
}
console.error('# response headers');
console.error(JSON.stringify(res.headers, null, 4));
console.error('# manifest');
resolve({sha256Digest: drc.digestFromManifestStr(manifestStr)});
});
});
},
})
@jsteinich
Copy link

This is pretty cool. Too bad jsii doesn't support generics.

@skorfmann
Copy link
Author

Yes, generics would be nice 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment