https://pulumi.com logo
#general
Title
# general
i

icy-london-58403

02/18/2020, 8:27 AM
I followed the guide on dynamic providers and I have it working fairly well. I am making a dynamic provider for IPAM (IP Address Management). I've wrapped Netbox with typescript and I my dynamic provider calls my wrapper for CRUD. Everything creates and updates great and everything destroys if I use the
pulumi destroy
command. But if I comment out one of my custom resources, it tries to connect to it with default provider values. I pass the provider a token and a url. I can tell by the errors that it is defaulting to localhost port 80 with no token. So it seems the provider doesn't hold onto these values in the state and it needs the code to know these values during deletions. Is this normal behaviour or is there something I can do to enhance my setup?
code partial:
Copy code
class PrefixProvider implements pulumi.dynamic.ResourceProvider {
    public async check(olds: any, news: any): Promise<pulumi.dynamic.CheckResult> {
        pulumi.log.debug("check prefix")
        const failures = []

        if (news[prefixProp] === undefined) {
            failures.push({property: prefixProp, reason: `required property '${prefixProp}' missing`})
        }

        return { inputs: news, failures }
    }

    public async create(inputs: PrefixInputs): Promise<pulumi.dynamic.CreateResult> {
        pulumi.log.debug("create prefix")

        let netboxClient = new Client({
            host: inputs.endpoint,
            token: inputs.token,
        })

        let data:createPrefixData

        data = {
            prefix: inputs.prefix,
            tags: [
                "pulumi_managed",
            ]
        }

        if(inputs.description) {data.description = inputs.description}
        if(inputs.vrfID      ) {data.vrf         = inputs.vrfID      }
        if(inputs.familyID   ) {data.family      = inputs.familyID   }
        if(inputs.siteID     ) {<http://data.site|data.site>        = inputs.siteID     }
        if(inputs.is_pool    ) {data.is_pool     = inputs.is_pool    }
        if(inputs.roleID     ) {data.role        = inputs.roleID     }
        if(inputs.status     ) {data.status      = inputs.status     }
        if(inputs.vlan       ) {data.vlan        = inputs.vlan       }
        
        const response = await netboxClient.createPrefix({
            prefix: inputs.prefix,
            description: inputs.description,
            vrf: inputs.vrfID,
            family: inputs.familyID,
            site: inputs.siteID,
            is_pool: inputs.is_pool,
            role: inputs.roleID,
            status: inputs.status,
            vlan: inputs.vlan,
            tags: [
                "pulumi_managed",
            ]
        })

        // pulumi.log.debug("axios config: create prefix")
        // pulumi.log.debug(inspect(response.config))

        return {
            id:  response.data.id.toString(),
            outs: {
                endpoint: inputs.endpoint,
                token: inputs.token,
            },
        }
    }

    public async update(id: any, olds: PrefixInputs, news: PrefixInputs): Promise<pulumi.dynamic.UpdateResult> {
        pulumi.log.debug("update prefix")
        let netboxClient = new Client({
            host: news.endpoint,
            token: news.token,
        })

        let data:updatePrefixData

        data = {
            id: id,
            prefix: news.prefix
        }

        if (news.description) {data.description = news.description}
        if (news.vrfID      ) {data.vrf         = news.vrfID      }
        if (news.familyID   ) {data.family      = news.familyID   }
        if (news.siteID     ) {<http://data.site|data.site>        = news.siteID     }
        if (news.is_pool    ) {data.is_pool     = news.is_pool    }
        if (news.roleID     ) {data.role        = news.roleID     }
        if (news.status     ) {data.status      = news.status     }
        if (news.vlan       ) {data.vlan        = news.vlan       }
        
        const response = await netboxClient.updatePrefix(data)

        // pulumi.log.debug("axios config: update prefix")
        // pulumi.log.debug(inspect(response.config))

        return { 
            outs: {
            }, 
        };
    }

    public async delete(id: pulumi.ID, props: PrefixInputs): Promise<void> {
        pulumi.log.debug("delete prefix")

        let netboxClient = new Client({
            host: props.endpoint,
            token: props.token,
        })

        const response = await netboxClient.deletePrefix({
            id: Number(id),
        })
    }

    public async diff(id: pulumi.ID, olds: any, news: any): Promise<pulumi.dynamic.DiffResult> {
        pulumi.log.debug("diff prefix")
        var replaces: string[]
        var stables: string[]

        replaces = [
        ]

        stables = [
        ]

        return { replaces, stables }
    }

    public async read(id: pulumi.ID, props: ReadPrefixInputs): Promise<pulumi.dynamic.ReadResult> {
        let netboxClient = new Client({
            host: props.endpoint,
            token: props.token,
        })

        const response = await netboxClient.getPrefixes({
            id: props.id
        })

        return {
            id: response.data.id, 
            props: {
                prefix: response.data.prefix,
                description: response.data.description,
                vrf: response.data.vrfID,
                family: response.data.familyID,
                site: response.data.siteID,
                is_pool: response.data.is_pool,
                role: response.data.roleID,
                status: response.data.status,
                vlan: response.data.vlan,
            }
        }
    }

}

export class Prefix extends pulumi.dynamic.Resource {
    // public readonly endpoint: pulumi.Output<string>
    // public readonly token: pulumi.Output<string>
    // public readonly description: pulumi.Output<string>

    constructor(name: string, props: PrefixResourceInputs, opts?: pulumi.CustomResourceOptions) {
        super(new PrefixProvider(), name, props, opts)
    }
}
I have the public readonly properties commented out because I can't get it to stop showing errors when I try it. This is probably a separate issue, though.
Most everything is working super well. I'm really excited to have a tool like pulumi to automatically reserve the next n number of cidrs with a given netmask inside of a given range. I'll have these bundled and given paths and then just have other project read the outputs of this project to get all the cidrs that were reserved by the given path. Easy IPAM that is as easy as confirming the path being correct to know if your cidr settings are correct.
example index.js:
Copy code
const firstVPCPrefix = new VPCPrefix("first", {
    endpoint: endpoint,
    token: config.require("token"),
    parentPrefixID: 1,
    platform: "aws",
    region: "usw2",
    name: "first",
    netMaskLength: 16,
    presentationSubnets: [
        {
            prefix_length: 24,
            description: "a",
            tags: ["hi"]
        },
        {
            prefix_length: 24,
            description: "b",
            tags: ["hi"]
        },
        {
            prefix_length: 24,
            description: "c",
            tags: ["hi"]
        }
    ],
    publicSubnets: [
        {
            prefix_length: 24,
            description: "a",
            tags: ["hi"]
        },
        {
            prefix_length: 24,
            description: "b",
            tags: ["hi"]
        },
        {
            prefix_length: 24,
            description: "c",
            tags: ["hi"]
        }
    ],
})

const firstDevChannelsPrefix = new EnvPrefixes("first dev channels", {
    endpoint: endpoint,
    token: config.require("token"),
    parentPrefixID: "212",
    bu: "channels",
    envType: "dev",
    subnetCount: 3,
    subnetMaskLength: 24,
    vpcPrefixPath: "/aws/usw2/vpc/first"

}, {
    parent: firstVPCPrefix, dependsOn: [firstVPCPrefix]
})