Do not pass arguments as secrets to CheckConfig/Configure

Providers from plugins require that configuration value be
strings. This means if we are passing a secret string to a
provider (for example, trying to configure a kubernetes provider based
on some secret kubeconfig) we need to be careful to remove the
"secretness" before actually making the calls into the provider.

Failure to do this resulted in errors saying that the provider
configuration values had to be strings, and of course, the values
logically where, they were just marked as secret strings

Fixes #2741
This commit is contained in:
Matt Ellis 2019-05-17 13:51:28 -07:00
parent 2cd4409c0d
commit 4f693af023
7 changed files with 69 additions and 1 deletions

View file

@ -2,6 +2,9 @@
### Improvements
- Fix an issue where creating a first class provider would fail if any of the
configuration values for the providers were secrets. (fixes [pulumi/pulumi#2741](https://github.com/pulumi/pulumi/issues/2741)).
## 0.17.12 (Released May 15, 2019)
### Improvements

View file

@ -89,6 +89,12 @@ func (p *provider) CheckConfig(olds, news resource.PropertyMap) (resource.Proper
// Ensure that all config values are strings or unknowns.
var failures []CheckFailure
for k, v := range news {
// The configure method has to accept strings, so we go through and strip off all the secret markers before
// doing our checks (this mimics stripping code we have in Configure itself).
for v.IsSecret() {
v = v.SecretValue().Element
}
if !v.IsString() && !v.IsComputed() {
failures = append(failures, CheckFailure{
Property: k,
@ -172,13 +178,18 @@ func (p *provider) Configure(inputs resource.PropertyMap) error {
label := fmt.Sprintf("%s.Configure()", p.label())
logging.V(7).Infof("%s executing (#vars=%d)", label, len(inputs))
// Convert the inputs to a config map. If any are unknown, do not configure the underlying plugin: instead, leavce
// Convert the inputs to a config map. If any are unknown, do not configure the underlying plugin: instead, leave
// the cfgknown bit unset and carry on.
config := make(map[string]string)
for k, v := range inputs {
if k == "version" {
continue
}
// The configure method has to accept strings, so we go through and strip off all the secret markers before
// calling configure.
for v.IsSecret() {
v = v.SecretValue().Element
}
switch {
case v.IsComputed():
p.cfgknown, p.acceptSecrets = false, false

View file

@ -758,3 +758,12 @@ func TestPython3NotInstalled(t *testing.T) {
},
})
}
// TestProviderSecretConfig that a first class provider can be created when it has secrets as part of its config.
func TestProviderSecretConfig(t *testing.T) {
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: "provider_secret_config",
Dependencies: []string{"@pulumi/pulumi"},
Quick: true,
})
}

View file

@ -0,0 +1,3 @@
name: provider-secret-config
runtime: nodejs
description: A minimal TypeScript Pulumi program

View file

@ -0,0 +1,11 @@
import * as pulumi from "@pulumi/pulumi";
// Regression test for [pulumi/pulumi#2741], you should be able to create an instance of a first class provider
// with secret configuration values, so long as these values are themselves strings.
class DynamicProvider extends pulumi.ProviderResource {
constructor(name: string, opts?: pulumi.ResourceOptions) {
super("pulumi-nodejs", name, { secretProperty: pulumi.secret("it's a secret to everybody") }, opts);
}
}
const p = new DynamicProvider("p");

View file

@ -0,0 +1,9 @@
{
"name": "typescript",
"devDependencies": {
"@types/node": "latest"
},
"peerDependencies": {
"@pulumi/pulumi": "latest"
}
}

View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"outDir": "bin",
"target": "es6",
"lib": [
"es6"
],
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true,
"strictNullChecks": true
},
"files": [
"index.ts"
]
}