https://pulumi.com logo
#golang
Title
# golang
s

salmon-winter-68864

11/30/2023, 4:07 AM
Hi Team - I'm hoping someone here can help be determine how Pulumi decides what is executed in the main func of a Go file. I'm kicking the tyres of Pulumi and looking to automate my cloud build using Linode. ATPIT I can create a new Linode instance, bind ssh keys so that I can run a remote command. However, when attempting to
pulumi up
, the time sleep always runs first before the instance create in the following example and I don't understand why (even with the dependsOn)
Copy code
func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		linodeConfig := LinodeConfig{}.New()
		roxyConfig := config.New(ctx, "roxy")
		roxyConfig.RequireObject("linode", linodeConfig)
		if linodeConfig.SSH.Key.Private == "" {
			return fmt.Errorf("No SSH Private key found")
		}

		if linodeConfig.SSH.Key.Public == "" {
			return fmt.Errorf("No SSH Public key found")
		}

		// Create a linode resource (Linode Instance)
		instance, err := linode.NewInstance(ctx, "roxy", &linode.InstanceArgs{
			Type:   pulumi.String(linodeConfig.Instance.Type),
			Region: pulumi.String(linodeConfig.Instance.Region),
			Image:  pulumi.String(linodeConfig.Instance.Image),
			AuthorizedKeys: pulumi.StringArray{
				pulumi.String(linodeConfig.SSH.Key.Public),
			},
		})

		if err != nil {
			return err
		}

		fmt.Println("sleeping for a min")
		time.Sleep(time.Minute)

		_, err = remote.NewCommand(ctx, "echoCmd", &remote.CommandArgs{
			Create: pulumi.String("echo 'Hello, World!' > boo.txt"),
			Connection: remote.ConnectionArgs{
				Host:       instance.IpAddress,
				User:       pulumi.String("root"),
				PrivateKey: pulumi.StringPtr(linodeConfig.SSH.Key.Private),
			},
		}, pulumi.DependsOn([]pulumi.Resource{instance}))
		if err != nil {
			return err
		}

		return nil
	})
}
For brevity I added a plain time.Sleep - but I originally had a for loop to check the status of the linode instance, and based on this, I wanted to break and then run the remote command. But the for loop always ran first before the instance when calling
pulumi up
so of course it always failed. As does this example with the sleep. I have a feeling I will need to use "Component resources" to build my platform, but I'm currently trying to get a taste of Pulumi and this is driving me a little mental. Any help/advice welcome. I didn't imagine my Go code would be run in a different order than I wrote it?
Notice the
sleeping for a min
occurs 1st which means every
up
fails to process the new command because the sleep happens first? I don't understand how the execution order is altered?
I've dropped a few fmt.Println's around to confirm that the Go is executed in the correct order, so now I need to figure out how Pulumi determines the plan order to execute and why it wants to run the remoteCommand on the instance before the instance is created. Even with the dependsOn
For fun I ran an infinite for loop on an ApplyT on the instance already created. Turns out the "status" will change so there's definitely more happening behind the scenes. Pulumi must be making requests on my behalf to check the status, so I have no need to Get an instance and "wait". Learning....stay tuned
Ok so I have my remote.Command running on my new instance. I create a linode.NewInstance, then in another method I "wait" for the instance.Status to equal the status I want in an ApplyT on the status pulumi.StringOutput field. My ApplyT method closes a context.WithTimeOut chan when the status equals so code execution continues, otherwise an err is returned on Deadline exceeded. Being my first play with Pulumi, I thought that I needed to make a specific GetInstance request and check the Instance status. It would seem that Pulumi "knows" that I have an ApplyT on an instance field, and makes the required requests for me to run the ApplyT on change. Which is kinda magic and wasn't intuitive to me. Happily learning here 👍