Skip to content

Commit

Permalink
Always apply schema level secrets
Browse files Browse the repository at this point in the history
This will resolve pulumi/pulumi-docker-build#403 when
pulumi-docker-build upgrades to this version.
  • Loading branch information
iwahbe committed Jan 13, 2025
1 parent eb05f00 commit 1401699
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 19 deletions.
2 changes: 1 addition & 1 deletion infer/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (c *config[T]) checkConfig(ctx context.Context, req p.CheckRequest) (p.Chec
return p.CheckResponse{}, err
}
return p.CheckResponse{
Inputs: inputs,
Inputs: applySecrets[T](inputs),
Failures: failures,
}, nil
}
Expand Down
44 changes: 26 additions & 18 deletions infer/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -865,41 +865,50 @@ func (*derivedResourceController[R, I, O]) getInstance() *R {
}

func (rc *derivedResourceController[R, I, O]) Check(ctx context.Context, req p.CheckRequest) (p.CheckResponse, error) {
encoder, i, failures, err := decodeCheckingMapErrors[I](req.News)
if err != nil {
return p.CheckResponse{}, err
}
if len(failures) > 0 {
return p.CheckResponse{
// If we failed to decode, we apply secrets pro-actively to ensure
// that they don't leak into previews.
Inputs: applySecrets[I](req.News),
Failures: failures,
}, nil
}

var r R
if r, ok := ((interface{})(r)).(CustomCheck[I]); ok {
// The user implemented check manually, so call that.
//
// We do not apply defaults or secrets if the user has implemented Check
// themselves. Defaults and secrets are applied by [DefaultCheck].

defCheckEnc, i, failures, err := callCustomCheck(ctx, r, req.Urn.Name(), req.Olds, req.News)
backupEncoder, _, _, _ := decodeCheckingMapErrors[I](req.News)

encoder, i, failures, err := callCustomCheck(ctx, r, req.Urn.Name(), req.Olds, req.News)
if err != nil {
return p.CheckResponse{}, err
}
if defCheckEnc != nil {
encoder = *defCheckEnc

// callCustomCheck will have an encoder if and only if the custom check
// calls [DefaultCheck].
//
// If it doesn't have an encoder, but no error was returned, we do our
// best to recover secrets, unknowns, etc by calling
// decodeCheckingMapErrors to re-derive an encoder to use.
if encoder == nil {
encoder = &backupEncoder
}

inputs, err := encoder.Encode(i)
return p.CheckResponse{
Inputs: inputs,
Inputs: applySecrets[I](inputs),
Failures: failures,
}, err
}

encoder, i, failures, err := decodeCheckingMapErrors[I](req.News)
if err != nil {
return p.CheckResponse{}, err
}
if len(failures) > 0 {
return p.CheckResponse{
// If we failed to decode, we apply secrets pro-actively to ensure
// that they don't leak into previews.
Inputs: applySecrets[I](req.News),
Failures: failures,
}, nil
}

if i, err = defaultCheck(i); err != nil {
return p.CheckResponse{}, fmt.Errorf("unable to apply defaults: %w", err)
}
Expand Down Expand Up @@ -933,7 +942,6 @@ func callCustomCheck[T any](
//
// It also adds defaults to inputs as necessary, as defined by [Annotator.SetDefault].
func DefaultCheck[I any](ctx context.Context, inputs resource.PropertyMap) (I, []p.CheckFailure, error) {
inputs = applySecrets[I](inputs)
enc, i, failures, err := decodeCheckingMapErrors[I](inputs)

if v, ok := ctx.Value(defaultCheckEncoderKey{}).(*defaultCheckEncoderValue); ok {
Expand Down
44 changes: 44 additions & 0 deletions infer/tests/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
package tests

import (
"context"
"testing"

"github.com/pulumi/pulumi-go-provider/infer"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -257,3 +259,45 @@ func TestCheckDefaultsRecursive(t *testing.T) {
}, resp.Inputs)

}

func TestCheckAlwaysAppliesSecrets(t *testing.T) {
t.Parallel()

prov := provider()
resp, err := prov.Check(p.CheckRequest{
Urn: urn("CustomCheckNoDefaults", "check-env"),
News: resource.PropertyMap{
"input": resource.NewProperty("value"),
},
})
require.NoError(t, err)

assert.Equal(t, resource.PropertyMap{
"input": resource.MakeSecret(resource.NewProperty("value")),
}, resp.Inputs)
}

var (
_ infer.CustomResource[CustomCheckNoDefaultsArgs, CustomCheckNoDefaultsOutput] = &CustomCheckNoDefaults{}
_ infer.CustomCheck[CustomCheckNoDefaultsArgs] = &CustomCheckNoDefaults{}
)

type CustomCheckNoDefaults struct{}

func (w *CustomCheckNoDefaults) Check(_ context.Context,
_ string, _ resource.PropertyMap, m resource.PropertyMap,
) (CustomCheckNoDefaultsArgs, []p.CheckFailure, error) {
return CustomCheckNoDefaultsArgs{Input: m["input"].StringValue()}, nil, nil
}

type CustomCheckNoDefaultsArgs struct {
Input string `pulumi:"input" provider:"secret"`
}

type CustomCheckNoDefaultsOutput struct{ CustomCheckNoDefaultsArgs }

func (w *CustomCheckNoDefaults) Create(
ctx context.Context, name string, inputs CustomCheckNoDefaultsArgs, preview bool,
) (string, CustomCheckNoDefaultsOutput, error) {
return "id", CustomCheckNoDefaultsOutput{inputs}, nil
}
1 change: 1 addition & 0 deletions infer/tests/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ func providerOpts(config infer.InferredConfig) infer.Options {
infer.Resource[*Recursive, RecursiveArgs, RecursiveOutput](),
infer.Resource[*ReadConfig, ReadConfigArgs, ReadConfigOutput](),
infer.Resource[*ReadConfigCustom, ReadConfigCustomArgs, ReadConfigCustomOutput](),
infer.Resource[*CustomCheckNoDefaults, CustomCheckNoDefaultsArgs, CustomCheckNoDefaultsOutput](),
},
Functions: []infer.InferredFunction{
infer.Function[*GetJoin, JoinArgs, JoinResult](),
Expand Down
6 changes: 6 additions & 0 deletions middleware/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"reflect"
"strings"
"sync"

"github.com/hashicorp/go-multierror"
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
Expand Down Expand Up @@ -92,6 +93,8 @@ type state struct {
lowerSchema *cache
combinedSchema *cache
innerGetSchema func(ctx context.Context, req p.GetSchemaRequest) (p.GetSchemaResponse, error)

m sync.Mutex
}

// Options sets the schema options used by [Wrap].
Expand Down Expand Up @@ -179,6 +182,9 @@ func Wrap(provider p.Provider, opts Options) p.Provider {
}

func (s *state) GetSchema(ctx context.Context, req p.GetSchemaRequest) (p.GetSchemaResponse, error) {
s.m.Lock()
defer s.m.Unlock()

if s.schema.isEmpty() {
spec, err := s.generateSchema(ctx)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions tests/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ func TestInferCustomCheckConfig(t *testing.T) {
}))

t.Run("with-default-check", func(t *testing.T) {
t.Parallel()
resp, err := s.CheckConfig(p.CheckRequest{
Urn: resource.CreateURN("p", "pulumi:providers:test", "", "test", "dev"),
News: resource.PropertyMap{
Expand All @@ -210,6 +211,7 @@ func TestInferCustomCheckConfig(t *testing.T) {
})

t.Run("without-default-check", func(t *testing.T) {
t.Parallel()
resp, err := s.CheckConfig(p.CheckRequest{
News: resource.PropertyMap{
"field": resource.NewProperty("value"),
Expand Down

0 comments on commit 1401699

Please sign in to comment.