Skip to content

Commit

Permalink
Need to mark the custom resource as a singleton even though it has a …
Browse files Browse the repository at this point in the history
…Delete method
  • Loading branch information
thomas11 committed Dec 6, 2024
1 parent 2378d82 commit ba9989d
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 2 deletions.
6 changes: 5 additions & 1 deletion provider/pkg/gen/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ func (g *packageGenerator) genResourceVariant(apiSpec *openapi.ResourceSpec, res
PutParameters: resourceRequest.parameters,
Response: resourceResponse.properties,
DefaultBody: resource.DefaultBody,
Singleton: resource.PathItem.Delete == nil,
Singleton: isSingleton(resource),
PutAsyncStyle: g.getAsyncStyle(updateOp),
DeleteAsyncStyle: g.getAsyncStyle(resource.PathItem.Delete),
ReadMethod: readMethod,
Expand All @@ -970,6 +970,10 @@ func (g *packageGenerator) genResourceVariant(apiSpec *openapi.ResourceSpec, res
return nil
}

func isSingleton(resource *resourceVariant) bool {
return resource.PathItem.Delete == nil || customresources.IsSingleton(resource.Path)
}

func (g *packageGenerator) generateAliases(resource *resourceVariant, typeNameAliases ...string) []pschema.AliasSpec {
var aliases []pschema.AliasSpec

Expand Down
35 changes: 35 additions & 0 deletions provider/pkg/gen/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,38 @@ func TestPropertyDescriptions(t *testing.T) {
assert.Equal(t, "This is not allowed", desc)
})
}

func TestResourceIsSingleton(t *testing.T) {
t.Run("singleton", func(t *testing.T) {
res := &resourceVariant{
ResourceSpec: &openapi.ResourceSpec{
Path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DBforPostgreSQL/flexibleServers/{serverName}/configurations/{configurationName}",
PathItem: &spec.PathItem{},
},
}
assert.True(t, isSingleton(res))
})

t.Run("implicit singleton", func(t *testing.T) {
res := &resourceVariant{
ResourceSpec: &openapi.ResourceSpec{
Path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Foo/bar",
PathItem: &spec.PathItem{},
},
}
assert.True(t, isSingleton(res))
})

t.Run("not a singleton", func(t *testing.T) {
res := &resourceVariant{
ResourceSpec: &openapi.ResourceSpec{
Path: "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Foo/bar",
PathItem: &spec.PathItem{
PathItemProps: spec.PathItemProps{
Delete: &spec.Operation{},
},
}},
}
assert.False(t, isSingleton(res))
})
}
6 changes: 5 additions & 1 deletion provider/pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,10 @@ func restoreDefaultInputsForRemovedProperties(inputs resource.PropertyMap, res r
return nil
}

func isSingleton(res *resources.AzureAPIResource) bool {
return res.Singleton
}

// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed
// to still exist.
func (k *azureNativeProvider) Delete(ctx context.Context, req *rpc.DeleteRequest) (*pbempty.Empty, error) {
Expand Down Expand Up @@ -1469,7 +1473,7 @@ func (k *azureNativeProvider) Delete(ctx context.Context, req *rpc.DeleteRequest
if err != nil {
return nil, azure.AzureError(err)
}
case res.Singleton:
case isSingleton(res):
// Singleton resources can't be deleted (or created), set them to the default state.
for _, param := range res.PutParameters {
if defaults.SkipDeleteOperation(res.Path, res.APIVersion) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func postgresFlexibleServerConfiguration(crudClientFactory crud.ResourceCrudClie
}
return nil
},
isSingleton: true,
}, nil
}

Expand Down
12 changes: 12 additions & 0 deletions provider/pkg/resources/customresources/customresources.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ type CustomResource struct {
Update func(ctx context.Context, id string, news, olds resource.PropertyMap) (map[string]interface{}, error)
// Delete an existing resource. Constructs the resource ID based on input values.
Delete func(ctx context.Context, id string, properties resource.PropertyMap) error
// IsSingleton is true if the resource is a singleton resource that cannot be created or deleted, only initialized
// and reset to a default state. Normally, we infer this from whether the `Delete` property is set. In some cases
// we need to set it explicitly if the resource is a singleton but does have a `Delete` property implementing a
// custom reset to the default state.
isSingleton bool
}

// ResourceDefinition is a combination of the external schema and runtime metadata
Expand Down Expand Up @@ -235,6 +240,13 @@ func HasCustomDelete(path string) bool {
return false
}

func IsSingleton(path string) bool {
if res, ok := featureLookup[path]; ok {
return res.isSingleton
}
return false
}

// SchemaMixins returns the map of custom resource schema definitions per resource token.
func SchemaMixins() map[string]schema.ResourceSpec {
specs := map[string]schema.ResourceSpec{}
Expand Down
20 changes: 20 additions & 0 deletions provider/pkg/resources/customresources/customresources_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2024, Pulumi Corporation. All rights reserved.

package customresources

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestIsSingleton(t *testing.T) {
assert.False(t, IsSingleton(""))
assert.False(t, IsSingleton("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DBforPostgreSQL/flexibleServers/{serverName}"))

postgresResource, err := postgresFlexibleServerConfiguration(nil, nil)
require.NoError(t, err)
assert.True(t, postgresResource.isSingleton)
assert.True(t, IsSingleton(postgresResource.path))
}

0 comments on commit ba9989d

Please sign in to comment.