pulumi / pulumi-hcloud

A Hetzner Cloud Pulumi resource package, providing multi-language access to Hetzner Cloud
Apache License 2.0
69 stars 7 forks source link

cannot use mainNetwork.CustomResourceState.ID() (type pulumi.IDOutput) as type pulumi.IntInput #52

Open sebandgo opened 3 years ago

sebandgo commented 3 years ago

Hello, I have followed the example from Pulumi Documentation (https://www.pulumi.com/docs/reference/pkg/hcloud/networksubnet/) but it looks like that the hcloud.NewNetworkSubnet doesn't gets the .ID() output from hcloud.NewNetwork.

Expected behavior

Subnet to be created under the right Network CIDR.

Current behaviour

mainNetwork.ID() output cannot be used as Input for Subnets.

./main.go:104:7: cannot use mainNetwork.CustomResourceState.ID() (type pulumi.IDOutput) as type pulumi.IntInput in field value:
pulumi.IDOutput does not implement pulumi.IntInput (missing ToIntOutput method)

As you can see below if I provide the ID manually using pulumi.Int(1061706) it works.

Steps to reproduce

package main

import (
    "fmt"

    "github.com/pulumi/pulumi-hcloud/sdk/go/hcloud"
    "github.com/pulumi/pulumi/sdk/v2/go/pulumi"
)

func main() {

    pulumi.Run(func(ctx *pulumi.Context) error {

        //
        // Network
        //

        selectedNetwork := map[string]string{}

        productionNetwork := map[string]string{
            "production": "10.1.0.0/16",
        }

        stagingNetwork := map[string]string{
            "staging": "10.2.0.0/16",
        }

        developmentNetwork := map[string]string{
            "development": "10.3.0.0/16",
        }

        switch ctx.Stack() {

        case "production":
            selectedNetwork = productionNetwork
        case "staging":
            selectedNetwork = stagingNetwork
        case "development":
            selectedNetwork = developmentNetwork

        }

        for networkName, networkCIDR := range selectedNetwork {

            mainNetwork, err := hcloud.NewNetwork(
                ctx,
                networkName,
                &hcloud.NetworkArgs{
                    IpRange: pulumi.String(networkCIDR),
                },
            )

            if err != nil {
                return err
            }

            fmt.Println(mainNetwork.ID())

            ctx.Export("network/"+networkName+"/id", mainNetwork.ID())
            ctx.Export("network/"+networkName+"/name", mainNetwork.Name)

            //
            // Subnets
            //

            selectedSubnet := map[string]string{}

            productionSubnets := map[string]string{
                "kubernetes": "10.1.1.0/24",
                "databases":  "10.1.2.0/24",
            }

            stagingSubnets := map[string]string{
                "kubernetes": "10.2.1.0/24",
                "databases":  "10.2.2.0/24",
            }

            developmentSubnets := map[string]string{
                "kubernetes": "10.3.1.0/24",
                "databases":  "10.3.2.0/24",
            }

            switch ctx.Stack() {

            case "production":
                selectedSubnet = productionSubnets
            case "staging":
                selectedSubnet = stagingSubnets
            case "development":
                selectedSubnet = developmentSubnets

            }

            for subnetName, subnetCIDR := range selectedSubnet {

                _, err := hcloud.NewNetworkSubnet(
                    ctx,
                    subnetName,
                    &hcloud.NetworkSubnetArgs{
                        NetworkId:   mainNetwork.ID(), //pulumi.Int(1061706),
                        Type:        pulumi.String("cloud"),
                        NetworkZone: pulumi.String("eu-central"),
                        IpRange:     pulumi.String(subnetCIDR),
                    },
                )

                if err != nil {
                    return err
                }

            }
        }

        return nil

    })
}

Context (Environment)

go version go1.16.2 darwin/amd64
github.com/pulumi/pulumi-hcloud/sdk v0.6.2
github.com/pulumi/pulumi/sdk/v2 v2.20.0 [v2.22.0]

Affected feature

sebandgo commented 3 years ago

Same issue with cloud.NewVolume and hcloud.NewVolumeAttachment, the [resource].ID() isn't passed. Works with pulumi.Int([Resource ID]):

...
            mdbNode, err := hcloud.NewServer(
                ctx,
                "just-a-node",
                ...)

            if err != nil {
                return err
            }

            //
            // Volume
            //

            dataVolume, err = hcloud.NewVolume(ctx, nodeName+"-data", &hcloud.VolumeArgs{
                // _, err = hcloud.NewVolume(ctx, nodeName+"-data", &hcloud.VolumeArgs{
                Location: pulumi.String("ngb1"),
                Size:     pulumi.Int(50),
                // ServerId:  pulumi.Int(10733537), //mdbNode.ID(), <- Commented so we can use hcloud.NewVolumeAttachment
                Format:    pulumi.String("xfs"),
                Automount: pulumi.Bool(true),
            })

            if err != nil {
                return err
            }

            //
            // Volume Attachment
            //

            _, err = hcloud.NewVolumeAttachment(ctx, "main", &hcloud.VolumeAttachmentArgs{
                VolumeId:  pulumi.Int(10733537), // dataVolume.ID()
                ServerId:  pulumi.Int(10103105), // mdbNode.ID()
                Automount: pulumi.Bool(true),
            })

            if err != nil {
                return err
            }
...

SDK Version:

github.com/pulumi/pulumi-hcloud/sdk v0.7.0
github.com/pulumi/pulumi/sdk/v2 v2.23.1
brpaz commented 3 years ago

Same issue with v1.0 and Pulumi v3 sdk.

Any workaround or way to fix this?

TobiasAhnert commented 3 years ago

Same problem here. I myself are no go-guru, but the following ugly trick seemed to work for now and created some resources successfully. However, it is not elegant this way, so can anyone fix this for good?

        mynet, err := hcloud.NewNetwork(ctx, "demo-network", &hcloud.NetworkArgs{
            IpRange: pulumi.String("10.0.1.0/24"),
        })
        if err != nil {
            return err
        }

        convertedNetId := mynet.ID().ToStringOutput().ApplyT(func(id string) (int, error) {
            return strconv.Atoi(strings.Split(id, "-")[0])
        }).(pulumi.IntOutput)

        mysubnet, err := hcloud.NewNetworkSubnet(ctx, "foonet", &hcloud.NetworkSubnetArgs{
            NetworkId:   convertedNetId,
            Type:        pulumi.String("cloud"),
            NetworkZone: pulumi.String("eu-central"),
            IpRange:     pulumi.String("10.0.1.0/24"),
        })
        if err != nil {
            return err
        }
RobbieMcKinstry commented 2 years ago

Hm, I came across this issue today on my first day as a Pulumi employee. Am I correct in saying the reason pulumi.IDOutput does not have a conversion function to pulumi.IntInput is because the IDOutput is provider-specific, and not necessarily an integer? I was working with the DigitalOcean provider, there are a couple of locations where I hit this same situation. The DO LoadBalancer type expects a list of Droplet IDs as an array of integers, but the *Droplet.ID() method provides a pulumi.IDOutput.

Here's my (overly verbose) workaround:

var conversionCallback = func(val string) (int, error) {
    return strconv.Atoi(val)
}
// "droplet" is is struct of type "*digitalocean.Droplet"
// after this line, dropletId is a "pulumi.Int"
var dropletId = droplet.ID().ToStringOutput().ApplyT(conversionCallback).(pulumi.IntOutput)

I wonder, should providers offer a package-level conversion function between pulumi.IDOutput and pulumi.Int if their concrete implementation is an Int under the hood?

daenney commented 1 year ago

I think I'm running into the same issue with hcloud.PrimaryIp and passing them into hcloud.ServerArgs.

ip, _ := hcloud.NewPrimaryIp(....)

s, _ := hcloud.NewServer(ctx, "server", &hcloud.ServerArgs{
    PublicNets: hcloud.ServerPublicNetArray{
            &hcloud.ServerPublicNetArgs{
                Ipv4Enabled: pulumi.Bool(true),
                Ipv4: ip.ID(),
            },
        },
}

cannot use ip.ID() (value of type pulumi.IDOutput) as pulumi.IntPtrInput value in struct literal: pulumi.IDOutput does not implement pulumi.IntPtrInput (missing method ToIntPtrOutput)compiler InvalidIfaceAssign

This makes it really difficult to use any of the resources created by the provider as inputs for a later resource.

marco-m commented 1 year ago

The absolute minimum Go code to convert the ID from string to int is (in May 2023):

network, err := hcloud.NewNetwork(ctx, "foo", &hcloud.NetworkArgs{
....
_, err = hcloud.NewNetworkSubnet(ctx, "bar", &hcloud.NetworkSubnetArgs{
    NetworkId: network.ID().ApplyT(strconv.Atoi).(pulumi.IntOutput),
...

~Although with the current pulumi + hcloud versions, one needs --skip-preview to be able to perform a pulumi up, see #175.~ UPDATE: As of 2023-06-07, github.com/pulumi/pulumi-hcloud/sdk v1.12.1 github.com/pulumi/pulumi/sdk/v3 v3.69.0

pulumi up shows correctly the preview!

bo0ts commented 1 year ago

This issue is really surprising to new users and even the documented examples are just wrong and don't work out of the box. See the go code in https://www.pulumi.com/registry/packages/hcloud/api-docs/networksubnet/

MindTooth commented 1 year ago

I've for a long time been looking into using Pulumi instead of Terraform, but even before I'm starting, the examples and the provider does not work out of the box. I'm not sure what to make out of this? 🤷🏻‍♂️

I'm using a banal example from the docs, and that already throws an error:

firewall, err := hcloud.NewFirewall(ctx, "myfirewall", &hcloud.FirewallArgs{
    Rules: hcloud.FirewallRuleArray{
        &hcloud.FirewallRuleArgs{
            Direction: pulumi.String("in"),
            Protocol:  pulumi.String("icmp"),
            SourceIps: pulumi.StringArray{
                pulumi.String("0.0.0.0/0"),
                pulumi.String("::/0"),
            },
        },
        &hcloud.FirewallRuleArgs{
            Direction: pulumi.String("in"),
            Protocol:  pulumi.String("tcp"),
            Port:      pulumi.String("22"),
            SourceIps: pulumi.StringArray{
                pulumi.String("0.0.0.0/0"),
                pulumi.String("::/0"),
            },
        },
    },
})

... 
_, err = hcloud.NewServer(ctx, "node1", &hcloud.ServerArgs{
    Image:      pulumi.String("debian-12"),
    ServerType: pulumi.String("cx11"),
    Location:   pulumi.String("hel1"),
    FirewallIds: pulumi.IntArray{
        firewall.ID(),
    },
})
./main.go:47:5: cannot use firewall.ID() (value of type pulumi.IDOutput) as pulumi.IntInput value in array or slice literal: pulumi.IDOutput does not implement pulumi.IntInput (missing method ToIntOutput)

My current take is that I need hacks to make it work, or continue using Terraform to have proper code?

daenney commented 1 year ago

You can do what the previous poster suggested, firewall.ID().ApplyT(strconv.Atoi).(pulumi.IntOutput)

bo0ts commented 1 year ago

You can do what the previous poster suggested, firewall.ID().ApplyT(strconv.Atoi).(pulumi.IntOutput)

Broken examples for simple use-cases still do not inspire much confidence which is a pity considering how good pulumi is.

srhb commented 1 year ago

Is there a way to workaround this issue for the yaml provider? (technically in json. I don't see anything relevant in the builtin fn::s)