Skip to content

Commit

Permalink
Interpret JSON Config values (#269)
Browse files Browse the repository at this point in the history
This PR adds a middleware to handle decoding JSON encoded config values.
The middleware is marked as deprecated, since we would like to remove
this functionality as soon as
pulumi/pulumi#15032 closes.

Fixes #171
  • Loading branch information
iwahbe authored Sep 10, 2024
1 parent 28c3ad1 commit 9c1bcdd
Show file tree
Hide file tree
Showing 17 changed files with 709 additions and 82 deletions.
16 changes: 8 additions & 8 deletions infer/apply_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"

"github.com/pulumi/pulumi-go-provider/infer/internal/ende"
"github.com/pulumi/pulumi-go-provider/internal/introspect"
"github.com/pulumi/pulumi-go-provider/internal/putil"
)

func applySecrets[I any](inputs resource.PropertyMap) resource.PropertyMap {
Expand All @@ -46,14 +46,14 @@ func (w *secretsWalker) walk(t reflect.Type, p resource.PropertyValue) (out reso

// Ensure we are working in raw value types for p

if ende.IsSecret(p) {
p = ende.MakePublic(p)
defer func() { out = ende.MakeSecret(p) }()
if putil.IsSecret(p) {
p = putil.MakePublic(p)
defer func() { out = putil.MakeSecret(p) }()
}

if ende.IsComputed(p) {
p = ende.MakeKnown(p)
defer func() { out = ende.MakeComputed(p) }()
if putil.IsComputed(p) {
p = putil.MakeKnown(p)
defer func() { out = putil.MakeComputed(p) }()
}

// Ensure we are working in raw value types for t
Expand Down Expand Up @@ -95,7 +95,7 @@ func (w *secretsWalker) walk(t reflect.Type, p resource.PropertyValue) (out reso
}
v = w.walk(field.Type, v)
if info.Secret {
v = ende.MakeSecret(v)
v = putil.MakeSecret(v)
}
obj[resource.PropertyKey(info.Name)] = v
}
Expand Down
9 changes: 5 additions & 4 deletions infer/internal/ende/ende.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/pulumi/pulumi-go-provider/infer/types"
"github.com/pulumi/pulumi-go-provider/internal/introspect"
"github.com/pulumi/pulumi-go-provider/internal/putil"

"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/sig"
Expand Down Expand Up @@ -104,10 +105,10 @@ func (p change) apply(v resource.PropertyValue) resource.PropertyValue {
})
}
if p.computed {
v = MakeComputed(v)
v = putil.MakeComputed(v)
}
if p.secret {
v = MakeSecret(v)
v = putil.MakeSecret(v)
}
return v
}
Expand Down Expand Up @@ -219,8 +220,8 @@ func (e *ende) walk(
}
}

contract.Assertf(!IsComputed(v), "failed to strip computed")
contract.Assertf(!IsSecret(v), "failed to strip secrets")
contract.Assertf(!putil.IsComputed(v), "failed to strip computed")
contract.Assertf(!putil.IsSecret(v), "failed to strip secrets")
contract.Assertf(!v.IsOutput(), "failed to strip outputs")

switch typ.Kind() {
Expand Down
28 changes: 0 additions & 28 deletions infer/internal/ende/ende_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,34 +69,6 @@ func TestRapidRoundTrip(t *testing.T) {
})
}

func TestRapidDeepEqual(t *testing.T) {
t.Parallel()
// Check that a value always equals itself
rapid.Check(t, func(t *rapid.T) {
value := rResource.PropertyValue(5).Draw(t, "value")

assert.True(t, DeepEquals(value, value))
})

// Check that "distinct" values never equal themselves.
rapid.Check(t, func(t *rapid.T) {
values := rapid.SliceOfNDistinct(rResource.PropertyValue(5), 2, 2,
func(v r.PropertyValue) string {
return v.String()
}).Draw(t, "distinct")
assert.False(t, DeepEquals(values[0], values[1]))
})

t.Run("folding", func(t *testing.T) {
assert.True(t, DeepEquals(
r.MakeComputed(r.MakeSecret(r.NewStringProperty("hi"))),
r.MakeSecret(r.MakeComputed(r.NewStringProperty("hi")))))
assert.False(t, DeepEquals(
r.MakeSecret(r.NewStringProperty("hi")),
r.MakeComputed(r.NewStringProperty("hi"))))
})
}

// Test that we round trip against our strongly typed interface.
func TestRoundtripIn(t *testing.T) {
t.Parallel()
Expand Down
3 changes: 3 additions & 0 deletions infer/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
p "github.com/pulumi/pulumi-go-provider"
t "github.com/pulumi/pulumi-go-provider/middleware"
"github.com/pulumi/pulumi-go-provider/middleware/cancel"
"github.com/pulumi/pulumi-go-provider/middleware/complexconfig" //nolint:staticcheck
mContext "github.com/pulumi/pulumi-go-provider/middleware/context"
"github.com/pulumi/pulumi-go-provider/middleware/dispatch"
"github.com/pulumi/pulumi-go-provider/middleware/schema"
Expand Down Expand Up @@ -166,6 +167,8 @@ func Wrap(provider p.Provider, opts Options) p.Provider {
return context.WithValue(ctx, configKey, opts.Config)
})
}

