Skip to content

Commit

Permalink
Merge pull request #423 from pulumi/friel/optimistic-resolution
Browse files Browse the repository at this point in the history
Ensure resource and invoke option "version" is used in package resolution
  • Loading branch information
AaronFriel committed Dec 7, 2022
2 parents 0af2103 + 4055023 commit 3abcc76
Show file tree
Hide file tree
Showing 18 changed files with 188 additions and 366 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/stage-lint.yml
Expand Up @@ -19,7 +19,7 @@ jobs:
uses: actions/setup-go@v3
with:
go-version: 1.19.x
- run: go mod tidy
- run: go mod tidy -compat=1.17
- name: Fail if god mod not tidy
run: |
if [ -n "$(git status --porcelain)" ]; then
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/stage-test.yml
Expand Up @@ -107,7 +107,7 @@ jobs:
- name: Fixup go.mod if we're not targeting 1.17x
run: |
if [ "${{ matrix.go-version }}" != "1.17.x" ]; then
go mod tidy
go mod tidy -compat=1.17
fi
- name: Test
run: make test
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG_PENDING.md
Expand Up @@ -3,4 +3,8 @@
- Deprecate `fn::stackReference`.
[#420](https://github.com/pulumi/pulumi-yaml/pull/420)

- Ensure resource and invoke option "version" is used in package resolution, enabling Docker v4
provider `docker:Image` resource.
[#423](https://github.com/pulumi/pulumi-yaml/pull/423)

### Bug Fixes
3 changes: 3 additions & 0 deletions examples/docker-image/Dockerfile
@@ -0,0 +1,3 @@
FROM hello-world

RUN echo "Pulumi 💜 Docker!"
21 changes: 21 additions & 0 deletions examples/docker-image/Pulumi.yaml
@@ -0,0 +1,21 @@
name: image-yaml
description: A minimal Pulumi YAML program
runtime: yaml
config: {}
variables: {}
resources:
docker-provider:
type: pulumi:providers:docker
defaultProvider: true
options:
version: 4.0.0-alpha.0
image:
type: docker:Image
properties:
imageName: pulumi.example.com/test-yaml:tag1
skipPush: true
build:
dockerfile: Dockerfile
context: .
outputs:
imageName: ${image.imageName}
12 changes: 6 additions & 6 deletions go.mod
Expand Up @@ -12,8 +12,8 @@ require (
github.com/hexops/autogold v1.3.0
github.com/iancoleman/strcase v0.2.0
github.com/pkg/errors v0.9.1
github.com/pulumi/pulumi/pkg/v3 v3.48.0
github.com/pulumi/pulumi/sdk/v3 v3.48.0
github.com/pulumi/pulumi/pkg/v3 v3.48.1-0.20221207010559-e812f69ba562
github.com/pulumi/pulumi/sdk/v3 v3.48.1-0.20221207010559-e812f69ba562
github.com/stretchr/testify v1.8.0
github.com/zclconf/go-cty v1.10.0
google.golang.org/grpc v1.49.0
Expand Down Expand Up @@ -53,14 +53,14 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/aws/aws-sdk-go v1.44.68 // indirect
github.com/aws/aws-sdk-go-v2 v1.16.8 // indirect
github.com/aws/aws-sdk-go-v2 v1.17.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.15.15 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.12.10 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.9 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.3 // indirect
Expand All @@ -71,7 +71,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.13 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.16.10 // indirect
github.com/aws/smithy-go v1.12.0 // indirect
github.com/aws/smithy-go v1.13.4 // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/cheggaaa/pb v1.0.29 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down
304 changes: 12 additions & 292 deletions go.sum

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions pkg/pulumiyaml/analyser.go
Expand Up @@ -563,9 +563,13 @@ func (tc *typeCache) assertTypeAssignable(ctx *evalContext, from ast.Expr, to sc
func (tc *typeCache) typeResource(r *Runner, node resourceNode) bool {
k, v := node.Key.Value, node.Value
ctx := r.newContext(node)
pkg, typ, err := ResolveResource(ctx.pkgLoader, v.Type.Value)
version, err := ParseVersion(v.Options.Version)
if err != nil {
ctx.error(v.Type, fmt.Sprintf("unable to parse resource %v provider version: %v", k, err))
return true
}
pkg, typ, err := ResolveResource(ctx.pkgLoader, v.Type.Value, version)
if err != nil {
ctx.sdiags.diags.Extend(syntax.NodeError(v.Syntax(), fmt.Sprintf("error resolving type of resource %v: %v", k, err), ""))
ctx.error(v.Type, fmt.Sprintf("error resolving type of resource %v: %v", k, err))
return true
}
Expand Down Expand Up @@ -736,7 +740,12 @@ func (tc *typeCache) typePropertyEntries(ctx *evalContext, resourceName, resourc
}

func (tc *typeCache) typeInvoke(ctx *evalContext, t *ast.InvokeExpr) bool {
pkg, functionName, err := ResolveFunction(ctx.pkgLoader, t.Token.Value)
version, err := ParseVersion(t.CallOpts.Version)
if err != nil {
ctx.error(t.CallOpts.Version, fmt.Sprintf("unable to parse function provider version: %v", err))
return true
}
pkg, functionName, err := ResolveFunction(ctx.pkgLoader, t.Token.Value, version)
if err != nil {
_, b := ctx.error(t, err.Error())
return b
Expand Down
26 changes: 3 additions & 23 deletions pkg/pulumiyaml/codegen/gen_program_test.go
Expand Up @@ -9,6 +9,7 @@ import (
"path/filepath"
"testing"

"github.com/blang/semver"
"github.com/stretchr/testify/assert"

"github.com/pulumi/pulumi-yaml/pkg/pulumiyaml"
Expand All @@ -20,35 +21,14 @@ import (
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

var defaultPlugins = []pulumiyaml.Plugin{
{Package: "aws", Version: "5.4.0"},
{Package: "azure-native", Version: "1.56.0"},
{Package: "azure", Version: "4.18.0"},
{Package: "kubernetes", Version: "3.7.2"},
{Package: "random", Version: "4.2.0"},
{Package: "eks", Version: "0.40.0"},
{Package: "aws-native", Version: "0.13.0"},
{Package: "docker", Version: "3.1.0"},
{Package: "awsx", Version: "1.0.0-beta.5"},

// Extra packages are to satisfy the versioning requirement of aws-eks.
// While the schemas are not the correct version, we rely on not
// depending on the difference between them.
{Package: "kubernetes", Version: "3.0.0"},

// not a real plugin, but a real schema
{Package: "synthetic", Version: "0.1.0"},
{Package: "other", Version: "0.1.0"},
}

type testPackageLoader struct{ *testing.T }

func (l testPackageLoader) LoadPackage(name string) (pulumiyaml.Package, error) {
func (l testPackageLoader) LoadPackage(name string, version *semver.Version) (pulumiyaml.Package, error) {
if name == "test" {
return FakePackage{l.T}, nil
}

pkg, err := schema.LoadPackageReference(rootPluginLoader, name, nil)
pkg, err := schema.LoadPackageReference(rootPluginLoader, name, version)
if err != nil {
return nil, err
}
Expand Down
26 changes: 16 additions & 10 deletions pkg/pulumiyaml/codegen/load.go
Expand Up @@ -293,7 +293,11 @@ func (imp *importer) importBuiltin(node ast.BuiltinExpr) (model.Expression, synt
case *ast.InvokeExpr:
var diags syntax.Diagnostics

pkg, functionName, err := pulumiyaml.ResolveFunction(imp.loader, node.Token.Value)
version, err := pulumiyaml.ParseVersion(node.CallOpts.Version)
if err != nil {
return nil, syntax.Diagnostics{ast.ExprError(node.CallOpts.Version, fmt.Sprintf("unable to parse function provider version: %v", err), "")}
}
pkg, functionName, err := pulumiyaml.ResolveFunction(imp.loader, node.Token.Value, version)
if err != nil {
return nil, syntax.Diagnostics{ast.ExprError(node.Token, fmt.Sprintf("unable to resolve function name: %v", err), "")}
}
Expand Down Expand Up @@ -652,11 +656,7 @@ func (imp *importer) importVariable(kvp ast.VariablesMapEntry, latestPkgInfo map
// gets the latest package version specified on a resource
func (imp *importer) getLatestPkgInfoResource(kvp ast.ResourcesMapEntry, pkgInfo map[string]*packageInfo) syntax.Diagnostics {
resource := kvp.Value

pkg, _, err := pulumiyaml.ResolveResource(imp.loader, resource.Type.Value)
if err != nil {
return syntax.Diagnostics{ast.ExprError(resource.Type, fmt.Sprintf("unable to resolve resource type: %v", err), "")}
}
pkg := pulumiyaml.ResolvePkgName(resource.Type.Value)

if resource.Options.Version != nil {
url := ""
Expand All @@ -665,14 +665,14 @@ func (imp *importer) getLatestPkgInfoResource(kvp ast.ResourcesMapEntry, pkgInfo
}
v1, err := semver.Make(resource.Options.Version.Value)
if err == nil {
if p, ok := pkgInfo[pkg.Name()]; ok {
if p, ok := pkgInfo[pkg]; ok {
v2, _ := semver.Make(p.version)
if v1.Compare(v2) == 1 {
p.version = resource.Options.Version.Value
p.pluginDownloadURL = url
}
} else {
pkgInfo[pkg.Name()] = &packageInfo{
pkgInfo[pkg] = &packageInfo{
version: resource.Options.Version.Value,
pluginDownloadURL: url,
}
Expand Down Expand Up @@ -722,15 +722,21 @@ func (imp *importer) importResource(kvp ast.ResourcesMapEntry, latestPkgInfo map
resourceVar, ok := imp.resources[name]
contract.Assert(ok)

pkg, token, err := pulumiyaml.ResolveResource(imp.loader, resource.Type.Value)
var diags syntax.Diagnostics

version, err := pulumiyaml.ParseVersion(resource.Options.Version)
if err != nil {
diags.Extend(ast.ExprError(resource.Options.Version, fmt.Sprintf("unable to parse resource %v provider version: %v", name, err), ""))
return nil, diags
}
pkg, token, err := pulumiyaml.ResolveResource(imp.loader, resource.Type.Value, version)
if err != nil {
return nil, syntax.Diagnostics{ast.ExprError(resource.Type, fmt.Sprintf("unable to resolve resource type: %v", err), "")}
}
props := pkg.ResourceTypeHint(token)
contract.Assertf(props != nil,
"token(%s) was obtained by the same ResolveResource call as pkg(%s),"+
" so must produce a non nil value", token.String(), pkg.Name())
var diags syntax.Diagnostics
items := []model.BodyItem{
&model.Attribute{
Name: pcl.LogicalNamePropertyKey,
Expand Down
43 changes: 27 additions & 16 deletions pkg/pulumiyaml/packages.go
Expand Up @@ -9,6 +9,7 @@ import (
"sort"
"strings"

"github.com/blang/semver"
"github.com/iancoleman/strcase"
"github.com/pulumi/pulumi-yaml/pkg/pulumiyaml/ast"
"github.com/pulumi/pulumi-yaml/pkg/pulumiyaml/syntax"
Expand Down Expand Up @@ -58,7 +59,7 @@ type Package interface {
}

type PackageLoader interface {
LoadPackage(name string) (Package, error)
LoadPackage(name string, version *semver.Version) (Package, error)
Close()
}

Expand All @@ -68,8 +69,8 @@ type packageLoader struct {
host plugin.Host
}

func (l packageLoader) LoadPackage(name string) (Package, error) {
pkg, err := l.ReferenceLoader.LoadPackageReference(name, nil)
func (l packageLoader) LoadPackage(name string, version *semver.Version) (Package, error) {
pkg, err := l.ReferenceLoader.LoadPackageReference(name, version)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -115,7 +116,7 @@ func GetReferencedPlugins(tmpl *ast.TemplateDecl) ([]Plugin, syntax.Diagnostics)
pluginMap := map[string]*pluginEntry{}

acceptType := func(r *Runner, typeName string, version, pluginDownloadURL *ast.StringExpr) {
pkg := resolvePkgName(typeName)
pkg := ResolvePkgName(typeName)
if entry, found := pluginMap[pkg]; found {
if v := version.GetValue(); v != "" && entry.version != v {
if entry.version == "" {
Expand Down Expand Up @@ -190,7 +191,7 @@ func GetReferencedPlugins(tmpl *ast.TemplateDecl) ([]Plugin, syntax.Diagnostics)
return plugins, nil
}

func resolvePkgName(typeString string) string {
func ResolvePkgName(typeString string) string {
typeParts := strings.Split(typeString, ":")

// If it's pulumi:providers:aws, the package name is the last label:
Expand All @@ -201,14 +202,14 @@ func resolvePkgName(typeString string) string {
return typeParts[0]
}

func loadPackage(loader PackageLoader, typeString string) (Package, error) {
func loadPackage(loader PackageLoader, typeString string, version *semver.Version) (Package, error) {
typeParts := strings.Split(typeString, ":")
if len(typeParts) < 2 || len(typeParts) > 3 {
return nil, fmt.Errorf("invalid type token %q", typeString)
}

packageName := resolvePkgName(typeString)
pkg, err := loader.LoadPackage(packageName)
packageName := ResolvePkgName(typeString)
pkg, err := loader.LoadPackage(packageName, version)
if errors.Is(err, schema.ErrGetSchemaNotImplemented) {
return nil, fmt.Errorf("error loading schema for %q: %w", packageName, err)
} else if err != nil {
Expand All @@ -218,9 +219,13 @@ func loadPackage(loader PackageLoader, typeString string) (Package, error) {
return pkg, nil
}

var disallowedResourceNames = map[string]string{
"docker:image:Image": "https://github.com/pulumi/pulumi-docker/issues/132",
"docker:Image": "https://github.com/pulumi/pulumi-docker/issues/132",
// Unavailable in Docker versions <4.
var docker3ResourceNames = map[string]struct{}{
"docker:image:Image": {},
"docker:Image": {},
}

var kubernetesResourceNames = map[string]string{
"kubernetes:apiextensions.k8s.io:CustomResource": "https://github.com/pulumi/pulumi-kubernetes/issues/1971",
"kubernetes:kustomize:Directory": "https://github.com/pulumi/pulumi-kubernetes/issues/1971",
"kubernetes:yaml:ConfigFile": "https://github.com/pulumi/pulumi-kubernetes/issues/1971",
Expand All @@ -235,16 +240,22 @@ var helmResourceNames = map[string]struct{}{
// ResolveResource determines the appropriate package for a resource, loads that package, then calls
// the package's ResolveResource method to determine the canonical name of the resource, returning
// both the package and the canonical name.
func ResolveResource(loader PackageLoader, typeString string) (Package, ResourceTypeToken, error) {
if issue, found := disallowedResourceNames[typeString]; found {
func ResolveResource(loader PackageLoader, typeString string, version *semver.Version) (Package, ResourceTypeToken, error) {
if _, found := docker3ResourceNames[typeString]; found {
if version == nil || version.Major <= 3 {
return nil, "", fmt.Errorf("Docker Image resources are not supported in YAML without an explicit version, see: https://github.com/pulumi/pulumi-yaml/issues/421")
}
}

if issue, found := kubernetesResourceNames[typeString]; found {
return nil, "", fmt.Errorf("The resource type [%v] is not supported in YAML at this time, see: %v", typeString, issue)
}

if _, found := helmResourceNames[typeString]; found {
return nil, "", fmt.Errorf("Helm Chart resources are not supported in YAML, consider using the Helm Release resource instead: https://www.pulumi.com/registry/packages/kubernetes/api-docs/helm/v3/release/")
}

pkg, err := loadPackage(loader, typeString)
pkg, err := loadPackage(loader, typeString, version)
if err != nil {
return nil, "", err
}
Expand All @@ -260,8 +271,8 @@ func ResolveResource(loader PackageLoader, typeString string) (Package, Resource
// ResolveFunction determines the appropriate package for a function, loads that package, then calls
// the package's ResolveFunction method to determine the canonical name of the function, returning
// both the package and the canonical name.
func ResolveFunction(loader PackageLoader, typeString string) (Package, FunctionTypeToken, error) {
pkg, err := loadPackage(loader, typeString)
func ResolveFunction(loader PackageLoader, typeString string, version *semver.Version) (Package, FunctionTypeToken, error) {
pkg, err := loadPackage(loader, typeString, version)
if err != nil {
return nil, "", err
}
Expand Down

0 comments on commit 3abcc76

Please sign in to comment.