[sdk/go] Respect default parent in go aliases. (#8288)
* Respect default parent in go aliases. * Update changelog * Handle empty parrents correctly * Allow specifying no parent * clarify variable name * Improve `Unparent` ergonomics * Take t0yv0's suggestion * Adopt @t0yv0's tests and doc comments. * Make NoParent,Parent,ParentURN mutually exclusive
This commit is contained in:
parent
470893a980
commit
e38876f7af
|
@ -1,5 +1,15 @@
|
|||
### Improvements
|
||||
|
||||
- [cli] Reformat error message string in `sdk/go/common/diag/errors.go`
|
||||
[#8284](https://github.com/pulumi/pulumi/pull/8284)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [sdk/go] - Respect implicit parents in alias resolution
|
||||
[#8288](https://github.com/pulumi/pulumi/pull/8288)
|
||||
|
||||
- [sdk/dotnet] - Fix a race condition when detecting exceptions in stack creation
|
||||
[#8294](https://github.com/pulumi/pulumi/pull/8294)
|
||||
- Clarify error message string in `sdk/go/common/diag/errors.go`
|
||||
[#8284](https://github.com/pulumi/pulumi/pull/8284)
|
||||
|
||||
|
@ -11,8 +21,6 @@
|
|||
|
||||
[#8275](https://github.com/pulumi/pulumi/pull/8275)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [codegen/go] - Interaction between the `plain` and `default` tags of a type.
|
||||
[#8254](https://github.com/pulumi/pulumi/pull/8254)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2016-2020, Pulumi Corporation.
|
||||
// Copyright 2016-2021, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +16,7 @@ package pulumi
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -29,16 +30,35 @@ type Alias struct {
|
|||
Name StringInput
|
||||
// The previous type of the resource. If not provided, the current type of the resource is used.
|
||||
Type StringInput
|
||||
// The previous parent of the resource. If not provided, the current parent of the resource is used.
|
||||
// The previous parent of the resource. If not provided, the current parent of the resource is used by default.
|
||||
// This option is mutually exclusive to `ParentURN` and `NoParent`.
|
||||
// Use `Alias { NoParent: pulumi.Bool(true) }` to avoid defaulting to the current parent.
|
||||
Parent Resource
|
||||
// The previous parent of the resource in URN format, mutually exclusive to 'Parent'
|
||||
// The previous parent of the resource in URN format, mutually exclusive to `Parent` and `ParentURN`.
|
||||
// To specify no original parent, use `Alias { NoParent: pulumi.Bool(true) }`.
|
||||
ParentURN URNInput
|
||||
// When true, indicates that the resource previously had no parent.
|
||||
// This option is mutually exclusive to `Parent` and `ParentURN`.
|
||||
NoParent BoolInput
|
||||
// The name of the previous stack of the resource. If not provided, defaults to `context.GetStack()
|
||||
Stack StringInput
|
||||
// The previous project of the resource. If not provided, defaults to `context.GetProject()`.
|
||||
Project StringInput
|
||||
}
|
||||
|
||||
// More then one bool is set to true.
|
||||
func multipleTrue(booleans ...bool) bool {
|
||||
var found bool
|
||||
for _, b := range booleans {
|
||||
if b && found {
|
||||
return true
|
||||
} else if b {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a Alias) collapseToURN(defaultName, defaultType string, defaultParent Resource,
|
||||
defaultProject, defaultStack string) (URNOutput, error) {
|
||||
|
||||
|
@ -55,16 +75,27 @@ func (a Alias) collapseToURN(defaultName, defaultType string, defaultParent Reso
|
|||
t = String(defaultType)
|
||||
}
|
||||
|
||||
var parent StringInput
|
||||
if a.Parent != nil && a.ParentURN != nil {
|
||||
return URNOutput{}, errors.New("alias can specify either Parent or ParentURN but not both")
|
||||
var parent StringInput = String("")
|
||||
if defaultParent != nil {
|
||||
parent = defaultParent.URN().ToStringOutput()
|
||||
}
|
||||
if multipleTrue(a.Parent != nil, a.ParentURN != nil, a.NoParent != nil) {
|
||||
return URNOutput{}, errors.New("alias can specify Parent, ParentURN or NoParent but not more then one")
|
||||
}
|
||||
if a.Parent != nil {
|
||||
parent = a.Parent.URN()
|
||||
parent = a.Parent.URN().ToStringOutput()
|
||||
}
|
||||
if a.ParentURN != nil {
|
||||
parent = a.ParentURN.ToURNOutput()
|
||||
}
|
||||
if a.NoParent != nil {
|
||||
parent = All(a.NoParent.ToBoolOutput(), parent).ApplyT(func(a []interface{}) string {
|
||||
if a[0].(bool) {
|
||||
return ""
|
||||
}
|
||||
return a[1].(string)
|
||||
}).(StringOutput)
|
||||
}
|
||||
|
||||
project := a.Project
|
||||
if project == nil {
|
||||
|
@ -80,20 +111,27 @@ func (a Alias) collapseToURN(defaultName, defaultType string, defaultParent Reso
|
|||
|
||||
// CreateURN computes a URN from the combination of a resource name, resource type, and optional parent,
|
||||
func CreateURN(name, t, parent, project, stack StringInput) URNOutput {
|
||||
var parentPrefix StringInput
|
||||
if parent != nil {
|
||||
parentPrefix = parent.ToStringOutput().ApplyT(func(p string) string {
|
||||
return p[0:strings.LastIndex(p, "::")] + "$"
|
||||
}).(StringOutput)
|
||||
} else {
|
||||
parentPrefix = All(stack, project).ApplyT(func(a []interface{}) string {
|
||||
return "urn:pulumi:" + a[0].(string) + "::" + a[1].(string) + "::"
|
||||
}).(StringOutput)
|
||||
|
||||
if parent == nil {
|
||||
parent = String("")
|
||||
}
|
||||
|
||||
return All(parentPrefix, t, name).ApplyT(func(a []interface{}) URN {
|
||||
return URN(a[0].(string) + a[1].(string) + "::" + a[2].(string))
|
||||
createURN := func(parent, stack, project, t, name string) URN {
|
||||
var parentPrefix string
|
||||
if parent == "" {
|
||||
parentPrefix = "urn:pulumi:" + stack + "::" + project + "::"
|
||||
} else {
|
||||
ix := strings.LastIndex(parent, "::")
|
||||
if ix == -1 {
|
||||
panic(fmt.Sprintf("Expected 'parent' string '%s' to contain '::'", parent))
|
||||
}
|
||||
parentPrefix = parent[0:ix] + "$"
|
||||
}
|
||||
return URN(parentPrefix + t + "::" + name)
|
||||
}
|
||||
// The explicit call to `ToStringOutput` is necessary because `URNOutput`
|
||||
// conforms to `StringInput` so `parent.(string)` can fail without the
|
||||
// explicit conversion.
|
||||
return All(parent.ToStringOutput(), stack, project, t, name).ApplyT(func(a []interface{}) URN {
|
||||
return createURN(a[0].(string), a[1].(string), a[2].(string), a[3].(string), a[4].(string))
|
||||
}).(URNOutput)
|
||||
}
|
||||
|
||||
|
@ -108,5 +146,5 @@ func inheritedChildAlias(childName, parentName, childType, project, stack string
|
|||
return string(parentPrefix) + childName[len(parentName):]
|
||||
}).(StringOutput)
|
||||
}
|
||||
return CreateURN(aliasName, String(childType), parentURN, String(project), String(stack))
|
||||
return CreateURN(aliasName, String(childType), parentURN.ToStringOutput(), String(project), String(stack))
|
||||
}
|
||||
|
|
84
sdk/go/pulumi/alias_test.go
Normal file
84
sdk/go/pulumi/alias_test.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2016-2021, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pulumi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var aliasTestCases = []struct {
|
||||
name string
|
||||
alias func(t *testing.T) Alias
|
||||
expectedURN string
|
||||
}{
|
||||
{
|
||||
"plain",
|
||||
func(*testing.T) Alias {
|
||||
return Alias{
|
||||
Type: String("kubernetes:storage.k8s.io/v1beta1:CSIDriver"),
|
||||
}
|
||||
},
|
||||
"AnUrn$kubernetes:storage.k8s.io/v1beta1:CSIDriver::defName",
|
||||
},
|
||||
{
|
||||
"noParent",
|
||||
func(*testing.T) Alias {
|
||||
return Alias{
|
||||
Type: String("kubernetes:storage.k8s.io/v1beta1:CSIDriver"),
|
||||
NoParent: Bool(true),
|
||||
}
|
||||
}, "urn:pulumi:defStack::defProject::kubernetes:storage.k8s.io/v1beta1:CSIDriver::defName",
|
||||
},
|
||||
{
|
||||
"parent",
|
||||
func(t *testing.T) Alias {
|
||||
return Alias{
|
||||
Type: String("kubernetes:storage.k8s.io/v1beta1:CSIDriver"),
|
||||
Parent: newResource(t, URN("AParent::AParent"), ID("theParent")),
|
||||
}
|
||||
}, "AParent$kubernetes:storage.k8s.io/v1beta1:CSIDriver::defName",
|
||||
},
|
||||
{
|
||||
"parentURN",
|
||||
func(*testing.T) Alias {
|
||||
return Alias{
|
||||
Type: String("kubernetes:storage.k8s.io/v1beta1:CSIDriver"),
|
||||
ParentURN: URN("AParent::AParent"),
|
||||
}
|
||||
}, "AParent$kubernetes:storage.k8s.io/v1beta1:CSIDriver::defName",
|
||||
},
|
||||
}
|
||||
|
||||
func TestAliasResolution(t *testing.T) {
|
||||
for _, tt := range aliasTestCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
parent := newResource(t, URN("AnUrn::ASegment"), ID("hello"))
|
||||
out, err := tt.alias(t).collapseToURN("defName", "defType", parent, "defProject", "defStack")
|
||||
assert.NoError(t, err)
|
||||
urn, _, _, err := out.awaitURN(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, URN(tt.expectedURN), urn)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newResource(t *testing.T, urn URN, id ID) Resource {
|
||||
ctx, err := NewContext(context.Background(), RunInfo{})
|
||||
assert.NoError(t, err)
|
||||
return newSimpleCustomResource(ctx, urn, id)
|
||||
}
|
|
@ -551,6 +551,7 @@ func (ctx *Context) ReadResource(
|
|||
}
|
||||
|
||||
options := merge(opts...)
|
||||
aliasParent := options.Parent
|
||||
if options.Parent == nil {
|
||||
options.Parent = ctx.stack
|
||||
}
|
||||
|
@ -563,7 +564,7 @@ func (ctx *Context) ReadResource(
|
|||
}
|
||||
|
||||
// Collapse aliases to URNs.
|
||||
aliasURNs, err := ctx.collapseAliases(options.Aliases, t, name, options.Parent)
|
||||
aliasURNs, err := ctx.collapseAliases(options.Aliases, t, name, aliasParent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -736,6 +737,7 @@ func (ctx *Context) registerResource(
|
|||
}
|
||||
|
||||
options := merge(opts...)
|
||||
parent := options.Parent
|
||||
if options.Parent == nil {
|
||||
options.Parent = ctx.stack
|
||||
}
|
||||
|
@ -748,7 +750,7 @@ func (ctx *Context) registerResource(
|
|||
}
|
||||
|
||||
// Collapse aliases to URNs.
|
||||
aliasURNs, err := ctx.collapseAliases(options.Aliases, t, name, options.Parent)
|
||||
aliasURNs, err := ctx.collapseAliases(options.Aliases, t, name, parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -80,9 +80,13 @@ func NewFooComponent3(ctx *pulumi.Context,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
alias := &pulumi.Alias{
|
||||
Parent: childAliasParent,
|
||||
var alias = &pulumi.Alias{}
|
||||
if childAliasParent != nil {
|
||||
alias.Parent = childAliasParent
|
||||
} else {
|
||||
alias.NoParent = pulumi.Bool(true)
|
||||
}
|
||||
|
||||
aliasOpt := pulumi.Aliases([]pulumi.Alias{*alias})
|
||||
parentOpt := pulumi.Parent(fooComp)
|
||||
_, err = NewFooComponent2(ctx, name+"-child", aliasOpt, parentOpt)
|
||||
|
@ -114,7 +118,7 @@ func main() {
|
|||
return err
|
||||
}
|
||||
alias := &pulumi.Alias{
|
||||
Parent: nil,
|
||||
NoParent: pulumi.Bool(true),
|
||||
}
|
||||
aliasOpt := pulumi.Aliases([]pulumi.Alias{*alias})
|
||||
parentOpt := pulumi.Parent(comp2)
|
||||
|
|
Loading…
Reference in a new issue