-
-
Save brandonkal/af95cc41a7548a0e323fac90cbe2df23 to your computer and use it in GitHub Desktop.
Pulumi Custom Resource Validation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Replacing line 172 to throw a plain Error | |
Diagnostics: | |
pulumi:pulumi:Stack (crd-crd): | |
Error: No events found for SealedSecret ss-test @ https://127.0.0.1:6443/api/v1/namespaces/default/events?fieldSelector=involvedObject.name%3Dss-test%2CinvolvedObject.namespace%3Ddefault%2CinvolvedObject.kind%3DSealedSecret: {"kind":"EventList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/default/events","resourceVersion":"708310"},"items":[]} | |
at SealedSecret.<anonymous> (/home/brandon/pulumi-project/lib/sealedsecret.ts:175:15) | |
at Generator.throw (<anonymous>) | |
at rejected (/home/brandon/pulumi-project/lib/sealedsecret.ts:6:65) | |
at processTicksAndRejections (internal/process/task_queues.js:93:5) | |
Error: No events found for SealedSecret ss-test @ https://127.0.0.1:6443/api/v1/namespaces/default/events?fieldSelector=involvedObject.name%3Dss-test%2CinvolvedObject.namespace%3Ddefault%2CinvolvedObject.kind%3DSealedSecret: {"kind":"EventList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/default/events","resourceVersion":"708310"},"items":[]} | |
at SealedSecret.<anonymous> (/home/brandon/pulumi-project/lib/sealedsecret.ts:175:15) | |
at Generator.throw (<anonymous>) | |
at rejected (/home/brandon/pulumi-project/lib/sealedsecret.ts:6:65) | |
at processTicksAndRejections (internal/process/task_queues.js:93:5) | |
error: Running program '/home/brandon/pulumi-project/08-crd' failed with an unhandled exception: | |
Error: No events found for SealedSecret ss-test @ https://127.0.0.1:6443/api/v1/namespaces/default/events?fieldSelector=involvedObject.name%3Dss-test%2CinvolvedObject.namespace%3Ddefault%2CinvolvedObject.kind%3DSealedSecret: {"kind":"EventList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/default/events","resourceVersion":"708310"},"items":[]} | |
at SealedSecret.<anonymous> (/home/brandon/pulumi-project/lib/sealedsecret.ts:175:15) | |
at Generator.throw (<anonymous>) | |
at rejected (/home/brandon/pulumi-project/lib/sealedsecret.ts:6:65) | |
at processTicksAndRejections (internal/process/task_queues.js:93:5) | |
// See revision 2 of this gist for a similar duplicate error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Diagnostics: | |
pulumi:pulumi:Stack (crd-crd): | |
Error: No events found for SealedSecret ss-test @ https://127.0.0.1:6443/api/v1/namespaces/default/events?fieldSelector=involvedObject.name%3Dss-test%2CinvolvedObject.namespace%3Ddefault%2CinvolvedObject.kind%3DSealedSecret: {"kind":"EventList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/default/events","resourceVersion":"706082"},"items":[]} | |
at SealedSecret.<anonymous> (/home/brandon/pulumi-project/lib/sealedsecret.ts:173:15) | |
at Generator.throw (<anonymous>) | |
at rejected (/home/brandon/pulumi-project/lib/sealedsecret.ts:6:65) | |
at processTicksAndRejections (internal/process/task_queues.js:93:5) | |
Error: No events found for SealedSecret ss-test @ https://127.0.0.1:6443/api/v1/namespaces/default/events?fieldSelector=involvedObject.name%3Dss-test%2CinvolvedObject.namespace%3Ddefault%2CinvolvedObject.kind%3DSealedSecret: {"kind":"EventList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/default/events","resourceVersion":"706082"},"items":[]} | |
at SealedSecret.<anonymous> (/home/brandon/pulumi-project/lib/sealedsecret.ts:173:15) | |
at Generator.throw (<anonymous>) | |
at rejected (/home/brandon/pulumi-project/lib/sealedsecret.ts:6:65) | |
at processTicksAndRejections (internal/process/task_queues.js:93:5) | |
sealedsecret (ss-test): | |
error: No events found for SealedSecret ss-test @ https://127.0.0.1:6443/api/v1/namespaces/default/events?fieldSelector=involvedObject.name%3Dss-test%2CinvolvedObject.namespace%3Ddefault%2CinvolvedObject.kind%3DSealedSecret: {"kind":"EventList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/default/events","resourceVersion":"706082"},"items":[]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as k8s from '@pulumi/kubernetes' | |
import { types } from '@pulumi/kubernetesx' | |
import * as pulumi from '@pulumi/pulumi' | |
import * as k8sClient from '@kubernetes/client-node' | |
import * as request from 'request' | |
import { promisify } from 'util' | |
import * as jsyaml from 'js-yaml' | |
import * as fs from 'fs' | |
const rget = promisify(request.get) | |
type SealedSecretArgs = { file: string } | { yaml: string } | |
interface Meta { | |
name: string | |
namespace: string | |
[key: string]: string | |
} | |
interface Spec { | |
template: { | |
metadata: Meta | |
} | |
encryptedData: Record<string, string> | |
} | |
export interface Sealed { | |
kind: 'SealedSecret' | |
apiVersion: 'bitnami.com/v1alpha1' | |
metadata: Meta | |
spec: Spec | |
} | |
function isSealedSecret(obj: any): obj is Sealed { | |
if ( | |
obj != null && | |
typeof obj === 'object' && | |
obj.kind === 'SealedSecret' && | |
obj.apiVersion === 'bitnami.com/v1alpha1' && | |
obj.metadata && | |
typeof obj.metadata.name === 'string' && | |
typeof obj.metadata.namespace === 'string' && | |
obj.spec && | |
obj.spec.template && | |
obj.spec.template.metadata && | |
typeof obj.spec.template.metadata.name === 'string' && | |
typeof obj.spec.template.metadata.namespace === 'string' && | |
obj.spec.encryptedData | |
) { | |
return true | |
} | |
return false | |
} | |
/** | |
* SealedSecret takes a Bitnami SealedSecret and applies it to the kubernetes cluster. It uses the Kubernetes API to look at events related to the SealedSecret to verify that the secret could be unsealed. | |
* It also provides kx-like methods for referencing the unsealed secret including `mount()` and `asEnvValue()`. | |
* It assumes the default kubeconfig cluster. | |
*/ | |
export class SealedSecret extends pulumi.ComponentResource { | |
public readonly metadata: pulumi.Output<Meta> | |
public readonly spec: pulumi.Output<Spec> | |
public readonly unsealed: pulumi.Output<boolean> | |
mount( | |
destPath: pulumi.Input<string>, | |
srcPath?: pulumi.Input<string> | |
): pulumi.Output<types.VolumeMount> { | |
return pulumi.output({ | |
volume: { | |
name: this.metadata.name, | |
secret: { | |
secretName: this.metadata.name, | |
}, | |
}, | |
destPath: destPath, | |
srcPath: srcPath, | |
}) | |
} | |
asEnvValue( | |
key: pulumi.Input<string> | |
): pulumi.Output<k8s.types.input.core.v1.EnvVarSource> { | |
return pulumi.output({ | |
secretKeyRef: { | |
name: this.metadata.name, | |
key: key, | |
}, | |
}) | |
} | |
constructor( | |
name: string, | |
args: SealedSecretArgs, | |
opts: pulumi.CustomResourceOptions = {} | |
) { | |
super('sealedsecret', name, undefined, opts) | |
let content: string | |
if ('file' in args) { | |
content = fs.readFileSync(args.file).toString() | |
} else { | |
content = args.yaml | |
} | |
const obj = jsyaml.safeLoad(content) | |
if (!isSealedSecret(obj)) { | |
throw new pulumi.ResourceError( | |
`Expected a SealedSecret but got: ${JSON.stringify(obj)}`, | |
this, | |
true | |
) | |
} | |
this.metadata = pulumi.output(obj.metadata) | |
this.spec = pulumi.output(obj.spec) | |
opts.parent = this | |
const ss = new k8s.apiextensions.CustomResource(name, obj, opts) | |
const secName = obj.metadata.name | |
function awaitUnseal(input: any): Promise<boolean> { | |
return new Promise(async (resolve, reject) => { | |
if (!pulumi.runtime.isDryRun()) { | |
const kc = new k8sClient.KubeConfig() | |
kc.loadFromDefault() | |
const opts = {} as any | |
kc.applyToRequest(opts) | |
const cluster = kc.getCurrentCluster() | |
if (cluster == null) { | |
reject( | |
new pulumi.RunError( | |
'Unable to configure kubernetes client for sealedsecret' | |
) | |
) | |
return | |
} | |
const url = `${cluster.server}/api/v1/namespaces/${ | |
obj.metadata.namespace | |
}/events?fieldSelector=${encodeURIComponent( | |
`involvedObject.name=${secName},involvedObject.namespace=${obj.metadata.namespace},involvedObject.kind=SealedSecret` | |
)}` | |
const result = await rget({ ...opts, url: url }) | |
const body = await JSON.parse(result.body) | |
if (body.items && body.items.length) { | |
const event = body.items.pop() | |
if (event.reason !== 'Unsealed') { | |
reject(new pulumi.ResourceError(event.message, this, true)) | |
return | |
} | |
resolve(event) | |
return | |
} else { | |
reject( | |
new Error( | |
`No events found for SealedSecret ${secName} @ ${url}: ${JSON.stringify( | |
body | |
)}` | |
) | |
) | |
} | |
} else { | |
resolve(input) | |
return | |
} | |
}) | |
} | |
this.unsealed = pulumi.output(false) | |
this.unsealed = pulumi.output(ss).apply(async (res) => { | |
try { | |
await awaitUnseal(res) | |
return true | |
} catch (e) { | |
throw new pulumi.ResourceError(e.message, this, true) | |
} | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment