is there a way to wait for things, like what you c...
# general
b
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
Did you try dependsOn?
b
dependsOn
what?
e
the ClusterIssuer
b
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
hmmm
Does it timeout?
or do that immediately?
b
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
Have you tried setting parent?
b
of the clusterissuer?
e
yes
b
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
yeah probably
this is on DO right?
b
it is, but i doubt it matters.
e
and your passing in the provider as well?
b
yup. if i run it again it works, so it's clearly just a resource creation timing issue
e
b
and wait for what?
e
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
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
That would d be really cool if that worked
Let me know if it does
b
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
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
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
cool i thought about doing that but i only hit errors about namespace so far
b
you're using the all-in-one yaml, yeah? same as the link in mine there ^
b
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
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
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
@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.
164 Views