pulumi / pulumi-component-provider-ts-boilerplate

Apache License 2.0
25 stars 6 forks source link

Access bucket properties #34

Open pasqualet opened 2 years ago

pasqualet commented 2 years ago

What happened?

I've modified the example project to export the bucket id as output, but pulumi fails to get the bucket.id property.

Steps to reproduce

Just add a line to export the bucketId:

diff --git a/examples/simple/index.ts b/examples/simple/index.ts
index 9c397a0..39d23b5 100644
--- a/examples/simple/index.ts
+++ b/examples/simple/index.ts
@@ -6,3 +6,4 @@ const page = new xyz.StaticPage("page", {

 export const bucket = page.bucket;
 export const url = page.websiteUrl;
+export const bucketId = page.bucket.id;

Expected Behavior

The page.bucket.id is exported in the bucketId constant.

Actual Behavior

Pulumi fails with the following error:

% pulumi pre
Previewing update (test)

View Live: https://app.pulumi.com/XXX

     Type                 Name         Plan       Info
 +   pulumi:pulumi:Stack  simple-test  create     1 error

Diagnostics:
  pulumi:pulumi:Stack (simple-test):
    error: Running program '/Users/pasquale/GIT/pulumi-component-provider-ts-boilerplate/examples/simple' failed with an unhandled exception:
    TSError: ⨯ Unable to compile TypeScript:
    index.ts(9,37): error TS2339: Property 'id' does not exist on type 'Output<any>'.
      Property 'id' does not exist on type 'OutputInstance<any>'.

Output of pulumi about

CLI
Version      3.39.3
Go Version   go1.19.1
Go Compiler  gc

Plugins
NAME    VERSION
nodejs  unknown
xyz     0.0.1

Host
OS       darwin
Version  12.3.1
Arch     arm64

Additional context

No response

Contributing

Vote on this issue by adding a 👍 reaction. To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

jkisk commented 2 years ago

Can you tell me a little bit more about what you are trying to do here? This is a template repo to create components and you might instead be looking for a component like: https://github.com/pulumi/pulumi-aws-static-website or https://github.com/pulumi/pulumi-awsx.

pasqualet commented 2 years ago

I'm developing a set of Pulumi components for AWS with TypeScript and I want to publish them as a Pulumi Package. So I'm testing this repository to build the pacakge, but I've seen this behaviour and I want to figure out if it's an expected behaviour, a bug or something that I'm doing wrong.

Each of my component creates many AWS resources and I would like that the users can access their properties, because in this way they can take the values for the declaration of other resources (from my package or from other ones).

As far as I can, I see that the resource properties created by the pulumi-eks package can be accessed and this is what I'm expecting when using this template.

This is my test with pulumi-eks:

import * as pulumi from "@pulumi/pulumi";
import * as eks from "@pulumi/eks";

const cluster1 = new eks.Cluster("test");
export const id = cluster1.eksCluster.id;

Pulumi results:

% pulumi up -y
Previewing update (dev)

View Live: https://app.pulumi.com/XXX

     Type                                   Name                                    Plan
 +   pulumi:pulumi:Stack                    example-cluster-dev                     create
 +   └─ eks:index:Cluster                   test                                    create
 +      ├─ eks:index:ServiceRole            test-instanceRole                       create
 +      │  ├─ aws:iam:Role                  test-instanceRole-role                  create
 +      │  ├─ aws:iam:RolePolicyAttachment  test-instanceRole-3eb088f2              create
 +      │  ├─ aws:iam:RolePolicyAttachment  test-instanceRole-e1b295bd              create
 +      │  └─ aws:iam:RolePolicyAttachment  test-instanceRole-03516f97              create
 +      ├─ eks:index:ServiceRole            test-eksRole                            create
 +      │  ├─ aws:iam:Role                  test-eksRole-role                       create
 +      │  └─ aws:iam:RolePolicyAttachment  test-eksRole-4b490823                   create
 +      ├─ eks:index:RandomSuffix           test-cfnStackName                       create
 +      ├─ aws:iam:InstanceProfile          test-instanceProfile                    create
 +      ├─ aws:ec2:SecurityGroup            test-eksClusterSecurityGroup            create
 +      ├─ aws:ec2:SecurityGroupRule        test-eksClusterInternetEgressRule       create
 +      ├─ aws:eks:Cluster                  test-eksCluster                         create
 +      ├─ aws:ec2:SecurityGroup            test-nodeSecurityGroup                  create
 +      ├─ pulumi:providers:kubernetes      test-eks-k8s                            create
 +      ├─ eks:index:VpcCni                 test-vpc-cni                            create
 +      ├─ aws:ec2:SecurityGroupRule        test-eksNodeInternetEgressRule          create
 +      ├─ aws:ec2:SecurityGroupRule        test-eksClusterIngressRule              create
 +      ├─ aws:ec2:SecurityGroupRule        test-eksNodeClusterIngressRule          create
 +      ├─ aws:ec2:SecurityGroupRule        test-eksExtApiServerClusterIngressRule  create
 +      ├─ kubernetes:core/v1:ConfigMap     test-nodeAccess                         create
 +      ├─ aws:ec2:SecurityGroupRule        test-eksNodeIngressRule                 create
 +      ├─ aws:ec2:LaunchConfiguration      test-nodeLaunchConfiguration            create
 +      ├─ aws:cloudformation:Stack         test-nodes                              create
 +      └─ pulumi:providers:kubernetes      test-provider                           create

Outputs:
    id: output<string>

Resources:
    + 27 to create

Updating (dev)

View Live: https://app.pulumi.com/XXX

     Type                                   Name                                    Status
 +   pulumi:pulumi:Stack                    example-cluster-dev                     created
 +   └─ eks:index:Cluster                   test                                    created
 +      ├─ eks:index:ServiceRole            test-instanceRole                       created
 +      │  ├─ aws:iam:Role                  test-instanceRole-role                  created
 +      │  ├─ aws:iam:RolePolicyAttachment  test-instanceRole-03516f97              created
 +      │  ├─ aws:iam:RolePolicyAttachment  test-instanceRole-3eb088f2              created
 +      │  └─ aws:iam:RolePolicyAttachment  test-instanceRole-e1b295bd              created
 +      ├─ eks:index:ServiceRole            test-eksRole                            created
 +      │  ├─ aws:iam:Role                  test-eksRole-role                       created
 +      │  └─ aws:iam:RolePolicyAttachment  test-eksRole-4b490823                   created
 +      ├─ eks:index:RandomSuffix           test-cfnStackName                       created
 +      ├─ aws:ec2:SecurityGroup            test-eksClusterSecurityGroup            created
 +      ├─ aws:iam:InstanceProfile          test-instanceProfile                    created
 +      ├─ aws:ec2:SecurityGroupRule        test-eksClusterInternetEgressRule       created
 +      ├─ aws:eks:Cluster                  test-eksCluster                         created
 +      ├─ aws:ec2:SecurityGroup            test-nodeSecurityGroup                  created
 +      ├─ eks:index:VpcCni                 test-vpc-cni                            created
 +      ├─ pulumi:providers:kubernetes      test-eks-k8s                            created
 +      ├─ kubernetes:core/v1:ConfigMap     test-nodeAccess                         created
 +      ├─ aws:ec2:SecurityGroupRule        test-eksNodeInternetEgressRule          created
 +      ├─ aws:ec2:SecurityGroupRule        test-eksExtApiServerClusterIngressRule  created
 +      ├─ aws:ec2:SecurityGroupRule        test-eksNodeClusterIngressRule          created
 +      ├─ aws:ec2:SecurityGroupRule        test-eksNodeIngressRule                 created
 +      ├─ aws:ec2:SecurityGroupRule        test-eksClusterIngressRule              created
 +      ├─ aws:ec2:LaunchConfiguration      test-nodeLaunchConfiguration            created
 +      ├─ aws:cloudformation:Stack         test-nodes                              created
 +      └─ pulumi:providers:kubernetes      test-provider                           created

Outputs:
    **id: "test-eksCluster-6e9e17e"**

Resources:
    + 27 created

Duration: 10m57s
pasqualet commented 2 years ago

I've notice that the example project is missing the @pulumi/aws dependency and I suppose this is the reason of Pulumi beliving the bucket property is has type 'Output'.

This is the change I'm appling in the package.json:

diff --git a/examples/simple/package.json b/examples/simple/package.json
index 641c4e3..2435e14 100644
--- a/examples/simple/package.json
+++ b/examples/simple/package.json
@@ -4,6 +4,7 @@
         "@types/node": "^10.0.0"
     },
     "dependencies": {
+        "@pulumi/aws": "^5.13.0",
         "@pulumi/pulumi": "^3.0.0"
     }
 }

