Skip to content

Commit

Permalink
Automatic Alias Removal on Major Version Bump (#890)
Browse files Browse the repository at this point in the history
  • Loading branch information
aq17 committed Mar 24, 2023
1 parent 4f8616f commit c768911
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 15 deletions.
38 changes: 38 additions & 0 deletions pkg/tfbridge/x/token_test.go
Expand Up @@ -379,6 +379,7 @@ func TestAliasing(t *testing.T) {
}, simple.Resources)

modules := provider()
modules.Version = "1.0.0"

knownModules := TokensKnownModules("pkg_", "",
[]string{"mod1", "mod2"}, MakeStandardToken("pkg"))
Expand Down Expand Up @@ -422,6 +423,7 @@ func TestAliasing(t *testing.T) {
}, modules.Resources)

modules2 := provider()
modules2.Version = "1.0.0"

err = ComputeDefaults(modules2, knownModules)
require.NoError(t, err)
Expand All @@ -432,6 +434,42 @@ func TestAliasing(t *testing.T) {
hist3 := md.Clone(metadata)
assert.Equal(t, hist2, hist3, "No changes should imply no change in history")
assert.Equal(t, modules, modules2)

modules3 := provider()
modules3.Version = "100.0.0"

err = ComputeDefaults(modules3, knownModules)
require.NoError(t, err)

err = AutoAliasing(modules3, metadata)
require.NoError(t, err)

// All hard aliases should be removed on a major version upgrade
assert.Equal(t, map[string]*tfbridge.ResourceInfo{
"pkg_mod1_r1": {
Tok: "pkg:mod1/r1:R1",
Aliases: []tfbridge.AliasInfo{{Type: ref("pkg:index/mod1R1:Mod1R1")}},
},
"pkg_mod1_r2": {
Tok: "pkg:mod1/r2:R2",
Aliases: []tfbridge.AliasInfo{{Type: ref("pkg:index/mod1R2:Mod1R2")}},
},
"pkg_mod2_r1": {
Tok: "pkg:mod2/r1:R1",
Aliases: []tfbridge.AliasInfo{{Type: ref("pkg:index/mod2R1:Mod2R1")}},
},
}, modules3.Resources)

// A provider with no version should assume the most recent major
// version in history – in this case, all aliases should be kept
modules4 := provider()

err = ComputeDefaults(modules4, knownModules)
require.NoError(t, err)

err = AutoAliasing(modules4, metadata)
require.NoError(t, err)
assert.Equal(t, modules.Resources, modules4.Resources)
}

