2024-09-09 12:05:45 +00:00
|
|
|
// Copyright 2022-2024, 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.
|
|
|
|
|
2022-09-22 17:13:55 +00:00
|
|
|
package resource
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Alias struct {
|
[engine] Fix aliasing children
There is an issue with how the engine computes the aliases when the
resource is a child and doesn't have `Parent` set on the alias spec
(and the parent doesn't have any aliases).
```python
class FooResource(pulumi.ComponentResource):
def __init__(self, name, opts=None):
super().__init__("my:module:FooResource", name, None, opts)
class ComponentResource(pulumi.ComponentResource):
def __init__(self, name, opts=None):
super().__init__("my:module:ComponentResource", name, None, opts)
FooResource("childrenamed", pulumi.ResourceOptions(
parent=self,
aliases=[pulumi.Alias(name="child")]
))
```
In the example above, `ComponentResource` has a child `FooResource`
which was renamed from `child` to `childrenamed`.
The engine does not compute the correct alias:
```
expected: urn:pulumi:stack::project::my:module:ComponentResource$my:module:FooResource::child
actual: urn:pulumi:stack::project::my:module:FooResource::child
```
The problem is due to:
https://github.com/pulumi/pulumi/blob/117955ce14b6cf2dd093f8356ec7e26427aca4bf/pkg/resource/deploy/step_generator.go#L370-L382
... and:
https://github.com/pulumi/pulumi/blob/117955ce14b6cf2dd093f8356ec7e26427aca4bf/sdk/go/common/resource/alias.go#L24-L26
Because the alias spec doesn't have `parent` specified, the parent type
is not being included the computed alias URN.
Existing tests such as https://github.com/pulumi/pulumi/tree/master/tests/integration/aliases/python/rename_component_and_child
didn't catch the problem because the alias specifies both the `name` and
`parent`:
https://github.com/pulumi/pulumi/blob/117955ce14b6cf2dd093f8356ec7e26427aca4bf/tests/integration/aliases/python/rename_component_and_child/step2/__main__.py#L15
In this case, specifying `parent` on the alias shouldn't be necessary.
However, even after removing `parent` from the alias spec, the test
still succeeds because the parent itself has an alias:
https://github.com/pulumi/pulumi/blob/117955ce14b6cf2dd093f8356ec7e26427aca4bf/tests/integration/aliases/python/rename_component_and_child/step2/__main__.py#L18
... and parent aliases are inherited as part of a child's aliases, so we
still get an alias that works from the inheritance.
If we change the test to make no changes to the parent such that it
doesn't have any aliases, then we get the failure as we'd expect.
A similar problem will happen when retyping a child.
**Fix**
The fix involves using the child's parent in the calculated alias URN
when `Parent` isn't specified for the alias.
As part of this, we need to properly handled `NoParent` because right
now the engine is not correctly using it. The struct representing an
alias in the engine does not have a `NoParent` field:
https://github.com/pulumi/pulumi/blob/117955ce14b6cf2dd093f8356ec7e26427aca4bf/sdk/go/common/resource/alias.go#L8-L15
And therefore does not copy it over in the gRPC request:
https://github.com/pulumi/pulumi/blob/117955ce14b6cf2dd093f8356ec7e26427aca4bf/pkg/resource/deploy/source_eval.go#L1082-L1088
Instead, the `Alias` struct has an incorrect `NoParent` method which
returns `true` if the `Parent` field has a value of `""`:
https://github.com/pulumi/pulumi/blob/117955ce14b6cf2dd093f8356ec7e26427aca4bf/sdk/go/common/resource/alias.go#L24-L26
2023-05-01 21:36:32 +00:00
|
|
|
URN URN
|
|
|
|
Name string
|
|
|
|
Type string
|
|
|
|
Project string
|
|
|
|
Stack string
|
|
|
|
Parent URN
|
|
|
|
NoParent bool
|
2022-09-22 17:13:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Alias) GetURN() URN {
|
|
|
|
if a.URN != "" {
|
|
|
|
return a.URN
|
|
|
|
}
|
|
|
|
return CreateURN(a.Name, a.Type, a.Parent, a.Project, a.Stack)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateURN computes a URN from the combination of a resource name, resource type, and optional parent,
|
|
|
|
func CreateURN(name string, t string, parent URN, project string, stack string) URN {
|
|
|
|
createURN := func(parent URN, stack string, project string, t string, name string) URN {
|
|
|
|
parentString := string(parent)
|
|
|
|
var parentPrefix string
|
|
|
|
if parent == "" {
|
|
|
|
parentPrefix = "urn:pulumi:" + stack + "::" + project + "::"
|
|
|
|
} else {
|
|
|
|
ix := strings.LastIndex(parentString, "::")
|
|
|
|
if ix == -1 {
|
|
|
|
panic(fmt.Sprintf("Expected 'parent' string '%s' to contain '::'", parent))
|
|
|
|
}
|
|
|
|
parentPrefix = parentString[0:ix] + "$"
|
|
|
|
}
|
|
|
|
return URN(parentPrefix + t + "::" + name)
|
|
|
|
}
|
|
|
|
return createURN(parent, stack, project, t, name)
|
|
|
|
}
|