provider = complexconfig.Wrap(provider)
return cancel.Wrap(provider)
}

Expand Down
21 changes: 11 additions & 10 deletions infer/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/pulumi/pulumi-go-provider/infer/internal/ende"
"github.com/pulumi/pulumi-go-provider/internal"
"github.com/pulumi/pulumi-go-provider/internal/introspect"
"github.com/pulumi/pulumi-go-provider/internal/putil"
t "github.com/pulumi/pulumi-go-provider/middleware"
"github.com/pulumi/pulumi-go-provider/middleware/schema"
)
Expand Down Expand Up @@ -430,19 +431,19 @@ func markComputed(
oldInputs, inputs resource.PropertyMap, isCreate bool,
) resource.PropertyValue {
// If the value is already computed or if it is guaranteed to be known, we don't need to do anything
if field.known || ende.IsComputed(prop) {
if field.known || putil.IsComputed(prop) {
return prop
}

if input, ok := inputs[key]; ok && !ende.IsComputed(prop) && ende.DeepEquals(input, prop) {
if input, ok := inputs[key]; ok && !putil.IsComputed(prop) && putil.DeepEquals(input, prop) {
// prop is an output during a create, but the output mirrors an
// input in name and value. We don't make it computed.
return prop
}

// If this is during a create and the value is not explicitly marked as known, we mark it computed.
if isCreate {
return ende.MakeComputed(prop)
return putil.MakeComputed(prop)
}

// If a dependency is computed or has changed, we mark this field as computed.
Expand All @@ -464,8 +465,8 @@ func markComputed(
// (or do it for the user), ensuring that we have access to information
// that changed..
oldInput, hasOldInput := oldInputs[k]
if ende.IsComputed(inputs[k]) || (hasOldInput && !ende.DeepEquals(inputs[k], oldInput)) {
return ende.MakeComputed(prop)
if putil.IsComputed(inputs[k]) || (hasOldInput && !putil.DeepEquals(inputs[k], oldInput)) {
return putil.MakeComputed(prop)
}
}

Expand All @@ -478,20 +479,20 @@ func markSecret(
// If we should never return a secret, ensure that the field *is not* marked as
// secret, then return.
if field.neverSecret {
return ende.MakePublic(prop)
return putil.MakePublic(prop)
}

if ende.IsSecret(prop) {
if putil.IsSecret(prop) {
return prop
}

// If we should always return a secret, ensure that the field *is* marked as secret,
// then return.
if field.alwaysSecret {
return ende.MakeSecret(prop)
return putil.MakeSecret(prop)
}

if input, ok := inputs[key]; ok && ende.DeepEquals(input, prop) {
if input, ok := inputs[key]; ok && putil.DeepEquals(input, prop) {
// prop might depend on a secret value, but the output mirrors a input in
// name and value. We don't make it secret since it will either be public
// in the state as an input, or is already a secret.
Expand All @@ -505,7 +506,7 @@ func markSecret(
continue
}
if inputs[resource.PropertyKey(k.name)].ContainsSecrets() {
return ende.MakeSecret(prop)
return putil.MakeSecret(prop)
}
}

Expand Down
16 changes: 8 additions & 8 deletions infer/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import (
"pgregory.net/rapid"

p "github.com/pulumi/pulumi-go-provider"
"github.com/pulumi/pulumi-go-provider/infer/internal/ende"
"github.com/pulumi/pulumi-go-provider/infer/types"
"github.com/pulumi/pulumi-go-provider/internal/putil"
rRapid "github.com/pulumi/pulumi-go-provider/internal/rapid/resource"
)

Expand Down Expand Up @@ -94,32 +94,32 @@ func TestDefaultDependencies(t *testing.T) {
if newInput.ContainsUnknowns() {
for k, v := range output {
if newV, ok := newInput[k]; ok &&
ende.DeepEquals(newV, v) {
putil.DeepEquals(newV, v) {
continue
}
assert.True(t, ende.IsComputed(v),
assert.True(t, putil.IsComputed(v),
"key: %q", string(k))
}
} else if !ende.DeepEquals(
} else if !putil.DeepEquals(
r.NewObjectProperty(oldInput),
r.NewObjectProperty(newInput)) {
// If there is a change, then every item item should be
// computed, except items that mirror a known input.
for k, v := range output {
newV, ok := newInput[k]
if !ok {
assert.True(t, ende.IsComputed(v),
assert.True(t, putil.IsComputed(v),
"key: %q", string(k))
} else if !ende.IsComputed(v) {
assert.True(t, ende.DeepEquals(v, newV))
} else if !putil.IsComputed(v) {
assert.True(t, putil.DeepEquals(v, newV))
}
}
}

for k, v := range output {
// An input of the same name is secret, so this should be too.
if newInput[k].ContainsSecrets() {
assert.Truef(t, ende.IsSecret(v),
assert.Truef(t, putil.IsSecret(v),
"key: %q", string(k))
}
}
Expand Down
1 change: 0 additions & 1 deletion infer/tests/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ func TestCheckDefaults(t *testing.T) {
return pMap{
"pi": pInt(2),
"s": pString("one"),
"nested": defaultNestedMap(),
"nestedPtr": defaultNestedMap(),
}
}
Expand Down
4 changes: 2 additions & 2 deletions infer/tests/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/stretchr/testify/assert"

p "github.com/pulumi/pulumi-go-provider"
ende "github.com/pulumi/pulumi-go-provider/infer/internal/ende"
"github.com/pulumi/pulumi-go-provider/internal/putil"
)

func TestCreate(t *testing.T) {
Expand Down Expand Up @@ -148,7 +148,7 @@ func TestCreate(t *testing.T) {
prov := provider()
c := resource.MakeComputed
s := resource.NewStringProperty
sec := ende.MakeSecret
sec := putil.MakeSecret
resp, err := prov.Create(p.CreateRequest{
Urn: urn("Wired", "preview"),
Properties: resource.PropertyMap{
Expand Down
4 changes: 2 additions & 2 deletions infer/tests/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ type WithDefaultsArgs struct {
// NestedDefaults.
String string `pulumi:"s,optional"`
IntPtr *int `pulumi:"pi,optional"`
Nested NestedDefaults `pulumi:"nested,optional"`
Nested *NestedDefaults `pulumi:"nested,optional"`
NestedPtr *NestedDefaults `pulumi:"nestedPtr"`
OptWithReq *OptWithReq `pulumi:"optWithReq,optional"`
ArrNested []NestedDefaults `pulumi:"arrNested,optional"`
Expand Down Expand Up @@ -306,7 +306,7 @@ func (w *RecursiveArgs) Annotate(a infer.Annotator) {
}

type Config struct {
Value string `pulumi:"value,optional"`
Value *string `pulumi:"value,optional"`
}

type ReadConfig struct{}
Expand Down
5 changes: 3 additions & 2 deletions infer/internal/ende/util.go → internal/putil/putil.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023, Pulumi Corporation.
// Copyright 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.
Expand All @@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package ende
// Package putil contains utility functions for working with [resource.PropertyValue]s.
package putil

import "github.com/pulumi/pulumi/sdk/v3/go/common/resource"

Expand Down
42 changes: 37 additions & 5 deletions internal/rapid/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Typed struct {

// ValueOf annotates a [reflect.Type] with an appropriate random [resource.PropertyValue].
//
// A value is considered "appropriate" to a type if the vlaue can be safely unmarshaled
// A value is considered "appropriate" to a type if the value can be safely unmarshaled
// into the type.
func ValueOf(typ *rapid.Generator[reflect.Type]) *rapid.Generator[Typed] {
// Note: we generate values from types instead of vice versa because the set of
Expand Down Expand Up @@ -160,9 +160,17 @@ func PropertyValue(maxDepth int) *rapid.Generator[resource.PropertyValue] {
Object(maxDepth),
Secret(maxDepth),
Computed(maxDepth),
Output(maxDepth),
)
}

func PropertyMap(maxDepth int) *rapid.Generator[resource.PropertyMap] {
return rapid.Map(MapOf(PropertyValue(maxDepth-1)),
func(v resource.PropertyValue) resource.PropertyMap {
return v.ObjectValue()
})
}

func Primitive() *rapid.Generator[resource.PropertyValue] {
return rapid.OneOf(
String(),
Expand Down Expand Up @@ -260,6 +268,34 @@ func Computed(maxDepth int) *rapid.Generator[resource.PropertyValue] {
})
}

func Output(maxDepth int) *rapid.Generator[resource.PropertyValue] {
return rapid.Custom(func(t *rapid.T) resource.PropertyValue {
o := resource.Output{
Secret: rapid.Bool().Draw(t, "is-secret"),
Dependencies: outputDependencies().Draw(t, "dependencies"),
}

// The wire doesn't transport elements unless they are known, so we don't
// generate non-round-trip-able values.
if rapid.Bool().Draw(t, "is-known") {
o.Element = PropertyValue(maxDepth-1).Draw(t, "V")
o.Known = true
}

return resource.NewProperty(o)
})
}

func outputDependencies() *rapid.Generator[[]resource.URN] {
return rapid.SliceOfN(urn(), 0, 10)
}

func urn() *rapid.Generator[resource.URN] {
return rapid.Custom(func(t *rapid.T) resource.URN {
return resource.URN(rapid.String().Draw(t, "urn-body"))
})
}

func makeComputed(t *rapid.T, v resource.PropertyValue) resource.PropertyValue {
// If a value is marker, we fold the computedness into it.
if v.IsComputed() {
Expand All @@ -278,10 +314,6 @@ func makeComputed(t *rapid.T, v resource.PropertyValue) resource.PropertyValue {
return resource.NewOutputProperty(o)
}

// Otherwise we pick between the two kinds of secretness we can accept.
if rapid.Bool().Draw(t, "isOutput") {
return resource.MakeOutput(v)
}
return resource.MakeComputed(v)

}
Loading

0 comments on commit 9c1bcdd

Please sign in to comment.