Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[program-gen] Emit Output-returning JSON serialization methods without rewriting applies #15371

Merged
merged 1 commit into from
Feb 20, 2024

Conversation

Zaid-Ajaj
Copy link
Contributor

@Zaid-Ajaj Zaid-Ajaj commented Feb 4, 2024

Description

A while ago we started implementing specialized JSON serialization methods for Pulumi programs which can accept nested outputs without having to rewrite and combine applies.

  • Output.SerializeJson in .NET
  • pulumi.jsonStringify in nodejs
  • pulumi.Output.json_dumps in Python

This PR extends program-gen for TypeScript, C# and Python to start emitting these JSON serialization functions (when necessary). The PR special-cases the toJSON PCL function when rewriting applies so that nested outputs aren't rewritted.

Example PCL program and generated results:

Also check out the downstream codegen tests to see improved generated examples

resource vpc "aws:ec2:Vpc" {
	cidrBlock = "10.100.0.0/16"
	instanceTenancy = "default"
}

resource policy "aws:iam/policy:Policy" {
	description = "test"
	policy = toJSON({
		"Version" = "2012-10-17"
		"Interpolated" = "arn:${vpc.arn}:value"
		"Value" = vpc.id
	})
}

Generated TypeScript Before

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

const vpc = new aws.ec2.Vpc("vpc", {
    cidrBlock: "10.100.0.0/16",
    instanceTenancy: "default",
});
const policy = new aws.iam.Policy("policy", {
    description: "test",
    policy: pulumi.all([vpc.arn, vpc.id]).apply(([arn, id]) => JSON.stringify({
        Version: "2012-10-17",
        Interpolated: `arn:${arn}:value`,
        Value: id,
    })),
});

Generated TypeScript After

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

const vpc = new aws.ec2.Vpc("vpc", {
    cidrBlock: "10.100.0.0/16",
    instanceTenancy: "default",
});
const policy = new aws.iam.Policy("policy", {
    description: "test",
    policy: pulumi.jsonStringify({
        Version: "2012-10-17",
        Interpolated: pulumi.interpolate`arn:${vpc.arn}:value`,
        Value: vpc.id,
    }),
});

Generated Python Before

import pulumi
import json
import pulumi_aws as aws

vpc = aws.ec2.Vpc("vpc",
    cidr_block="10.100.0.0/16",
    instance_tenancy="default")
policy = aws.iam.Policy("policy",
    description="test",
    policy=pulumi.Output.all(vpc.arn, vpc.id).apply(lambda arn, id: json.dumps({
        "Version": "2012-10-17",
        "Interpolated": f"arn:{arn}:value",
        "Value": id,
    })))

Generated Python After

import pulumi
import json
import pulumi_aws as aws

vpc = aws.ec2.Vpc("vpc",
    cidr_block="10.100.0.0/16",
    instance_tenancy="default")
policy = aws.iam.Policy("policy",
    description="test",
    policy=pulumi.Output.json_dumps({
        "Version": "2012-10-17",
        "Interpolated": vpc.arn.apply(lambda arn: f"arn:{arn}:value"),
        "Value": vpc.id,
    }))

Generated C# Before

using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var vpc = new Aws.Ec2.Vpc("vpc", new()
    {
        CidrBlock = "10.100.0.0/16",
        InstanceTenancy = "default",
    });

    var policy = new Aws.Iam.Policy("policy", new()
    {
        Description = "test",
        PolicyDocument = Output.Tuple(vpc.Arn, vpc.Id).Apply(values =>
        {
            var arn = values.Item1;
            var id = values.Item2;
            return JsonSerializer.Serialize(new Dictionary<string, object?>
            {
                ["Version"] = "2012-10-17",
                ["Interpolated"] = $"arn:{arn}:value",
                ["Value"] = id,
            });
        }),
    });

});

Generated C# After

using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var vpc = new Aws.Ec2.Vpc("vpc", new()
    {
        CidrBlock = "10.100.0.0/16",
        InstanceTenancy = "default",
    });

    var policy = new Aws.Iam.Policy("policy", new()
    {
        Description = "test",
        PolicyDocument = Output.JsonSerialize(Output.Create(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Interpolated"] = vpc.Arn.Apply(arn => $"arn:{arn}:value"),
            ["Value"] = vpc.Id,
        })),
    });

});

