Support stack references in Go SDK (#3829)

This commit is contained in:
Evan Boyle 2020-02-06 10:00:46 -08:00 committed by GitHub
parent 63be1b5d21
commit 411f1a179a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 225 additions and 0 deletions

View file

@ -1,6 +1,10 @@
CHANGELOG
=========
## HEAD (unreleased)
- Support stack references in the Go SDK.
[#3829](https://github.com/pulumi/pulumi/pull/3829)
## 1.10.0 (2020-02-05)
- Avoid writing checkpoints to backend storage in common case where no changes are being made.
[#3860](https://github.com/pulumi/pulumi/pull/3860)

View file

@ -0,0 +1,56 @@
package pulumi
import "reflect"
// StackReference manages a reference to a Pulumi stack.
type StackReference struct {
CustomResourceState
// Name is in the form "Org/Program/Stack"
Name StringOutput `pulumi:"name"`
// Outputs resolves with exports from the named stack
Outputs MapOutput `pulumi:"outputs"`
}
func (s *StackReference) GetOutput(name StringInput) AnyOutput {
return All(name, s.Outputs).
ApplyT(func(args []interface{}) interface{} {
n, outs := args[0].(string), args[1].(map[string]interface{})
return outs[n]
}).(AnyOutput)
}
type stackReferenceArgs struct {
Name string `pulumi:"name"`
}
// StackReferenceArgs is the input to NewStackReference that allows specifying a stack name
type StackReferenceArgs struct {
// Name is in the form "Org/Program/Stack"
Name StringInput
}
func (StackReferenceArgs) ElementType() reflect.Type {
return reflect.TypeOf((*stackReferenceArgs)(nil)).Elem()
}
// NewStackReference creates a stack reference that makes available outputs from the specified stack
func NewStackReference(ctx *Context, name string, args *StackReferenceArgs,
opts ...ResourceOption) (*StackReference, error) {
if args == nil {
args = &StackReferenceArgs{}
}
if args.Name == nil {
args.Name = StringInput(String(name))
}
id := args.Name.ToStringOutput().ApplyT(func(s string) ID { return ID(s) }).(IDOutput)
var ref StackReference
if err := ctx.ReadResource("pulumi:pulumi:StackReference", name, id, args, &ref, opts...); err != nil {
return nil, err
}
return &ref, nil
}

View file

@ -0,0 +1,59 @@
package pulumi
import (
"testing"
"github.com/pulumi/pulumi/pkg/resource"
"github.com/stretchr/testify/assert"
)
func TestStackReference(t *testing.T) {
var resName string
outputs := map[string]interface{}{
"foo": "bar",
"baz": []interface{}{"qux"},
"zed": map[string]interface{}{
"alpha": "beta",
},
}
mocks := &testMonitor{
NewResourceF: func(typeToken, name string, inputs resource.PropertyMap,
provider, id string) (string, resource.PropertyMap, error) {
assert.Equal(t, "pulumi:pulumi:StackReference", typeToken)
assert.Equal(t, resName, name)
assert.True(t, inputs.DeepEquals(resource.NewPropertyMapFromMap(map[string]interface{}{
"name": "stack",
})))
assert.Equal(t, "", provider)
assert.Equal(t, inputs["name"].StringValue(), id)
return inputs["name"].StringValue(), resource.NewPropertyMapFromMap(map[string]interface{}{
"name": "stack",
"outputs": outputs,
}), nil
},
}
err := RunErr(func(ctx *Context) error {
resName = "stack"
ref0, err := NewStackReference(ctx, resName, nil)
assert.NoError(t, err)
_, _, err = await(ref0.ID())
assert.NoError(t, err)
resName = "stack2"
ref1, err := NewStackReference(ctx, resName, &StackReferenceArgs{Name: String("stack")})
assert.NoError(t, err)
outs0, _, err := await(ref0.Outputs)
assert.NoError(t, err)
assert.Equal(t, outputs, outs0)
zed0, _, err := await(ref0.GetOutput(String("zed")))
assert.NoError(t, err)
assert.Equal(t, outputs["zed"], zed0)
outs1, _, err := await(ref1.Outputs)
assert.NoError(t, err)
assert.Equal(t, outputs, outs1)
zed1, _, err := await(ref1.GetOutput(String("zed")))
assert.NoError(t, err)
assert.Equal(t, outputs["zed"], zed1)
return nil
}, WithMocks("project", "stack", mocks))
assert.NoError(t, err)
}

View file

@ -1180,6 +1180,31 @@ func TestStackReferenceDotnet(t *testing.T) {
integration.ProgramTest(t, opts)
}
// Tests that stack references work in Go.
func TestStackReferenceGo(t *testing.T) {
if runtime.GOOS == WindowsOS {
t.Skip("Temporarily skipping test on Windows - pulumi/pulumi#3811")
}
if owner := os.Getenv("PULUMI_TEST_OWNER"); owner == "" {
t.Skipf("Skipping: PULUMI_TEST_OWNER is not set")
}
opts := &integration.ProgramTestOptions{
Dir: filepath.Join("stack_reference", "go"),
Quick: true,
Config: map[string]string{
"org": os.Getenv("PULUMI_TEST_OWNER"),
},
EditDirs: []integration.EditDir{
{
Dir: "step1",
Additive: true,
},
},
}
integration.ProgramTest(t, opts)
}
// Tests that we issue an error if we fail to locate the Python command when running
// a Python example.
func TestPython3NotInstalled(t *testing.T) {

View file

@ -0,0 +1,3 @@
name: stack_reference_go
description: A simple go program that has a stack reference.
runtime: go

View file

@ -0,0 +1,30 @@
// Copyright 2016-2020, Pulumi Corporation. All rights reserved.
package main
import (
"fmt"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/sdk/go/pulumi"
"github.com/pulumi/pulumi/sdk/go/pulumi/config"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
cfg := config.New(ctx, ctx.Project())
org := cfg.Require("org")
slug := fmt.Sprintf("%v/%v/%v", org, ctx.Project(), ctx.Stack())
_, err := pulumi.NewStackReference(ctx, slug, nil)
if err != nil {
return errors.Wrap(err, "Error reading stack reference.")
}
ctx.Export("val",
pulumi.StringArray([]pulumi.StringInput{pulumi.String("a"), pulumi.String("b")}))
return nil
})
}

View file

@ -0,0 +1,48 @@
// Copyright 2016-2020, Pulumi Corporation. All rights reserved.
package main
import (
"fmt"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/sdk/go/pulumi"
"github.com/pulumi/pulumi/sdk/go/pulumi/config"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
cfg := config.New(ctx, ctx.Project())
org := cfg.Require("org")
slug := fmt.Sprintf("%v/%v/%v", org, ctx.Project(), ctx.Stack())
stackRef, err := pulumi.NewStackReference(ctx, slug, nil)
if err != nil {
return errors.Wrap(err, "Error reading stack reference.")
}
val := pulumi.StringArrayOutput(stackRef.GetOutput(pulumi.String("val")))
errChan := make(chan error)
results := make(chan []string)
_ = val.ApplyStringArray(func(v []string) ([]string, error) {
if len(v) != 2 || v[0] != "a" || v[1] != "b" {
errChan <- errors.Errorf("Invalid result")
return nil, errors.Errorf("Invalid result")
}
results <- v
return v, nil
})
select {
case err = <-errChan:
return err
case <-results:
return nil
}
})
}