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

Ensure resource and invoke option "version" is used in package resolution #423

Merged
merged 3 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/stage-lint.yml
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM hello-world

RUN echo "Pulumi 💜 Docker!"
21 changes: 21 additions & 0 deletions examples/docker-image/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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