Skip to content

Commit c59b010

Browse files
authored
Automatically infer an id for pf resources (#3193)
This applies the logic from `dynamic` to `pf` based resources. As more and more providers are moved to creating new resources with the plugin framework, more and more of our upgrades are failing due to the missing id error. This requires manual work for the team to create a `ComputeID` mapping. Since this `id` is not actually used for anything in Pulumi, we have decided to fallback to hardcoding a "missing ID" value. This will reduce the number of provider upgrades that require manual intervention by the team and decrease the delay in releasing upgrades. closes #3182
1 parent 998de46 commit c59b010

File tree

5 files changed

+44
-17
lines changed

5 files changed

+44
-17
lines changed

docs/guides/resource-ids.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,14 @@ If the type of the `"id"` attribute is not coercible to a string, you must set `
6060
error: Resource test_res has a problem: no "id" attribute. To map this resource consider specifying ResourceInfo.ComputeID
6161
```
6262

63-
If the resource simply doesn't have an `"id"` attribute, you will need to set `ResourceInfo.ComputeID`.
64-
If you want to delegate the ID field in Pulumi to another attribute, you should use `tfbridge.DelegateIDField` to produce a `ResourceInfo.ComputeID` compatible function.
63+
This error should not occur anymore. For dynamic providers and Plugin Framework resources the bridge falls back to emitting a placeholder
64+
value (`"missing ID"`, exported as `tfbridge.MissingIDPlaceholder`) so schema generation and provider execution no longer fail.
65+
Providing an explicit `ComputeID` keeps the generated provider more aligned with the upstream import story. If you want to delegate the ID field
66+
in Pulumi to another attribute, you should use `tfbridge.DelegateIDField` to produce a `ResourceInfo.ComputeID` compatible function.
67+
68+
When using `ResourceInfo.ComputeID` we typically map it to the "import id" of the resource, but there is nothing that requires it to be set to a
69+
specific value. The resource id and the import id do not need to match. Pulumi requires that each resource has an id (Terraform does not), but
70+
there is not requirement that the id mean anything. This is why we fall back to adding the `"missing ID"` value.
6571

6672
```go
6773
"test_res": {ComputeID: tfbridge.DelegateIDField("id", "testprovider", "https://github.com/pulumi/pulumi-testprovider")}

dynamic/internal/fixup/properties.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package fixup
2121

2222
import (
23-
"context"
2423
"errors"
2524
"fmt"
2625
"reflect"
@@ -100,16 +99,12 @@ func fixMissingIDs(fixCtx fixCtx, p *info.Provider) error {
10099
(id.Type() == shim.TypeString || getIDType(r.Schema) == "string") &&
101100
id.Computed()
102101
if !ok {
103-
r.Schema.ComputeID = missingID
102+
r.Schema.ComputeID = tfbridge.MissingIDComputeID()
104103
}
105104
return nil
106105
})
107106
}
108107

109-
func missingID(context.Context, resource.PropertyMap) (resource.ID, error) {
110-
return "missing ID", nil
111-
}
112-
113108
func fixPropertyConflict(fixCtx fixCtx, p *info.Provider) error {
114109
if p.Name == "" {
115110
return fmt.Errorf("must set p.Name")

pkg/pf/internal/check/check_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,8 @@ func TestMissingIDProperty(t *testing.T) {
101101
P: pfbridge.ShimProvider(testProvider{missingID: true}),
102102
})
103103

104-
assert.Equal(t, "error: Resource test_res has a problem: no \"id\" attribute. "+
105-
"To map this resource consider specifying ResourceInfo.ComputeID to a valid field on the upstream resource\n", stderr)
106-
107-
assert.ErrorContains(t, err, "There were 1 unresolved ID mapping errors")
104+
assert.Equal(t, "", stderr)
105+
assert.NoError(t, err)
108106
}
109107

110108
func TestMissingIDWithOverride(t *testing.T) {

pkg/pf/internal/check/checks.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func Provider(sink diag.Sink, info tfbridge.ProviderInfo) error {
4646
}
4747

4848
func checkIDProperties(sink diag.Sink, info tfbridge.ProviderInfo, isPFResource func(tfToken string) bool) error {
49-
errors := 0
49+
numErrors := 0
5050

5151
info.P.ResourcesMap().Range(func(rname string, resource shim.Resource) bool {
5252
// Unmapped resources are not available, so they don't need to be correct.
@@ -67,14 +67,32 @@ func checkIDProperties(sink diag.Sink, info tfbridge.ProviderInfo, isPFResource
6767
return true
6868
}
6969

70-
errors++
70+
// if the resource does not have an id, and the provider author has not
71+
// added a ComputeID override, then fallback to using MissingID.
72+
var missing errMissingIDAttribute
73+
if errors.As(err, &missing) {
74+
if info.Resources == nil {
75+
info.Resources = map[string]*tfbridge.ResourceInfo{}
76+
}
77+
resInfo := info.Resources[rname]
78+
if resInfo == nil {
79+
resInfo = &tfbridge.ResourceInfo{}
80+
info.Resources[rname] = resInfo
81+
}
82+
if resInfo.ComputeID == nil {
83+
resInfo.ComputeID = tfbridge.MissingIDComputeID()
84+
}
85+
return true
86+
}
87+
88+
numErrors++
7189
sink.Errorf(&diag.Diag{Message: resourceError{rname, err}.Error()})
7290

7391
return true
7492
})
7593

76-
if errors > 0 {
77-
return fmt.Errorf("There were %d unresolved ID mapping errors", errors)
94+
if numErrors > 0 {
95+
return fmt.Errorf("There were %d unresolved ID mapping errors", numErrors)
7896
}
7997

8098
return nil

pkg/tfbridge/info.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package tfbridge
1616

1717
import (
18+
"context"
1819
"fmt"
1920
"os"
2021
"strings"
@@ -24,7 +25,6 @@ import (
2425
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
2526
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
2627
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
27-
"golang.org/x/net/context"
2828

2929
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info"
3030
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/log"
@@ -35,6 +35,16 @@ type urnCtxKeyType struct{}
3535

3636
var urnCtxKey = urnCtxKeyType{}
3737

38+
// MissingIDPlaceholder is the fallback identifier used when a Terraform resource does not expose an "id" field.
39+
const MissingIDPlaceholder = "missing ID"
40+
41+
// MissingIDComputeID returns a ComputeID implementation that always supplies MissingIDPlaceholder.
42+
func MissingIDComputeID() ComputeID {
43+
return func(ctx context.Context, state resource.PropertyMap) (resource.ID, error) {
44+
return resource.ID(MissingIDPlaceholder), nil
45+
}
46+
}
47+
3848
// XWithUrn returns a copy of ctx with the resource URN value.
3949
//
4050
// XWithUrn is unstable and may be changed or removed in any release.

0 commit comments

Comments
 (0)