https://pulumi.com logo
b

bitter-dentist-28132

08/30/2019, 2:10 PM
is there a way to wait for things, like what you can do with terraform and
null_resource
? I just can't seem to order a
ClusterIssuer
correctly so i'd like to just wait a bit after cert-manager is installed.
e

early-intern-90238

08/30/2019, 2:54 PM
Did you try dependsOn?
b

bitter-dentist-28132

08/30/2019, 3:12 PM
dependsOn
what?
e

early-intern-90238

08/30/2019, 3:16 PM
the ClusterIssuer
b

bitter-dentist-28132

08/30/2019, 3:16 PM
to be verbose, i've tried doing a
dependsOn
various cert-manager resources, but it always fails with
Plan apply failed: resource letsencrypt-prod was not successfully created by the Kubernetes API server : Internal error occurred: failed calling webhook "<http://clusterissuers.admission.certmanager.k8s.io|clusterissuers.admission.certmanager.k8s.io>": the server is currently unable to handle the request
i also tried depending on the parent
kubernetes:yaml:ConfigFile
. this didn't work because depending on the parent doesn't wait for its children resources. (so it failed because the CRD didn't exist yet) what i'd like to try is waiting on all children resources (which i don't think i can do without enumerating all children resources). and if that doesn't work, wait for 30s after all the children resources have been created, then create the ClusterIssuer.
e

early-intern-90238

08/30/2019, 3:16 PM
hmmm
Does it timeout?
or do that immediately?
b

bitter-dentist-28132

08/30/2019, 3:18 PM
i haven't caught it during creation other than when it fails due to the CRD not existing, where i saw it doing retries.
so i don't know
e

early-intern-90238

08/30/2019, 3:18 PM
Have you tried setting parent?
b

bitter-dentist-28132

08/30/2019, 3:18 PM
of the clusterissuer?
e

early-intern-90238

08/30/2019, 3:18 PM
yes
b

bitter-dentist-28132

08/30/2019, 3:19 PM
i tried containing the clusterissuer and the cert-manager yaml in a single
kubernetes:yaml:ConfigGroup
-- that also didn't work (CRD not existing issue)
so i imagine the same thing would happen when explicitly setting the parent of a separate resource
e

early-intern-90238

08/30/2019, 3:20 PM
yeah probably
this is on DO right?
b

bitter-dentist-28132

08/30/2019, 3:20 PM
it is, but i doubt it matters.
e

early-intern-90238

08/30/2019, 3:22 PM
and your passing in the provider as well?
b

bitter-dentist-28132

08/30/2019, 3:23 PM
yup. if i run it again it works, so it's clearly just a resource creation timing issue
e

early-intern-90238

08/30/2019, 3:26 PM
b

bitter-dentist-28132

08/30/2019, 3:26 PM
and wait for what?
e

early-intern-90238

08/30/2019, 3:27 PM
well my intial thought was just use a timer, its a hack until you find a real solution
I guess you could go the route of deploying an instrumentationpod to check if everything is really done and returns a http 200
b

bitter-dentist-28132

08/30/2019, 3:37 PM
i'm not really sure what's happening in that example tbh. i guess it's just waiting for the promise returned by
util.checkHttpLatency
to resolve? and during that time it might throw an error?
umm, i guess i could add a transformation to the yaml that would add a promise annotation to all the resources created by the yaml, and then do a
Promise.all([...]).then(createCluserIssuer)
?
e

early-intern-90238

08/30/2019, 3:47 PM
That would d be really cool if that worked
Let me know if it does
b

bitter-dentist-28132

08/30/2019, 4:14 PM
I'm a dummy. I should be able to just use the
resources
property of the
ConfigFile
as the
dependsOn
... i guess i'll see if that works
b

better-rainbow-14549

08/30/2019, 4:27 PM
working on this too
Copy code
const certManager = new kubernetes.yaml.ConfigFile(
        "cert-manager",
        { file: CertManagerYamlFile, },
        { providers: { kubernetes: provider } }
    );
    const dependsOn = [certManager.getResource("v1/Namespace", "cert-manager")];
thats where i'm at
b

bitter-dentist-28132

08/30/2019, 4:56 PM
i just tried this, and it seems to work:
Copy code
const certManager = new k8s.yaml.ConfigFile("<https://github.com/jetstack/cert-manager/releases/download/v0.9.1/cert-manager.yaml>", {}, {provider: provider});
    const CLUSTER_ISSUER_YAML = `...`;
    
    // transform the {key: Resource} type of 
    // certManager.resources to the
    // Resource[] type accepted by dependsOn
    const dependsOn = certManager.resources.apply(resources => Object.keys(resources).map(key => resources[key]));
    const clusterIssuer = new k8s.yaml.ConfigGroup('cluster-issuer', {yaml: CLUSTER_ISSUER_YAML}, {provider, dependsOn});
b

better-rainbow-14549

08/30/2019, 4:57 PM
cool i thought about doing that but i only hit errors about namespace so far
b

bitter-dentist-28132

08/30/2019, 4:58 PM
you're using the all-in-one yaml, yeah? same as the link in mine there ^
b

better-rainbow-14549

08/30/2019, 4:58 PM
instead of doing the yaml like that... i have written some custom resource things like so
Copy code
/**
 * A resource which issues certificates via HTTP01 Challenge Provider.
 */
export class Http01CertificateIssuer extends kubernetes.apiextensions.CustomResource implements CertificateIssuer {
    /**
     * The name of the issuer.
     */
    public name: string;

    /**
     * The issuer configuration.
     */
    public configuration: CertificateProviderConfigHttp01[];

    /**
     * The provider used to deploy the created resource.
     */
    public provider: pulumi.ProviderResource;

    /**
     * Registers a new ClusterCertificateIssuer using LetsEncrypt.
     * @param issuerName The name to refer to the issuer as.
     * @param namespace The namespace to store the issuer in.
     * @param acmeCertificateEmail The email to supply as the registrant.
     * @param server The LetsEncrypt endpoint to use.
     * @param provider A specific provider to use.
     * @param dependsOn Additional dependencies.
     * @param privateKeySecretRef An optional name for the secret where the private key for the certificate is stored.
     */
    constructor(
        issuerName: string,
        namespace: pulumi.Input<string>,
        acmeCertificateEmail: pulumi.Input<string>,
        server: pulumi.Input<string>,
        provider: pulumi.ProviderResource,
        dependsOn: pulumi.Resource[] | undefined,
        privateKeySecretRef: pulumi.Input<string> = issuerName,
        ingressClass: pulumi.Input<string> = "nginx"
    ) {
        super(
            issuerName,
            {
                apiVersion: "<http://certmanager.k8s.io/v1alpha1|certmanager.k8s.io/v1alpha1>",
                kind: "ClusterIssuer",
                metadata: {
                    name: issuerName,
                    namespace: namespace
                },
                spec: {
                    acme: {
                        server: server,
                        email: acmeCertificateEmail,
                        privateKeySecretRef: {
                            name: privateKeySecretRef
                        },
                        http01: {}
                    },
                    solvers: [{
                        selector: {},
                        http01: {
                            ingress: {
                                class: "nginx"
                            }
                        }
                    }]
                }
            },
            { provider: provider, dependsOn: dependsOn }
        );

        this.name = issuerName;
        this.provider = provider;
        this.configuration = [{ http01: { ingressClass: ingressClass } }];
    }
}
yeah using the single file
i have a similar one for certificates themselves too
worked well for 6 i'm hoping it'll be fine for 9.2
b

bitter-dentist-28132

08/30/2019, 5:00 PM
that's definitely the better way of doing things
if i can get things working, i will definitely go that more elegant route. right now i'm just trying to have my CI work well 🙂
b

better-rainbow-14549

08/30/2019, 5:01 PM
it pays off for certs imo
Copy code
/**
 * A certificate issued by a CertificateIssuer.
 */
export class Certificate extends kubernetes.apiextensions.CustomResource implements ICertificate {

    public readonly name: string;
    public readonly namespace: pulumi.Output<string>;
    public readonly secretName: pulumi.Output<string>;
    public readonly domains: pulumi.Output<string[]>;

    /**
     * Creates a new certificate.
     * @param name The name of the certificate.
     * @param namespace The namespace to store the certificate in.
     * @param domains The domains the certificate certifies.
     * @param issuer The CertificateIssuer used to issue it.
     */
    constructor(
        name: string,
        namespace: pulumi.Input<string>,
        domains: pulumi.Input<string>[],
        issuer: CertificateIssuer,
        secretName: pulumi.Input<string> = name
    ) {
        super(
            name,
            {
                apiVersion: "<http://certmanager.k8s.io/v1alpha1|certmanager.k8s.io/v1alpha1>",
                kind: "Certificate",
                metadata: {
                    name: name,
                    namespace: namespace
                },
                spec: {
                    secretName: secretName,
                    dnsNames: domains,
                    acme: {
                        config: issuer.configuration.map(x => ({
                            ...x,
                            domains: domains
                        }))
                    },
                    issuerRef: {
                        name: issuer.name,
                        kind: issuer.kind
                    }
                }
            },
            {
                provider: issuer.provider
            }
        );

        this.name = name;
        this.namespace = pulumi.Output.create(namespace);
        this.secretName = pulumi.Output.create(secretName);
        this.domains = pulumi.Output.create(domains);
    }
}
yeah cool i did think about 'open sourcing' the junk i have but i'm not too happy with how it is atm
i'd be interested in helping out with one though
that ^ is for pre-0.8 cert-manager it uses the deprecated solver references
c

curved-doctor-83600

12/11/2019, 5:16 PM
@better-rainbow-14549 I would also be interested in having cert-manager resources as first-class citizens in pulumi instead of having to deal with the yaml. If you want to open source your efforts, you can count on me in helping carrying the thing forward, as new cert-manager releases arrive.