Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return errors on parse failures for config.Try* #9407

Merged
merged 13 commits into from Apr 20, 2022
13 changes: 12 additions & 1 deletion CHANGELOG_PENDING.md
Expand Up @@ -5,5 +5,16 @@

### Bug Fixes

<<<<<<< HEAD
iwahbe marked this conversation as resolved.
Show resolved Hide resolved
- [codegen/node] - Fix an issue with escaping deprecation messages.
[#9371](https://github.com/pulumi/pulumi/pull/9371)

- [cli] - StackReferences will now correctly use the service bulk decryption end point.
[#9373](https://github.com/pulumi/pulumi/pull/9373)

- [sdk/go] - Correctly handle present but invalid config values.
[#9407](https://github.com/pulumi/pulumi/pull/9407)
=======
- [cli/plugin] - Dynamic provider binaries will now be found even if pulumi/bin is not on $PATH.
[#9396](https://github.com/pulumi/pulumi/pull/9396)
[#9396](https://github.com/pulumi/pulumi/pull/9396)
>>>>>>> master
22 changes: 18 additions & 4 deletions sdk/go/pulumi/config/config_test.go
Expand Up @@ -16,6 +16,7 @@ package config

import (
"context"
"errors"
"fmt"
"reflect"
"testing"
Expand All @@ -39,6 +40,7 @@ func TestConfig(t *testing.T) {
"testpkg:sss": "a string value",
"testpkg:bbb": "true",
"testpkg:intint": "42",
"testpkg:badint": "4d2",
"testpkg:fpfpfp": "99.963",
"testpkg:obj": `
{
Expand Down Expand Up @@ -96,6 +98,10 @@ func TestConfig(t *testing.T) {
assert.Equal(t, "a string value", cfg.Require("sss"))
assert.Equal(t, true, cfg.RequireBool("bbb"))
assert.Equal(t, 42, cfg.RequireInt("intint"))
assert.PanicsWithValue(t,
"fatal: A failure has occurred: unable to parse required configuration variable"+
" 'testpkg:badint'; unable to cast \"4d2\" of type string to int",
func() { cfg.RequireInt("badint") })
assert.Equal(t, 99.963, cfg.RequireFloat64("fpfpfp"))
cfg.RequireObject("obj", &testStruct)
assert.Equal(t, expectedTestStruct, testStruct)
Expand All @@ -122,7 +128,7 @@ func TestConfig(t *testing.T) {
_ = cfg.Require("missing")
}()

// Test Try, which returns an error for missing entries.
// Test Try, which returns an error for missing or invalid entries.
k1, err := cfg.Try("sss")
assert.Nil(t, err)
assert.Equal(t, "a string value", k1)
Expand All @@ -132,6 +138,9 @@ func TestConfig(t *testing.T) {
k3, err := cfg.TryInt("intint")
assert.Nil(t, err)
assert.Equal(t, 42, k3)
invalidInt, err := cfg.TryInt("badint")
assert.Error(t, err)
assert.Zero(t, invalidInt)
k4, err := cfg.TryFloat64("fpfpfp")
assert.Nil(t, err)
assert.Equal(t, 99.963, k4)
Expand All @@ -142,16 +151,21 @@ func TestConfig(t *testing.T) {
testStruct = TestStruct{}
// missing TryObject
err = cfg.TryObject("missing", &testStruct)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Equal(t, emptyTestStruct, testStruct)
assert.True(t, errors.Is(err, ErrMissingVar))
testStruct = TestStruct{}
// malformed TryObject
err = cfg.TryObject("malobj", &testStruct)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Equal(t, emptyTestStruct, testStruct)
assert.False(t, errors.Is(err, ErrMissingVar))
testStruct = TestStruct{}
_, err = cfg.Try("missing")
assert.NotNil(t, err)
assert.Error(t, err)
assert.Equal(t, err.Error(),
"missing required configuration variable 'testpkg:missing'; run `pulumi config` to set")
assert.True(t, errors.Is(err, ErrMissingVar))
}

func TestSecretConfig(t *testing.T) {
Expand Down
20 changes: 16 additions & 4 deletions sdk/go/pulumi/config/require.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 29 additions & 9 deletions sdk/go/pulumi/config/try.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions sdk/go/pulumi/templates/config-require.go.template
@@ -1,4 +1,4 @@
// Copyright 2016-2018, Pulumi Corporation.
// Copyright 2016-2022, 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 Down Expand Up @@ -55,7 +55,11 @@ func RequireObject(ctx *pulumi.Context, key string, output interface{}) {
{{if .GenerateConfig}}
func require{{.Name}}(ctx *pulumi.Context, key, use, insteadOf string) {{.Type}} {
v := require(ctx, key, use, insteadOf)
return cast.To{{.Name}}(v)
o, err := cast.To{{.Name}}E(v)
iwahbe marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
contract.Failf("unable to parse required configuration variable '%s'; %s", key, err.Error())
}
return o
}

// Require{{.Name}} loads an optional configuration value by its key, as a {{.Type}}, or panics if it doesn't exist.
Expand Down
28 changes: 23 additions & 5 deletions sdk/go/pulumi/templates/config-try.go.template
@@ -1,4 +1,4 @@
// Copyright 2016-2018, Pulumi Corporation.
// Copyright 2016-2022, 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 @@ -25,11 +25,28 @@ import (
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

var ErrMissingVar = missingVariable{}

type missingVariable struct {
key string
}

func (m missingVariable) Error() string {
if m.key == "" {
return "missing required configuration variable"
}
return fmt.Sprintf("missing required configuration variable '%s'; run `pulumi config` to set", m.key)
}

func (m missingVariable) Is(target error) bool {
_, ok := target.(missingVariable)
return ok
}

func try(ctx *pulumi.Context, key, use, insteadOf string) (string, error) {
v, ok := get(ctx, key, use, insteadOf)
if !ok {
return "",
fmt.Errorf("missing required configuration variable '%s'; run `pulumi config` to set", key)
return "", missingVariable{key}
}
return v, nil
}
Expand Down Expand Up @@ -60,10 +77,11 @@ func try{{.Name}}(ctx *pulumi.Context, key, use, insteadOf string) ({{.Type}}, e
if err != nil {
return {{.DefaultConfig}}, err
}
return cast.To{{.Name}}(v), nil
return cast.To{{.Name}}E(v)
}

// Try{{.Name}} loads an optional configuration value by its key, as a {{.Type}}, or returns an error if it doesn't exist.
// Try{{.Name}} loads an optional configuration value by its key, as a {{.Type}},
// or returns an error if it doesn't exist or can't be parsed.
func Try{{.Name}}(ctx *pulumi.Context, key string) ({{.Type}}, error) {
return try{{.Name}}(ctx, key, "TrySecret{{.Name}}", "Try{{.Name}}")
}
Expand Down