Installing the dependencies (npm install) and running pulumi again, the components are created successfully but there is still an issue because the bucket properties are undefined.

The index.ts:

import * as xyz from "@pulumi/xyz";

const page = new xyz.StaticPage("page", {
    indexContent: "<html><body><p>Hello world!</p></body></html>",
});

export const bucket = page.bucket;
export const url = page.websiteUrl;
export const bucketId = page.bucket.id;
export const bucketArn = page.bucket.arn;
export const bucketDomainName = page.bucket.bucketDomainName;
export const bucketRegion = page.bucket.region;

Pulumi up:

% pulumi up -y
Previewing update (test)

View Live: https://app.pulumi.com/XXX

     Type                          Name           Plan
 +   pulumi:pulumi:Stack           simple-mytest  create
 +   └─ xyz:index:StaticPage       page           create
 +      └─ aws:s3:Bucket           page           create
 +         ├─ aws:s3:BucketPolicy  bucketPolicy   create
 +         └─ aws:s3:BucketObject  page           create

Outputs:
    bucket: "urn:pulumi:mytest::simple::xyz:index:StaticPage$aws:s3/bucket:Bucket::page"
    url   : output<string>

Resources:
    + 5 to create

Updating (test)

View Live: https://app.pulumi.com/XXX

     Type                          Name           Status
 +   pulumi:pulumi:Stack           simple-mytest  created
 +   └─ xyz:index:StaticPage       page           created
 +      └─ aws:s3:Bucket           page           created
 +         ├─ aws:s3:BucketPolicy  bucketPolicy   created
 +         └─ aws:s3:BucketObject  page           created

