github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/deploy/builtins.go (about)

     1  package deploy
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"sort"
     8  
     9  	uuid "github.com/gofrs/uuid"
    10  
    11  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
    12  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
    13  	"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
    14  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
    15  	"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
    16  )
    17  
    18  type builtinProvider struct {
    19  	context context.Context
    20  	cancel  context.CancelFunc
    21  
    22  	backendClient BackendClient
    23  	resources     *resourceMap
    24  }
    25  
    26  func newBuiltinProvider(backendClient BackendClient, resources *resourceMap) *builtinProvider {
    27  	ctx, cancel := context.WithCancel(context.Background())
    28  	return &builtinProvider{
    29  		context:       ctx,
    30  		cancel:        cancel,
    31  		backendClient: backendClient,
    32  		resources:     resources,
    33  	}
    34  }
    35  
    36  func (p *builtinProvider) Close() error {
    37  	return nil
    38  }
    39  
    40  func (p *builtinProvider) Pkg() tokens.Package {
    41  	return "pulumi"
    42  }
    43  
    44  // GetSchema returns the JSON-serialized schema for the provider.
    45  func (p *builtinProvider) GetSchema(version int) ([]byte, error) {
    46  	return []byte("{}"), nil
    47  }
    48  
    49  // CheckConfig validates the configuration for this resource provider.
    50  func (p *builtinProvider) CheckConfig(urn resource.URN, olds,
    51  	news resource.PropertyMap, allowUnknowns bool) (resource.PropertyMap, []plugin.CheckFailure, error) {
    52  
    53  	return nil, nil, nil
    54  }
    55  
    56  // DiffConfig checks what impacts a hypothetical change to this provider's configuration will have on the provider.
    57  func (p *builtinProvider) DiffConfig(urn resource.URN, olds, news resource.PropertyMap,
    58  	allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) {
    59  	return plugin.DiffResult{Changes: plugin.DiffNone}, nil
    60  }
    61  
    62  func (p *builtinProvider) Configure(props resource.PropertyMap) error {
    63  	return nil
    64  }
    65  
    66  const stackReferenceType = "pulumi:pulumi:StackReference"
    67  
    68  func (p *builtinProvider) Check(urn resource.URN, state, inputs resource.PropertyMap,
    69  	allowUnknowns bool, randomSeed []byte) (resource.PropertyMap, []plugin.CheckFailure, error) {
    70  
    71  	typ := urn.Type()
    72  	if typ != stackReferenceType {
    73  		return nil, nil, fmt.Errorf("unrecognized resource type '%v'", urn.Type())
    74  	}
    75  
    76  	var name resource.PropertyValue
    77  	for k := range inputs {
    78  		if k != "name" {
    79  			return nil, []plugin.CheckFailure{{Property: k, Reason: fmt.Sprintf("unknown property \"%v\"", k)}}, nil
    80  		}
    81  	}
    82  
    83  	name, ok := inputs["name"]
    84  	if !ok {
    85  		return nil, []plugin.CheckFailure{{Property: "name", Reason: `missing required property "name"`}}, nil
    86  	}
    87  	if !name.IsString() && !name.IsComputed() {
    88  		return nil, []plugin.CheckFailure{{Property: "name", Reason: `property "name" must be a string`}}, nil
    89  	}
    90  	return inputs, nil, nil
    91  }
    92  
    93  func (p *builtinProvider) Diff(urn resource.URN, id resource.ID, state, inputs resource.PropertyMap,
    94  	allowUnknowns bool, ignoreChanges []string) (plugin.DiffResult, error) {
    95  
    96  	contract.Assert(urn.Type() == stackReferenceType)
    97  
    98  	if !inputs["name"].DeepEquals(state["name"]) {
    99  		return plugin.DiffResult{
   100  			Changes:     plugin.DiffSome,
   101  			ReplaceKeys: []resource.PropertyKey{"name"},
   102  		}, nil
   103  	}
   104  
   105  	return plugin.DiffResult{Changes: plugin.DiffNone}, nil
   106  }
   107  
   108  func (p *builtinProvider) Create(urn resource.URN, inputs resource.PropertyMap, timeout float64,
   109  	preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
   110  
   111  	contract.Assert(urn.Type() == stackReferenceType)
   112  
   113  	state, err := p.readStackReference(inputs)
   114  	if err != nil {
   115  		return "", nil, resource.StatusUnknown, err
   116  	}
   117  
   118  	var id resource.ID
   119  	if !preview {
   120  		// generate a new uuid
   121  		uuid, err := uuid.NewV4()
   122  		if err != nil {
   123  			return "", nil, resource.StatusOK, err
   124  		}
   125  		id = resource.ID(uuid.String())
   126  	}
   127  
   128  	return id, state, resource.StatusOK, nil
   129  }
   130  
   131  func (p *builtinProvider) Update(urn resource.URN, id resource.ID, state, inputs resource.PropertyMap, timeout float64,
   132  	ignoreChanges []string, preview bool) (resource.PropertyMap, resource.Status, error) {
   133  
   134  	contract.Failf("unexpected update for builtin resource %v", urn)
   135  	contract.Assert(urn.Type() == stackReferenceType)
   136  
   137  	return state, resource.StatusOK, errors.New("unexpected update for builtin resource")
   138  }
   139  
   140  func (p *builtinProvider) Delete(urn resource.URN, id resource.ID,
   141  	state resource.PropertyMap, timeout float64) (resource.Status, error) {
   142  
   143  	contract.Assert(urn.Type() == stackReferenceType)
   144  
   145  	return resource.StatusOK, nil
   146  }
   147  
   148  func (p *builtinProvider) Read(urn resource.URN, id resource.ID,
   149  	inputs, state resource.PropertyMap) (plugin.ReadResult, resource.Status, error) {
   150  	contract.Assertf(urn != "", "Read URN was empty")
   151  	contract.Assertf(id != "", "Read ID was empty")
   152  	contract.Assert(urn.Type() == stackReferenceType)
   153  
   154  	outputs, err := p.readStackReference(state)
   155  	if err != nil {
   156  		return plugin.ReadResult{}, resource.StatusUnknown, err
   157  	}
   158  
   159  	return plugin.ReadResult{
   160  		Inputs:  inputs,
   161  		Outputs: outputs,
   162  	}, resource.StatusOK, nil
   163  }
   164  
   165  func (p *builtinProvider) Construct(info plugin.ConstructInfo, typ tokens.Type, name tokens.QName, parent resource.URN,
   166  	inputs resource.PropertyMap, options plugin.ConstructOptions) (plugin.ConstructResult, error) {
   167  	return plugin.ConstructResult{}, errors.New("builtin resources may not be constructed")
   168  }
   169  
   170  const readStackOutputs = "pulumi:pulumi:readStackOutputs"
   171  const readStackResourceOutputs = "pulumi:pulumi:readStackResourceOutputs"
   172  const getResource = "pulumi:pulumi:getResource"
   173  
   174  func (p *builtinProvider) Invoke(tok tokens.ModuleMember,
   175  	args resource.PropertyMap) (resource.PropertyMap, []plugin.CheckFailure, error) {
   176  
   177  	switch tok {
   178  	case readStackOutputs:
   179  		outs, err := p.readStackReference(args)
   180  		if err != nil {
   181  			return nil, nil, err
   182  		}
   183  		return outs, nil, nil
   184  	case readStackResourceOutputs:
   185  		outs, err := p.readStackResourceOutputs(args)
   186  		if err != nil {
   187  			return nil, nil, err
   188  		}
   189  		return outs, nil, nil
   190  	case getResource:
   191  		outs, err := p.getResource(args)
   192  		if err != nil {
   193  			return nil, nil, err
   194  		}
   195  		return outs, nil, nil
   196  	default:
   197  		return nil, nil, fmt.Errorf("unrecognized function name: '%v'", tok)
   198  	}
   199  }
   200  
   201  func (p *builtinProvider) StreamInvoke(
   202  	tok tokens.ModuleMember, args resource.PropertyMap,
   203  	onNext func(resource.PropertyMap) error) ([]plugin.CheckFailure, error) {
   204  
   205  	return nil, fmt.Errorf("the builtin provider does not implement streaming invokes")
   206  }
   207  
   208  func (p *builtinProvider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info plugin.CallInfo,
   209  	options plugin.CallOptions) (plugin.CallResult, error) {
   210  
   211  	return plugin.CallResult{}, fmt.Errorf("the builtin provider does not implement call")
   212  }
   213  
   214  func (p *builtinProvider) GetPluginInfo() (workspace.PluginInfo, error) {
   215  	// return an error: this should not be called for the builtin provider
   216  	return workspace.PluginInfo{}, errors.New("the builtin provider does not report plugin info")
   217  }
   218  
   219  func (p *builtinProvider) SignalCancellation() error {
   220  	p.cancel()
   221  	return nil
   222  }
   223  
   224  func (p *builtinProvider) readStackReference(inputs resource.PropertyMap) (resource.PropertyMap, error) {
   225  	name, ok := inputs["name"]
   226  	contract.Assert(ok)
   227  	contract.Assert(name.IsString())
   228  
   229  	if p.backendClient == nil {
   230  		return nil, errors.New("no backend client is available")
   231  	}
   232  
   233  	outputs, err := p.backendClient.GetStackOutputs(p.context, name.StringValue())
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	secretOutputs := make([]resource.PropertyValue, 0)
   239  	for k, v := range outputs {
   240  		if v.ContainsSecrets() {
   241  			secretOutputs = append(secretOutputs, resource.NewStringProperty(string(k)))
   242  		}
   243  	}
   244  
   245  	// Sort the secret outputs so the order is deterministic, to avoid spurious diffs during updates.
   246  	sort.Slice(secretOutputs, func(i, j int) bool {
   247  		return secretOutputs[i].String() < secretOutputs[j].String()
   248  	})
   249  
   250  	return resource.PropertyMap{
   251  		"name":              name,
   252  		"outputs":           resource.NewObjectProperty(outputs),
   253  		"secretOutputNames": resource.NewArrayProperty(secretOutputs),
   254  	}, nil
   255  }
   256  
   257  func (p *builtinProvider) readStackResourceOutputs(inputs resource.PropertyMap) (resource.PropertyMap, error) {
   258  	name, ok := inputs["stackName"]
   259  	contract.Assert(ok)
   260  	contract.Assert(name.IsString())
   261  
   262  	if p.backendClient == nil {
   263  		return nil, errors.New("no backend client is available")
   264  	}
   265  
   266  	outputs, err := p.backendClient.GetStackResourceOutputs(p.context, name.StringValue())
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  
   271  	return resource.PropertyMap{
   272  		"name":    name,
   273  		"outputs": resource.NewObjectProperty(outputs),
   274  	}, nil
   275  }
   276  
   277  func (p *builtinProvider) getResource(inputs resource.PropertyMap) (resource.PropertyMap, error) {
   278  	urn, ok := inputs["urn"]
   279  	contract.Assert(ok)
   280  	contract.Assert(urn.IsString())
   281  
   282  	state, ok := p.resources.get(resource.URN(urn.StringValue()))
   283  	if !ok {
   284  		return nil, fmt.Errorf("unknown resource %v", urn.StringValue())
   285  	}
   286  
   287  	return resource.PropertyMap{
   288  		"urn":   urn,
   289  		"id":    resource.NewStringProperty(string(state.ID)),
   290  		"state": resource.NewObjectProperty(state.Outputs),
   291  	}, nil
   292  }