type Provider struct {
Expand Down
69 changes: 54 additions & 15 deletions pkg/tfbridge/x/tokens.go
Expand Up @@ -20,13 +20,14 @@ import (
"strings"
"unicode"

"github.com/Masterminds/semver"

b "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
md "github.com/pulumi/pulumi-terraform-bridge/v3/unstable/metadata"
"github.com/pulumi/pulumi/pkg/v3/codegen/cgstrings"
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"

b "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
md "github.com/pulumi/pulumi-terraform-bridge/v3/unstable/metadata"
)

const (
Expand Down Expand Up @@ -465,8 +466,9 @@ type tokenHistory[T ~string] struct {
}

type alias[T ~string] struct {
Name T `json:"name"` // The previous token.
InCodegen bool `json:"inCodegen"` // If the alias is a fully generated resource, or just a schema alias.
Name T `json:"name"` // The previous token.
InCodegen bool `json:"inCodegen"` // If the alias is a fully generated resource, or just a schema alias.
MajorVersion int `json:"majorVersion"` // The provider's major version when Name was introduced.
}

type aliasHistory struct {
Expand All @@ -482,12 +484,37 @@ func AutoAliasing(providerInfo *b.ProviderInfo, artifact b.ProviderMetadata) err
return err
}

var currentVersion int
// If version is missing, we assume the most recent major version in history
if providerInfo.Version != "" {
v, err := semver.NewVersion(providerInfo.Version)
if err != nil {
return err
}
currentVersion = int(v.Major())
} else {
for _, r := range hist.Resources {
for _, p := range r.Past {
if p.MajorVersion > currentVersion {
currentVersion = p.MajorVersion
}
}
}
for _, d := range hist.DataSources {
for _, p := range d.Past {
if p.MajorVersion > currentVersion {
currentVersion = p.MajorVersion
}
}
}
}

for tfToken, computed := range providerInfo.Resources {
aliasResource(hist.Resources, computed, tfToken, remaps)
aliasResource(hist.Resources, computed, tfToken, remaps, currentVersion)
}

for tfToken, computed := range providerInfo.DataSources {
aliasDataSource(hist.DataSources, computed, tfToken, remaps)
aliasDataSource(hist.DataSources, computed, tfToken, remaps, currentVersion)
}

for _, r := range *remaps {
Expand Down Expand Up @@ -524,6 +551,7 @@ func aliasResource(
computed *b.ResourceInfo,
tfToken string,
remaps *[]func(*b.ProviderInfo),
version int,
) {
prev, hasPrev := hist[tfToken]
if !hasPrev {
Expand All @@ -538,13 +566,16 @@ func aliasResource(
// It's in history, but something has changed. Update the history to reflect
// the new reality, then add aliases.
*remaps = append(*remaps, func(p *b.ProviderInfo) {
aliasOrRenameResource(p, tfToken, prev)
aliasOrRenameResource(p, tfToken, prev, version)
})

}
}

func aliasOrRenameResource(p *b.ProviderInfo, tfToken string, hist *tokenHistory[tokens.Type]) {
func aliasOrRenameResource(
p *b.ProviderInfo, tfToken string, hist *tokenHistory[tokens.Type],
currentVersion int,
) {
// re-fetch the resource, to make sure we have the right pointer.
res, ok := p.Resources[tfToken]
if !ok {
Expand All @@ -563,13 +594,15 @@ func aliasOrRenameResource(p *b.ProviderInfo, tfToken string, hist *tokenHistory
}
if !alreadyPresent {
hist.Past = append(hist.Past, alias[tokens.Type]{
Name: hist.Current,
InCodegen: true,
Name: hist.Current,
InCodegen: true,
MajorVersion: currentVersion,
})
}
for _, a := range hist.Past {
legacy := a.Name
if a.InCodegen {
// Only respect hard aliases introduced in the same major version
if a.InCodegen && a.MajorVersion == currentVersion {
p.RenameResourceWithAlias(tfToken, legacy,
res.Tok, legacy.Module().Name().String(),
res.Tok.Module().Name().String(), res)
Expand All @@ -586,6 +619,7 @@ func aliasDataSource(
computed *b.DataSourceInfo,
tfToken string,
remaps *[]func(*b.ProviderInfo),
version int,
) {
prev, hasPrev := hist[tfToken]
if !hasPrev {
Expand All @@ -595,11 +629,12 @@ func aliasDataSource(
Current: computed.Tok,
}
} else if prev.Current != computed.Tok {
aliasOrRenameDataSource(tfToken, remaps, prev)
aliasOrRenameDataSource(tfToken, remaps, prev, version)
}
}

func aliasOrRenameDataSource(tfToken string, remaps *[]func(*b.ProviderInfo), prev *tokenHistory[tokens.ModuleMember]) {
func aliasOrRenameDataSource(tfToken string, remaps *[]func(*b.ProviderInfo), prev *tokenHistory[tokens.ModuleMember],
currentVersion int) {
// It's in history, but something has changed. Update the history to reflect
// the new reality, then add aliases.
*remaps = append(*remaps, func(p *b.ProviderInfo) {
Expand All @@ -611,10 +646,14 @@ func aliasOrRenameDataSource(tfToken string, remaps *[]func(*b.ProviderInfo), pr
return
}
alias := alias[tokens.ModuleMember]{
Name: prev.Current,
Name: prev.Current,
MajorVersion: currentVersion,
}
prev.Past = append(prev.Past, alias)
for _, a := range prev.Past {
if a.MajorVersion != currentVersion {
continue
}
legacy := a.Name
p.RenameDataSource(tfToken, legacy,
computed.Tok, legacy.Module().Name().String(),
Expand Down

0 comments on commit c768911

Please sign in to comment.