From 7eef4a370b23970f023940164baac36b7c763365 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Fri, 9 Feb 2024 18:04:18 -0500 Subject: [PATCH] Fix azure loader issues (#1682) Fixes #1681 For providers such as Azure Classic that have a discrepancy between "azure" and "azurerm", PULUMI_CONVERT=1 conversion no longer attempts to resolve and download the "azurerm" plugin from GitHub. --- pkg/tfgen/convert_cli.go | 14 +++- pkg/tfgen/convert_cli_test.go | 131 ++++++++++++++++++++++++++++++++-- pkg/tfgen/generate.go | 1 - pkg/tfgen/loaders.go | 17 ++++- 4 files changed, 151 insertions(+), 12 deletions(-) diff --git a/pkg/tfgen/convert_cli.go b/pkg/tfgen/convert_cli.go index 3b279096a..46b7d5db7 100644 --- a/pkg/tfgen/convert_cli.go +++ b/pkg/tfgen/convert_cli.go @@ -35,6 +35,7 @@ import ( hcl2nodejs "github.com/pulumi/pulumi/pkg/v3/codegen/nodejs" "github.com/pulumi/pulumi/pkg/v3/codegen/pcl" hcl2python "github.com/pulumi/pulumi/pkg/v3/codegen/python" + "github.com/pulumi/pulumi/pkg/v3/codegen/schema" pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" "github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil" @@ -76,6 +77,8 @@ type cliConverter struct { ) string } + loader schema.Loader + convertExamplesList []struct { docs string path examplePath @@ -107,6 +110,12 @@ func (g *Generator) cliConverter() *cliConverter { pluginHost: g.pluginHost, pcls: map[string]translatedExample{}, } + if g.pluginHost != nil { + l := newLoader(g.pluginHost) + // Ensure azurerm resolves to azure for example: + l.aliasPackage(g.info.Name, string(g.pkg)) + g.cliConverterState.loader = l + } return g.cliConverterState } @@ -389,8 +398,9 @@ func (cc *cliConverter) convertPCL( opts = append(opts, pcl.SkipResourceTypechecking) if cc.pluginHost != nil { opts = append(opts, pcl.PluginHost(cc.pluginHost)) - loader := newLoader(cc.pluginHost) - opts = append(opts, pcl.Loader(loader)) + } + if cc.loader != nil { + opts = append(opts, pcl.Loader(cc.loader)) } if cc.packageCache != nil { opts = append(opts, pcl.Cache(cc.packageCache)) diff --git a/pkg/tfgen/convert_cli_test.go b/pkg/tfgen/convert_cli_test.go index 0900f3246..c51bdf97f 100644 --- a/pkg/tfgen/convert_cli_test.go +++ b/pkg/tfgen/convert_cli_test.go @@ -15,27 +15,34 @@ package tfgen import ( + "encoding/json" "fmt" "io" "os" "path/filepath" "runtime" + "strings" "testing" - "encoding/json" - + "github.com/blang/semver" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hexops/autogold/v2" "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - bridgetesting "github.com/pulumi/pulumi-terraform-bridge/v3/internal/testing" - "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" - sdkv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2" pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" "github.com/pulumi/pulumi/sdk/v3/go/common/diag" "github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors" + "github.com/pulumi/pulumi/sdk/v3/go/common/resource" + "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" + "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" + + bridgetesting "github.com/pulumi/pulumi-terraform-bridge/v3/internal/testing" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" + sdkv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2" + shimv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2" ) func TestConvertViaPulumiCLI(t *testing.T) { @@ -164,6 +171,7 @@ output "someOutput" { Package: info.Name, Version: info.Version, Language: Schema, + PluginHost: &testPluginHost{}, ProviderInfo: info, Root: fs, Sink: diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{ @@ -213,4 +221,115 @@ Converted 100.00% of yaml examples (1/1) withPrefix := tfbridge.ProviderInfo{Name: "p", ResourcePrefix: "prov"} assert.Equal(t, filepath.Join(".", "prov.json"), c.mappingsFile(".", withPrefix)) }) + + // Taken from https://github.com/pulumi/pulumi-azure/issues/1698 + // + // Emulate a case where pulumi name does not match the TF provider prefix, and one of the + // examples is referencing an unknown resource. + // + // Before the fix this would panic reaching out to PluginHost.ResolvePlugin. + t.Run("unknownResource", func(t *testing.T) { + md := []byte(strings.ReplaceAll(` +# azurerm_web_pubsub_custom_certificate + +Manages an Azure Web PubSub Custom Certificate. + +## Example Usage + +%%%hcl + +resource "azurerm_web_pubsub_service" "example" { + name = "example-webpubsub" +} + +resource "azurerm_web_pubsub_custom_certificate" "test" { + name = "example-cert" +} + +%%%`, "%%%", "```")) + p := &schema.Provider{ + ResourcesMap: map[string]*schema.Resource{ + "azurerm_web_pubsub_custom_certificate": { + Schema: map[string]*schema.Schema{"name": { + Type: schema.TypeString, + Optional: true, + }}, + }, + }, + } + pi := tfbridge.ProviderInfo{ + P: shimv2.NewProvider(p), + Name: "azurerm", + Version: "0.0.1", + Resources: map[string]*tfbridge.ResourceInfo{ + "azurerm_web_pubsub_custom_certificate": { + Tok: "azure:webpubsub/customCertificate:CustomCertificate", + Docs: &tfbridge.DocInfo{Markdown: md}, + }, + }, + } + g, err := NewGenerator(GeneratorOptions{ + Package: "azure", + Version: "0.0.1", + PluginHost: &testPluginHost{}, + Language: Schema, + ProviderInfo: pi, + Root: afero.NewBasePathFs(afero.NewOsFs(), t.TempDir()), + Sink: diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{ + Color: colors.Never, + }), + }) + require.NoError(t, err) + + err = g.Generate() + require.NoError(t, err) + }) +} + +type testPluginHost struct{} + +func (*testPluginHost) ServerAddr() string { panic("Unexpected call") } + +func (*testPluginHost) Log(diag.Severity, resource.URN, string, int32) { + panic("Unexpected call") +} + +func (*testPluginHost) LogStatus(diag.Severity, resource.URN, string, int32) { + panic("Unexpected call") } + +func (*testPluginHost) Analyzer(tokens.QName) (plugin.Analyzer, error) { panic("Unexpected call") } + +func (*testPluginHost) PolicyAnalyzer( + tokens.QName, string, *plugin.PolicyAnalyzerOptions, +) (plugin.Analyzer, error) { + panic("Unexpected call") +} + +func (*testPluginHost) ListAnalyzers() []plugin.Analyzer { panic("Unexpected call") } + +func (*testPluginHost) Provider(tokens.Package, *semver.Version) (plugin.Provider, error) { + panic("Unexpected call") +} + +func (*testPluginHost) CloseProvider(plugin.Provider) error { panic("Unexpected call") } + +func (*testPluginHost) LanguageRuntime(string, plugin.ProgramInfo) (plugin.LanguageRuntime, error) { + panic("Unexpected call") +} + +func (*testPluginHost) EnsurePlugins([]workspace.PluginSpec, plugin.Flags) error { + panic("Unexpected call") +} + +func (*testPluginHost) ResolvePlugin( + workspace.PluginKind, string, *semver.Version, +) (*workspace.PluginInfo, error) { + panic("Unexpected call") +} + +func (*testPluginHost) GetProjectPlugins() []workspace.ProjectPlugin { panic("Unexpected call") } +func (*testPluginHost) SignalCancellation() error { panic("Unexpected call") } +func (*testPluginHost) Close() error { return nil } + +var _ plugin.Host = (*testPluginHost)(nil) diff --git a/pkg/tfgen/generate.go b/pkg/tfgen/generate.go index d8535c54e..ae5f99fe8 100644 --- a/pkg/tfgen/generate.go +++ b/pkg/tfgen/generate.go @@ -871,7 +871,6 @@ type GenerateOptions struct { // Generate creates Pulumi packages from the information it was initialized with. func (g *Generator) Generate() error { - // First gather up the entire package contents. This structure is complete and sufficient to hand off to the // language-specific generators to create the full output. pack, err := g.gatherPackage() diff --git a/pkg/tfgen/loaders.go b/pkg/tfgen/loaders.go index 08c609bc9..de37b09d3 100644 --- a/pkg/tfgen/loaders.go +++ b/pkg/tfgen/loaders.go @@ -16,19 +16,30 @@ package tfgen import ( "github.com/blang/semver" - "github.com/pulumi/pulumi/pkg/v3/codegen/schema" "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" ) type loader struct { - innerLoader schema.Loader - emptyPackages map[string]bool + innerLoader schema.Loader + emptyPackages map[string]bool + aliasedPackages map[string]string } var _ schema.Loader = &loader{} +func (l *loader) aliasPackage(alias string, canonicalName string) { + if l.aliasedPackages == nil { + l.aliasedPackages = map[string]string{} + } + l.aliasedPackages[alias] = canonicalName +} + func (l *loader) LoadPackage(name string, ver *semver.Version) (*schema.Package, error) { + if renamed, ok := l.aliasedPackages[name]; ok { + name = renamed + } + if l.emptyPackages[name] { return &schema.Package{ Name: name,