Outputs:
    bucket: "page-30c0cf2"
    url   : "page-30c0cf2.s3-website-eu-west-1.amazonaws.com"

Resources:
    + 5 created

Duration: 8s
lblackstone commented 2 years ago

I believe you will need to register those outputs in the ComponentResource so that Pulumi will not mark it as complete until those outputs are ready.

This code currently only sets bucket and url.

wlsidlsi commented 1 year ago

Jumping in here =) cause I too have had this same issue! I tried asking in the pulumi slacking channel. Someone suggested checking out https://github.com/pulumi/pulumi-awsx/issues/972 which is similar but didn't work for me.

I think I am in similar situation where I create a aws resource and would like to pass it back as state property for my larger construct and be able to access that resources properties in the client code without passing each property and having to define each one in schema.json. With the ref to the type in schema.json I can see in sdk that the typescript object is "aws:s3/bucket:Bucket" but the actual staticPage.bucket doesn't contain a bucket object it contains a string with bucket's ID. At the end of static page in provider it registers the output.

this.registerOutputs({
            bucket,
            websiteUrl: bucket.websiteEndpoint,
        });

I would expect what the intent here was to register the output bucket as a whole and it's properties not simply the bucketId.

Using the original posters example though I thought you would have to do something similar to the following on the client side because arn wouldn't exist on bucket as bucket is an output. This is the case though b is resolving to a string, the id.

export const bucketArn = page.bucket.apply(b => b.arn);

How should I change the registerOutputs to achieve the above. I tried the following after reading this but it still shows arn doesn't exist on b cause b is a string.

this.registerOutputs({
            bucket,
            arn: bucket.arn,
            websiteUrl: bucket.websiteEndpoint,
        });

Looking again, if state.bucket is just going to return a string, id, why set the ref in schema.json.

        urn: staticPage.urn,
        state: {
            bucket: staticPage.bucket,
            websiteUrl: staticPage.websiteUrl,
        },
    };

schema.json

"properties": {
                "bucket": {
                    "$ref": "/aws/v4.0.0/schema.json#/resources/aws:s3%2Fbucket:Bucket",
                    "description": "The bucket resource."
                },
                "websiteUrl": {
                    "type": "string",
                    "description": "The website URL."
                }
            },

In this example why are they even doing websiteUrl in schema.json it redundant they have already declared bucket is /aws/v4.0.0/schema.json#/resources/aws:s3%2Fbucket:Bucket then they are defining the string websiteUrl which is property of bucket and equates to the following with more clarity

provider.ts
return {
        urn: staticPage.urn,
        state: {
            bucketId: staticPage.bucket, // here staticPage.bucket is resolving the output to a Id
            websiteUrl: staticPage.websiteUrl,
        },
    };

schema.json
"properties": {
                "bucketId": {
                    "type": "string",
                    "description": "The bucket resource."
                },
                "websiteUrl": {
                    "type": "string",
                    "description": "The website URL."
                }
            },

Just really excited to understand so forgive my super long post.