Checklist

  • I have run make tidy to update any new dependencies
  • I have run make lint to verify my code passes the lint check
    • I have formatted my code using gofumpt
  • I have added tests that prove my fix is effective or that my feature works
  • I have run make changelog and committed the changelog/pending/<file> documenting my change
  • Yes, there are changes in this PR that warrants bumping the Pulumi Cloud API version

@pulumi-bot
Copy link
Contributor

Changelog

[uncommitted] (2024-02-04)

Features

  • [programgen/{dotnet,nodejs,python}] Emit Output-returning JSON serialization methods without rewriting applies for top-level function expression
    #15371

@@ -374,3 +374,6 @@ const (
RandomSchema SchemaVersion = "4.11.2"
EksSchema SchemaVersion = "0.37.1"
)

// PulumiDotnetSDKVersion is the version of the Pulumi .NET SDK to use in program-gen tests
const PulumiDotnetSDKVersion = "3.59.0"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this so that program-gen can install and test generated programs against a specific version of .NET Pulumi SDK. In this case, latest version that contains the Output.JsonSerialize function. In case of pulumi convert, it already emits 3.* so latest is resolved there as well

g.genDictionaryOrTuple(w, expr.Args[0])
g.Fgen(w, ")")
if model.ContainsOutputs(expr.Args[0].Type()) {
g.Fgen(w, "Output.JsonSerialize(Output.Create(")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need Output.JsonSerialize(Output.Create(...)) instead of Output.JsonSerialize(...) because the function only accepts Output<T>. We could simplify that in a subsequent PR after we generalize Output.JsonSerialize to accept any T

@Zaid-Ajaj Zaid-Ajaj added this pull request to the merge queue Feb 20, 2024
Merged via the queue into master with commit c5ae74a Feb 20, 2024
57 checks passed
@Zaid-Ajaj Zaid-Ajaj deleted the zaid/use-output-json branch February 20, 2024 17:00
github-merge-queue bot pushed a commit that referenced this pull request Feb 21, 2024
### Features

- [cli/config] Adds an `--open` flag to `pulumi config` command which
resolves the environment listed in the stack configuration.

- [auto/go] Automation API support for `pulumi refresh --preview-only`
  [#15340](#15340)

- [engine] Add support for remote transforms to the engine.

- [pkg/testing] Add a InstallDevReleases option to ProgramTest, to
install pulumi dev SDKs
  [#15387](#15387)

- [programgen/{dotnet,nodejs,python}] Emit Output-returning JSON
serialization methods without rewriting applies for top-level function
expression
  [#15371](#15371)

- [sdk/nodejs] Detect npm and yarn workspaces setups when installing
dependencies
  [#15421](#15421)

- [sdk/nodejs] Use pnpm as package manager if we find a pnpm-lock.yaml
file
  [#15456](#15456)


### Bug Fixes

- [docs] Fixes docs generator parent module computation
  [#15035](#15035)

- [engine] Test and fix the engine filling in args dependencies to
provider calls.
  [#15450](#15450)

- [programgen] Fix infinite recursion when binding invoke signature into
promises without accounting for recursive type references
  [#15463](#15463)
@justinvp justinvp mentioned this pull request Feb 21, 2024
github-merge-queue bot pushed a commit that referenced this pull request Feb 21, 2024
### Features

- [cli/config] Adds an `--open` flag to `pulumi config` command which
resolves the environment listed in the stack configuration.
  [#15469](#15469)

- [auto/go] Automation API support for `pulumi refresh --preview-only`
  [#15340](#15340)

- [engine] Add support for remote transforms to the engine.
  [#15290](#15290)

- [pkg/testing] Add a InstallDevReleases option to ProgramTest, to
install pulumi dev SDKs
  [#15387](#15387)

- [programgen/{dotnet,nodejs,python}] Emit Output-returning JSON
serialization methods without rewriting applies for top-level function
expression
  [#15371](#15371)

- [sdk/nodejs] Detect npm and yarn workspaces setups when installing
dependencies
  [#15421](#15421)

- [sdk/nodejs] Use pnpm as package manager if we find a pnpm-lock.yaml
file
  [#15456](#15456)


### Bug Fixes

- [docs] Fixes docs generator parent module computation
  [#15035](#15035)

- [engine] Test and fix the engine filling in args dependencies to
provider calls.
  [#15450](#15450)

- [programgen] Fix infinite recursion when binding invoke signature into
promises without accounting for recursive type references
  [#15463](#15463)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants