pulumi/pkg/resource/deploy/builtins.go
Alex Clemmer 2c7af058de Expose resource outputs through invoke
This command exposes a new resource `Invoke` operation,
`pulumi:pulumi:readStackResourceOutputs` which retrieves all resource
outputs for some user-specified stack, not including those deleted.

Fixes #2600.
2019-05-02 18:08:08 -07:00

210 lines
5.8 KiB
Go

package deploy
import (
"context"
"fmt"
"github.com/pkg/errors"
uuid "github.com/satori/go.uuid"
"github.com/pulumi/pulumi/pkg/resource"
"github.com/pulumi/pulumi/pkg/resource/plugin"
"github.com/pulumi/pulumi/pkg/tokens"
"github.com/pulumi/pulumi/pkg/util/contract"
"github.com/pulumi/pulumi/pkg/workspace"
)
type builtinProvider struct {
backendClient BackendClient
context context.Context
cancel context.CancelFunc
}
func newBuiltinProvider(backendClient BackendClient) *builtinProvider {
ctx, cancel := context.WithCancel(context.Background())
return &builtinProvider{
backendClient: backendClient,
context: ctx,
cancel: cancel,
}
}
func (p *builtinProvider) Close() error {
return nil
}
func (p *builtinProvider) Pkg() tokens.Package {
return "pulumi"
}
// CheckConfig validates the configuration for this resource provider.
func (p *builtinProvider) CheckConfig(olds,
news resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) {
return nil, nil, nil
}
// DiffConfig checks what impacts a hypothetical change to this provider's configuration will have on the provider.
func (p *builtinProvider) DiffConfig(olds, news resource.PropertyMap) (plugin.DiffResult, error) {
return plugin.DiffResult{Changes: plugin.DiffNone}, nil
}
func (p *builtinProvider) Configure(props resource.PropertyMap) error {
return nil
}
const stackReferenceType = "pulumi:pulumi:StackReference"
func (p *builtinProvider) Check(urn resource.URN, state, inputs resource.PropertyMap,
allowUnknowns bool) (resource.PropertyMap, []plugin.CheckFailure, error) {
typ := urn.Type()
if typ != stackReferenceType {
return nil, nil, errors.Errorf("unrecognized resource type '%v'", urn.Type())
}
var name resource.PropertyValue
for k := range inputs {
if k != "name" {
return nil, []plugin.CheckFailure{{Property: k, Reason: fmt.Sprintf("unknown property \"%v\"", k)}}, nil
}
}
name, ok := inputs["name"]
if !ok {
return nil, []plugin.CheckFailure{{Property: "name", Reason: `missing required property "name"`}}, nil
}
if !name.IsString() && !name.IsComputed() {
return nil, []plugin.CheckFailure{{Property: "name", Reason: `property "name" must be a string`}}, nil
}
return inputs, nil, nil
}
func (p *builtinProvider) Diff(urn resource.URN, id resource.ID, state, inputs resource.PropertyMap,
allowUnknowns bool) (plugin.DiffResult, error) {
contract.Assert(urn.Type() == stackReferenceType)
if !inputs["name"].DeepEquals(state["name"]) {
return plugin.DiffResult{
Changes: plugin.DiffSome,
ReplaceKeys: []resource.PropertyKey{"name"},
}, nil
}
return plugin.DiffResult{Changes: plugin.DiffNone}, nil
}
func (p *builtinProvider) Create(urn resource.URN,
inputs resource.PropertyMap) (resource.ID, resource.PropertyMap, resource.Status, error) {
contract.Assert(urn.Type() == stackReferenceType)
state, err := p.readStackReference(inputs)
if err != nil {
return "", nil, resource.StatusUnknown, err
}
id := resource.ID(uuid.NewV4().String())
return id, state, resource.StatusOK, nil
}
func (p *builtinProvider) Update(urn resource.URN, id resource.ID, state,
inputs resource.PropertyMap) (resource.PropertyMap, resource.Status, error) {
contract.Failf("unexpected update for builtin resource %v", urn)
contract.Assert(urn.Type() == stackReferenceType)
return state, resource.StatusOK, errors.New("unexpected update for builtin resource")
}
func (p *builtinProvider) Delete(urn resource.URN, id resource.ID,
state resource.PropertyMap) (resource.Status, error) {
contract.Assert(urn.Type() == stackReferenceType)
return resource.StatusOK, nil
}
func (p *builtinProvider) Read(urn resource.URN, id resource.ID,
inputs, state resource.PropertyMap) (plugin.ReadResult, resource.Status, error) {
contract.Assert(urn.Type() == stackReferenceType)
outputs, err := p.readStackReference(state)
if err != nil {
return plugin.ReadResult{}, resource.StatusUnknown, err
}
return plugin.ReadResult{
Inputs: inputs,
Outputs: outputs,
}, resource.StatusOK, nil
}
const readStackResourceOutputs = "pulumi:pulumi:readStackResourceOutputs"
func (p *builtinProvider) Invoke(tok tokens.ModuleMember,
args resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) {
if tok != readStackResourceOutputs {
return nil, nil, errors.Errorf("unrecognized function name: '%v'", tok)
}
outs, err := p.readStackResourceOutputs(args)
if err != nil {
return nil, nil, err
}
return outs, nil, nil
}
func (p *builtinProvider) GetPluginInfo() (workspace.PluginInfo, error) {
// return an error: this should not be called for the builtin provider
return workspace.PluginInfo{}, errors.New("the builtin provider does not report plugin info")
}
func (p *builtinProvider) SignalCancellation() error {
p.cancel()
return nil
}
func (p *builtinProvider) readStackReference(inputs resource.PropertyMap) (resource.PropertyMap, error) {
name, ok := inputs["name"]
contract.Assert(ok)
contract.Assert(name.IsString())
if p.backendClient == nil {
return nil, errors.New("no backend client is available")
}
outputs, err := p.backendClient.GetStackOutputs(p.context, name.StringValue())
if err != nil {
return nil, err
}
return resource.PropertyMap{
"name": name,
"outputs": resource.NewObjectProperty(outputs),
}, nil
}
func (p *builtinProvider) readStackResourceOutputs(inputs resource.PropertyMap) (resource.PropertyMap, error) {
name, ok := inputs["stackName"]
contract.Assert(ok)
contract.Assert(name.IsString())
if p.backendClient == nil {
return nil, errors.New("no backend client is available")
}
outputs, err := p.backendClient.GetStackResourceOutputs(p.context, name.StringValue())
if err != nil {
return nil, err
}
return resource.PropertyMap{
"name": name,
"outputs": resource.NewObjectProperty(outputs),
}, nil
}