https://pulumi.com logo
Title
p

polite-sandwich-68547

04/12/2023, 4:25 PM
hi guys, how do you convert a
pulumi.StringOutput
to a normal go
string
?
n

nice-guitar-7761

04/12/2023, 4:30 PM
It is not recommended, but you can use
// import "<http://github.com/pulumi/pulumi/sdk/v3/go/pulumi/internals|github.com/pulumi/pulumi/sdk/v3/go/pulumi/internals>"

result, _ := internals.UnsafeAwaitOutput(context.Background(), myPulumiOutput)
strValue := result.Value.(string)
if I'm not wrong
p

polite-sandwich-68547

04/12/2023, 4:36 PM
I'll try now @nice-guitar-7761, thank you very much!
b

billowy-army-68599

04/12/2023, 4:50 PM
yeah i wouldn’t do that. if you want to use a string, do something inside an
ApplyT
p

polite-sandwich-68547

04/12/2023, 4:53 PM
I tried using
ApplyT
although I keep getting empty strings
bucketName := pulumi.String("")
        bucket.ID().ApplyT(func(id pulumi.ID) (string, error) {
            bucketName = pulumi.String(string(id))
            return "", nil
        })
n

nice-guitar-7761

04/12/2023, 4:56 PM
Ah, that's probably because the input type of your function in the ApplyT should be a golang type and not a pulumi type there :)
i

incalculable-parrot-23117

04/12/2023, 5:00 PM
@polite-sandwich-68547
ApplyT
doesn't run right away. It runs when the value becomes available, so trying to extract it like that will give you an empty string. The right way of doing this is:
bucketName := bucket.ID().ApplyT(func(id pulumi.ID) (string, error) {
  return pulumi.String(string(id)), nil
}).(pulumi.StringOutput)
And then
bucketName
is a
StringOutput
, not
string
. Although for this specific case, you can convert an
IDOutput
(
bucket.ID()
) into a
StringOutput
with the
ToStringOutput()
method of `IDOutput`:
bucketName := bucket.ID().ToStringOutput()
With bucketName as a StringOutput, you can pass it into, say
pulumi.Sprintf
to get back a formatted string output. For example:
bucketName := bucket.ID().ToStringOutput()
msg := pulumi.Sprintf("Data stored in bucket %s", bucketName)
p

polite-sandwich-68547

04/12/2023, 5:01 PM
func Sprintf(format string, args ...interface{}) StringOutput {
still returns a
StringOutput
though, when we need a
string
msg will be of type
StringOutput
i

incalculable-parrot-23117

04/12/2023, 5:03 PM
Yeah, message will be a StringOutput.
p

polite-sandwich-68547

04/12/2023, 5:03 PM
we need
string
i

incalculable-parrot-23117

04/12/2023, 5:04 PM
You can't/aren't supposed to break out of a StringOutput—the value isn't available yet. For example, for
bucket.ID()
above, when you run
pulumi preview
, the bucket doesn't have an ID because it hasn't been created yet, so the system can't give you an ID back. The IDOutput is a placeholder for a value that will become available in the future when this code runs for real (and even then, the ID won't be available right away because the operation doesn't run synchronously, but we can skip that detail for now). The idea is that you can feed this
Output
value as input to anything else, and among other things, their dependency relation will be established.
p

polite-sandwich-68547

04/12/2023, 5:05 PM
then I want to wait until the StringOutput and ready and then gather the string
thought that's what ApplyT is doing
b

billowy-army-68599

04/12/2023, 5:05 PM
you can only retrieve a string inside an
ApplyT
You can’t assign the result of an
ApplyT
to another value and use it elsewhere, because the value is only know inside the ApplyT
i

incalculable-parrot-23117

04/12/2023, 5:06 PM
Sorry, can I ask a different question: Where are you writing this string?
p

polite-sandwich-68547

04/12/2023, 5:06 PM
oh that's good to know, means I can execute whatever I'm doing outside inside the ApplyT
sure, I'll show you what I'm doing: creating a bucket, waiting for it to be ready then using s3sync library to sync files to it
syncManager := s3sync.New(sess)
	// Get the bucket name as a string using Apply
	bucketName, err := internals.UnsafeAwaitOutput(context.Background(), bucket.Bucket)
	if err != nil {
		return err
	}

	err = syncManager.Sync("../../build", fmt.Sprintf("<s3://%s>", bucketName.Value.(string)))
	if err != nil {
		return err
	}
I tried syncing using pulumi's s3.NewBucketObject, although it got messy pretty fast
i

incalculable-parrot-23117

04/12/2023, 5:10 PM
Just a moment; I think we had something to assist here. I need to ask around about it.
b

billowy-army-68599

04/12/2023, 5:11 PM
p

polite-sandwich-68547

04/12/2023, 5:11 PM
what do you guys think if I put the syncManager code inside the ApplyT func?
i

incalculable-parrot-23117

04/12/2023, 5:11 PM
Aha, that's it! Thanks, @billowy-army-68599.
If you put syncManager inside the ApplyT folder, it'll behave in unexpected ways. At minimum, it'll run twice: once during
preview
and once during
update
. You can verify this by trying to run
fmt.Println
inside the ApplyT function. Trying to have side-effects on the world from inside ApplyT breaks things, somewhat. Do consider the synced-folder package @billowy-army-68599 linked to above. The S3BucketFolderArgs takes a StringInput as argument, so you can feed bucket.ID into it directly. It'll treat every file in the folder as a separate resource, so it'll update files as needed.
p

polite-sandwich-68547

04/12/2023, 5:16 PM
hmm, could use a sync.Once object to avoid double runs
i

incalculable-parrot-23117

04/12/2023, 5:17 PM
With syncedfolder, your original sample becomes:
bucket := // ...

bucketName := bucket.ID().ToStringOutput()
syncedfolder.NewS3BucketFolder(ctx, "build", &synced.S3BucketFolderArgs{
  Path:       pulumi.String("../../build"),
  BucketName: bucketName,
  Acl: // ...
})
p

polite-sandwich-68547

04/12/2023, 5:17 PM
will definitely refactor using Synced folder, thank you for sharing guys!
loving pulumi more and more every day