Skip to content

Commit

Permalink
[program-gen] Fix generated utility functions for filebase64, filebas…
Browse files Browse the repository at this point in the history
…e64sha256, sha1 and mimeType (#14857)

# Description

While writing program tests for generated helper utility functions
`filebase64`, `filebase64sha256`, `sha1` and `mimeType` with the idea to
increase code coverage, it turned out that those are completely broken
in all of the languages containing syntax errors, missing imports and
wrong indentation. This PR fixes them and extends the `functions`
program to show how they now look like and to show that they compile.
Also adding example usage of `stack()`, `project()` and `cwd()` in the
test program.

## 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`

<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!--- 
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
  • Loading branch information
Zaid-Ajaj authored Dec 15, 2023
1 parent 6ae7c39 commit 177e2e0
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changes:
- type: fix
scope: programgen/dotnet,go,nodejs,python
description: Fix generated utility functions for filebase64, filebase64sha256, sha1 and mimeType
33 changes: 19 additions & 14 deletions pkg/codegen/dotnet/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,24 +125,29 @@ func makeSafeEnumName(name, typeName string) (string, error) {
func getHelperMethodIfNeeded(functionName string, indent string) (string, bool) {
switch functionName {
case "filebase64":
return `private static string ReadFileBase64(string path) {
return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path)));
}`, true
return fmt.Sprintf(`
%sstring ReadFileBase64(string path)
%s{
%s return Convert.ToBase64String(Encoding.UTF8.GetBytes(File.ReadAllText(path)));
%s}`, indent, indent, indent, indent), true
case "filebase64sha256":
return `private static string ComputeFileBase64Sha256(string path) {
var fileData = System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path));
var hashData = SHA256.Create().ComputeHash(fileData);
return Convert.ToBase64String(hashData);
}`, true
return fmt.Sprintf(`
%sstring ComputeFileBase64Sha256(string path)
%s{
%s var fileData = Encoding.UTF8.GetBytes(File.ReadAllText(path));
%s var hashData = SHA256.Create().ComputeHash(fileData);
%s return Convert.ToBase64String(hashData);
%s}`, indent, indent, indent, indent, indent, indent), true
case "sha1":
return `private static string ComputeSHA1(string input) {
return BitConverter.ToString(
SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(input))
).Replace("-","").ToLowerInvariant());
}`, true
return fmt.Sprintf(`
%sstring ComputeSHA1(string input)
%s{
%s var hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(input));
%s return BitConverter.ToString(hash).Replace("-","").ToLowerInvariant();
%s}`, indent, indent, indent, indent, indent), true
case "notImplemented":
return fmt.Sprintf(`
%sstatic object NotImplemented(string errorMessage)
%sobject NotImplemented(string errorMessage)
%s{
%s throw new System.NotImplementedException(errorMessage);
%s}`, indent, indent, indent, indent), true
Expand Down
4 changes: 2 additions & 2 deletions pkg/codegen/go/gen_program_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1234,8 +1234,8 @@ var functionPackages = map[string][]string{
"toBase64": {"encoding/base64"},
"fromBase64": {"encoding/base64"},
"toJSON": {"encoding/json"},
"sha1": {"fmt", "crypto/sha1"},
"filebase64sha256": {"fmt", "crypto/sha256", "os"},
"sha1": {"crypto/sha1", "encoding/hex"},
"filebase64sha256": {"crypto/sha256", "os"},
"cwd": {"os"},
"singleOrNone": {"fmt"},
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/codegen/go/gen_program_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,18 @@ func getHelperMethodIfNeeded(functionName string, indent string) (string, bool)
return pulumi.String(string(data))
}`, true
case "filebase64":
return `func filebase64OrPanic(path string) pulumi.StringPtrInput {
return `func filebase64OrPanic(path string) string {
if fileData, err := os.ReadFile(path); err == nil {
return pulumi.String(base64.StdEncoding.EncodeToString(fileData[:]))
return base64.StdEncoding.EncodeToString(fileData[:])
} else {
panic(err.Error())
}
}`, true
case "filebase64sha256":
return `func filebase64sha256OrPanic(path string) pulumi.StringPtrInput {
return `func filebase64sha256OrPanic(path string) string {
if fileData, err := os.ReadFile(path); err == nil {
hashedData := sha256.Sum256([]byte(fileData))
return pulumi.String(base64.StdEncoding.EncodeToString(hashedData[:]))
return base64.StdEncoding.EncodeToString(hashedData[:])
} else {
panic(err.Error())
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/codegen/nodejs/gen_program_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,14 +408,16 @@ func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionC
case "remoteAsset":
g.Fgenf(w, "new pulumi.asset.RemoteAsset(%.v)", expr.Args[0])
case "filebase64":
g.Fgenf(w, "Buffer.from(fs.readFileSync(%v), 'binary').toString('base64')", expr.Args[0])
g.Fgenf(w, "Buffer.from(fs.readFileSync(%v, 'binary')).toString('base64')", expr.Args[0])
case "filebase64sha256":
// Assuming the existence of the following helper method
g.Fgenf(w, "computeFilebase64sha256(%v)", expr.Args[0])
case "notImplemented":
g.Fgenf(w, "notImplemented(%v)", expr.Args[0])
case "singleOrNone":
g.Fgenf(w, "singleOrNone(%v)", expr.Args[0])
case "mimeType":
g.Fgenf(w, "mimeType(%v)", expr.Args[0])
case pcl.Invoke:
pkg, module, fn, diags := functionName(expr.Args[0])
contract.Assertf(len(diags) == 0, "unexpected diagnostics: %v", diags)
Expand Down
8 changes: 6 additions & 2 deletions pkg/codegen/nodejs/gen_program_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import "fmt"
func getHelperMethodIfNeeded(functionName string, indent string) (string, bool) {
switch functionName {
case "filebase64sha256":
return `function computeFilebase64sha256(path string) string {
const fileData = Buffer.from(fs.readFileSync(path), 'binary')
return `function computeFilebase64sha256(path: string): string {
const fileData = Buffer.from(fs.readFileSync(path, 'binary'))
return crypto.createHash('sha256').update(fileData).digest('hex')
}`, true
case "notImplemented":
Expand All @@ -26,6 +26,10 @@ func getHelperMethodIfNeeded(functionName string, indent string) (string, bool)
%s }
%s return elements[0];
%s}`, indent, indent, indent, indent, indent, indent), true
case "mimeType":
return fmt.Sprintf(`%sfunction mimeType(path: string): string {
%s throw new Error("mimeType not implemented, use the mime or mime-types package instead");
%s}`, indent, indent, indent), true
default:
return "", false
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/codegen/python/gen_program_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ var functionImports = map[string][]string{
"stack": {"pulumi"},
"project": {"pulumi"},
"cwd": {"os"},
"mimeType": {"mimetypes"},
}

func (g *generator) getFunctionImports(x *model.FunctionCallExpression) []string {
Expand Down Expand Up @@ -300,6 +301,8 @@ func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionC
g.Fgenf(w, "not_implemented(%v)", expr.Args[0])
case "singleOrNone":
g.Fgenf(w, "single_or_none(%v)", expr.Args[0])
case "mimeType":
g.Fgenf(w, "mimetypes.guess_type(%v)[0]", expr.Args[0])
case pcl.Invoke:
if expr.Signature.MultiArgumentInputs {
err := fmt.Errorf("python program-gen does not implement MultiArgumentInputs for function '%v'",
Expand Down
60 changes: 60 additions & 0 deletions pkg/codegen/testing/test/testdata/functions-pp/dotnet/functions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Pulumi;
using Aws = Pulumi.Aws;


string ComputeFileBase64Sha256(string path)
{
var fileData = Encoding.UTF8.GetBytes(File.ReadAllText(path));
var hashData = SHA256.Create().ComputeHash(fileData);
return Convert.ToBase64String(hashData);
}


string ComputeSHA1(string input)
{
var hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(input));
return BitConverter.ToString(hash).Replace("-","").ToLowerInvariant();
}


string ReadFileBase64(string path)
{
return Convert.ToBase64String(Encoding.UTF8.GetBytes(File.ReadAllText(path)));
}

return await Deployment.RunAsync(() =>
{
var encoded = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("haha business"));
Expand Down Expand Up @@ -31,5 +55,41 @@

var plainValue = Output.Unsecret(secretValue);

var currentStack = Deployment.Instance.StackName;

var currentProject = Deployment.Instance.ProjectName;

var workingDirectory = Directory.GetCurrentDirectory();

var fileMimeType = "TODO: call mimeType";

// using the filebase64 function
var first = new Aws.S3.BucketObject("first", new()
{
Bucket = bucket.Id,
Source = new StringAsset(ReadFileBase64("./base64.txt")),
ContentType = fileMimeType,
Tags =
{
{ "stack", currentStack },
{ "project", currentProject },
{ "cwd", workingDirectory },
},
});

// using the filebase64sha256 function
var second = new Aws.S3.BucketObject("second", new()
{
Bucket = bucket.Id,
Source = new StringAsset(ComputeFileBase64Sha256("./base64.txt")),
});

// using the sha1 function
var third = new Aws.S3.BucketObject("third", new()
{
Bucket = bucket.Id,
Source = new StringAsset(ComputeSHA1("content")),
});

});

33 changes: 30 additions & 3 deletions pkg/codegen/testing/test/testdata/functions-pp/functions.pp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,40 @@
zone = invoke("aws:index:getAvailabilityZones", {})
zone2 = invoke("aws:index:getAvailabilityZones", {})

resource bucket "aws:s3:Bucket" {

}
resource bucket "aws:s3:Bucket" { }

encoded2 = toBase64(bucket.id)

decoded2 = fromBase64(bucket.id)

secretValue = secret("hello")
plainValue = unsecret(secretValue)

currentStack = stack()
currentProject = project()
workingDirectory = cwd()
fileMimeType = mimeType("./base64.txt")

# using the filebase64 function
resource first "aws:s3:BucketObject" {
bucket = bucket.id
source = stringAsset(filebase64("./base64.txt"))
contentType = fileMimeType
tags = {
"stack" = currentStack
"project" = currentProject
"cwd" = workingDirectory
}
}

# using the filebase64sha256 function
resource second "aws:s3:BucketObject" {
bucket = bucket.id
source = stringAsset(filebase64sha256("./base64.txt"))
}

# using the sha1 function
resource third "aws:s3:BucketObject" {
bucket = bucket.id
source = stringAsset(sha1("content"))
}
64 changes: 64 additions & 0 deletions pkg/codegen/testing/test/testdata/functions-pp/go/functions.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
package main

import (
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"mime"
"os"
"path"
"strings"

"github.com/pulumi/pulumi-aws/sdk/v5/go/aws"
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/s3"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func filebase64OrPanic(path string) string {
if fileData, err := os.ReadFile(path); err == nil {
return base64.StdEncoding.EncodeToString(fileData[:])
} else {
panic(err.Error())
}
}

func filebase64sha256OrPanic(path string) string {
if fileData, err := os.ReadFile(path); err == nil {
hashedData := sha256.Sum256([]byte(fileData))
return base64.StdEncoding.EncodeToString(hashedData[:])
} else {
panic(err.Error())
}
}

func sha1Hash(input string) string {
hash := sha1.Sum([]byte(input))
return hex.EncodeToString(hash[:])
}

func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
encoded := base64.StdEncoding.EncodeToString([]byte("haha business"))
Expand Down Expand Up @@ -40,6 +68,42 @@ func main() {
}).(pulumi.StringOutput)
secretValue := pulumi.ToSecret("hello").(pulumi.StringOutput)
_ = pulumi.Unsecret(secretValue).(pulumi.StringOutput)
currentStack := ctx.Stack()
currentProject := ctx.Project()
workingDirectory := func(cwd string, err error) string {
if err != nil {
panic(err)
}
return cwd
}(os.Getwd())
fileMimeType := mime.TypeByExtension(path.Ext("./base64.txt"))
_, err = s3.NewBucketObject(ctx, "first", &s3.BucketObjectArgs{
Bucket: bucket.ID(),
Source: pulumi.NewStringAsset(filebase64OrPanic("./base64.txt")),
ContentType: pulumi.String(fileMimeType),
Tags: pulumi.StringMap{
"stack": pulumi.String(currentStack),
"project": pulumi.String(currentProject),
"cwd": pulumi.String(workingDirectory),
},
})
if err != nil {
return err
}
_, err = s3.NewBucketObject(ctx, "second", &s3.BucketObjectArgs{
Bucket: bucket.ID(),
Source: pulumi.NewStringAsset(filebase64sha256OrPanic("./base64.txt")),
})
if err != nil {
return err
}
_, err = s3.NewBucketObject(ctx, "third", &s3.BucketObjectArgs{
Bucket: bucket.ID(),
Source: pulumi.NewStringAsset(sha1Hash("content")),
})
if err != nil {
return err
}
return nil
})
}
36 changes: 36 additions & 0 deletions pkg/codegen/testing/test/testdata/functions-pp/nodejs/functions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as crypto from "crypto";
import * as fs from "fs";

function computeFilebase64sha256(path: string): string {
const fileData = Buffer.from(fs.readFileSync(path, 'binary'))
return crypto.createHash('sha256').update(fileData).digest('hex')
}

function mimeType(path: string): string {
throw new Error("mimeType not implemented, use the mime or mime-types package instead");
}

const encoded = Buffer.from("haha business").toString("base64");
const decoded = Buffer.from(encoded, "base64").toString("utf8");
Expand All @@ -15,3 +26,28 @@ const encoded2 = bucket.id.apply(id => Buffer.from(id).toString("base64"));
const decoded2 = bucket.id.apply(id => Buffer.from(id, "base64").toString("utf8"));
const secretValue = pulumi.secret("hello");
const plainValue = pulumi.unsecret(secretValue);
const currentStack = pulumi.getStack();
const currentProject = pulumi.getProject();
const workingDirectory = process.cwd();
const fileMimeType = mimeType("./base64.txt");
// using the filebase64 function
const first = new aws.s3.BucketObject("first", {
bucket: bucket.id,
source: new pulumi.asset.StringAsset(Buffer.from(fs.readFileSync("./base64.txt", 'binary')).toString('base64')),
contentType: fileMimeType,
tags: {
stack: currentStack,
project: currentProject,
cwd: workingDirectory,
},
});
// using the filebase64sha256 function
const second = new aws.s3.BucketObject("second", {
bucket: bucket.id,
source: new pulumi.asset.StringAsset(computeFilebase64sha256("./base64.txt")),
});
// using the sha1 function
const third = new aws.s3.BucketObject("third", {
bucket: bucket.id,
source: new pulumi.asset.StringAsset(crypto.createHash('sha1').update("content").digest('hex')),
});
Loading

0 comments on commit 177e2e0

Please sign in to comment.