mirror of https://github.com/pulumi/pulumi.git
631 lines
21 KiB
Go
631 lines
21 KiB
Go
// Copyright 2016-2023, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package deploy
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
|
|
opentracing "github.com/opentracing/opentracing-go"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/slice"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/logging"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/result"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/rpcutil"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
|
|
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
|
|
)
|
|
|
|
// QuerySource is used to synchronously wait for a query result.
|
|
type QuerySource interface {
|
|
Wait() error
|
|
}
|
|
|
|
// NewQuerySource creates a `QuerySource` for some target runtime environment specified by
|
|
// `runinfo`, and supported by language plugins provided in `plugctx`.
|
|
func NewQuerySource(cancel context.Context, plugctx *plugin.Context, client BackendClient,
|
|
runinfo *EvalRunInfo, defaultProviderVersions map[tokens.Package]workspace.PluginSpec,
|
|
provs ProviderSource,
|
|
) (QuerySource, error) {
|
|
// Create a new builtin provider. This provider implements features such as `getStack`.
|
|
builtins := newBuiltinProvider(client, nil, plugctx.Diag)
|
|
|
|
reg := providers.NewRegistry(plugctx.Host, false, builtins)
|
|
|
|
// Allows queryResmon to communicate errors loading providers.
|
|
providerRegErrChan := make(chan error)
|
|
|
|
// First, fire up a resource monitor that will disallow all resource operations, as well as
|
|
// service calls for things like resource ouptuts of state snapshots.
|
|
//
|
|
// NOTE: Using the queryResourceMonitor here is *VERY* important, as its job is to disallow
|
|
// resource operations in query mode!
|
|
mon, err := newQueryResourceMonitor(builtins, defaultProviderVersions, provs, reg, plugctx,
|
|
providerRegErrChan, opentracing.SpanFromContext(cancel), runinfo)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to start resource monitor: %w", err)
|
|
}
|
|
|
|
// Create a new iterator with appropriate channels, and gear up to go!
|
|
src := &querySource{
|
|
mon: mon,
|
|
plugctx: plugctx,
|
|
runinfo: runinfo,
|
|
runLangPlugin: runLangPlugin,
|
|
langPluginFinChan: make(chan error),
|
|
providerRegErrChan: make(chan error),
|
|
cancel: cancel,
|
|
}
|
|
|
|
// Now invoke Run in a goroutine. All subsequent resource creation events will come in over the gRPC channel,
|
|
// and we will pump them through the channel. If the Run call ultimately fails, we need to propagate the error.
|
|
src.forkRun()
|
|
|
|
// Finally, return the fresh iterator that the caller can use to take things from here.
|
|
return src, nil
|
|
}
|
|
|
|
type querySource struct {
|
|
mon SourceResourceMonitor // the resource monitor, per iterator.
|
|
plugctx *plugin.Context // the plugin context.
|
|
runinfo *EvalRunInfo // the directives to use when running the program.
|
|
runLangPlugin func(*querySource) error // runs the language plugin.
|
|
langPluginFinChan chan error // communicates language plugin completion.
|
|
providerRegErrChan chan error // communicates errors loading providers
|
|
done bool // set to true when the evaluation is done.
|
|
res error // result when the channel is finished.
|
|
cancel context.Context
|
|
}
|
|
|
|
func (src *querySource) Close() error {
|
|
// Cancel the monitor and reclaim any associated resources.
|
|
src.done = true
|
|
return src.mon.Cancel()
|
|
}
|
|
|
|
func (src *querySource) Wait() error {
|
|
// If we are done, quit.
|
|
if src.done {
|
|
return src.res
|
|
}
|
|
|
|
select {
|
|
case src.res = <-src.langPluginFinChan:
|
|
// Language plugin has exited. No need to call `Close`.
|
|
src.done = true
|
|
return src.res
|
|
case src.res = <-src.providerRegErrChan:
|
|
// Provider registration has failed.
|
|
src.Close()
|
|
return src.res
|
|
case <-src.cancel.Done():
|
|
src.Close()
|
|
return src.res
|
|
}
|
|
}
|
|
|
|
// forkRun evaluate the query program in a separate goroutine. Completion or cancellation will cause
|
|
// `Wait` to stop blocking and return.
|
|
func (src *querySource) forkRun() {
|
|
// Fire up the goroutine to make the RPC invocation against the language runtime. As this executes, calls
|
|
// to queue things up in the resource channel will occur, and we will serve them concurrently.
|
|
go func() {
|
|
// Next, launch the language plugin. Communicate the error, if it exists, or nil if the
|
|
// program exited cleanly.
|
|
src.langPluginFinChan <- src.runLangPlugin(src)
|
|
}()
|
|
}
|
|
|
|
func runLangPlugin(src *querySource) error {
|
|
rt := src.runinfo.Proj.Runtime.Name()
|
|
rtopts := src.runinfo.Proj.Runtime.Options()
|
|
programInfo := plugin.NewProgramInfo(
|
|
/* rootDirectory */ src.runinfo.ProjectRoot,
|
|
/* programDirectory */ src.runinfo.Pwd,
|
|
/* entryPoint */ src.runinfo.Program,
|
|
/* options */ rtopts)
|
|
langhost, err := src.plugctx.Host.LanguageRuntime(rt, programInfo)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to launch language host %s: %w", rt, err)
|
|
}
|
|
contract.Assertf(langhost != nil, "expected non-nil language host %s", rt)
|
|
|
|
// Decrypt the configuration.
|
|
var config map[config.Key]string
|
|
if src.runinfo.Target != nil {
|
|
config, err = src.runinfo.Target.Config.Decrypt(src.runinfo.Target.Decrypter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var name, organization string
|
|
if src.runinfo.Target != nil {
|
|
name = src.runinfo.Target.Name.String()
|
|
organization = string(src.runinfo.Target.Organization)
|
|
}
|
|
|
|
// Now run the actual program.
|
|
progerr, bail, err := langhost.Run(plugin.RunInfo{
|
|
MonitorAddress: src.mon.Address(),
|
|
Stack: name,
|
|
Project: string(src.runinfo.Proj.Name),
|
|
Pwd: src.runinfo.Pwd,
|
|
Args: src.runinfo.Args,
|
|
Config: config,
|
|
DryRun: true,
|
|
QueryMode: true,
|
|
Parallel: math.MaxInt32,
|
|
Organization: organization,
|
|
Info: programInfo,
|
|
})
|
|
|
|
// Check if we were asked to Bail. This a special random constant used for that
|
|
// purpose.
|
|
if err == nil && bail {
|
|
return result.BailErrorf("run bailed")
|
|
}
|
|
|
|
if err == nil && progerr != "" {
|
|
// If the program had an unhandled error; propagate it to the caller.
|
|
err = fmt.Errorf("an unhandled error occurred: %v", progerr)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// newQueryResourceMonitor creates a new resource monitor RPC server intended to be used in Pulumi's
|
|
// "query mode".
|
|
func newQueryResourceMonitor(
|
|
builtins *builtinProvider, defaultProviderInfo map[tokens.Package]workspace.PluginSpec,
|
|
provs ProviderSource, reg *providers.Registry, plugctx *plugin.Context,
|
|
providerRegErrChan chan<- error, tracingSpan opentracing.Span, runinfo *EvalRunInfo,
|
|
) (*queryResmon, error) {
|
|
// Create our cancellation channel.
|
|
cancel := make(chan bool)
|
|
|
|
// Create channel for handling registrations.
|
|
providerRegChan := make(chan *registerResourceEvent)
|
|
|
|
// Create a new default provider manager.
|
|
d := &defaultProviders{
|
|
defaultProviderInfo: defaultProviderInfo,
|
|
providers: make(map[string]providers.Reference),
|
|
config: runinfo.Target,
|
|
requests: make(chan defaultProviderRequest),
|
|
providerRegChan: providerRegChan,
|
|
cancel: cancel,
|
|
}
|
|
|
|
go func() {
|
|
for e := range providerRegChan {
|
|
urn := syntheticProviderURN(e.goal)
|
|
|
|
checkResponse, err := reg.Check(context.TODO(), plugin.CheckRequest{
|
|
URN: urn,
|
|
Olds: resource.PropertyMap{},
|
|
News: e.goal.Properties,
|
|
})
|
|
if err != nil {
|
|
providerRegErrChan <- err
|
|
return
|
|
}
|
|
resp, err := reg.Create(context.TODO(), plugin.CreateRequest{
|
|
URN: urn,
|
|
Properties: checkResponse.Properties,
|
|
Timeout: 9999,
|
|
})
|
|
if err != nil {
|
|
providerRegErrChan <- err
|
|
return
|
|
}
|
|
|
|
contract.Assertf(resp.ID != "", "expected non-empty provider ID")
|
|
contract.Assertf(resp.ID != providers.UnknownID, "expected non-unknown provider ID")
|
|
|
|
e.done <- &RegisterResult{State: &resource.State{
|
|
Type: e.goal.Type,
|
|
URN: urn,
|
|
ID: resp.ID,
|
|
}}
|
|
}
|
|
}()
|
|
|
|
// New up an engine RPC server.
|
|
queryResmon := &queryResmon{
|
|
builtins: builtins,
|
|
providers: provs,
|
|
defaultProviders: d,
|
|
cancel: cancel,
|
|
reg: reg,
|
|
}
|
|
|
|
// Fire up a gRPC server and start listening for incomings.
|
|
handle, err := rpcutil.ServeWithOptions(rpcutil.ServeOptions{
|
|
Cancel: queryResmon.cancel,
|
|
Init: func(srv *grpc.Server) error {
|
|
pulumirpc.RegisterResourceMonitorServer(srv, queryResmon)
|
|
return nil
|
|
},
|
|
Options: rpcutil.OpenTracingServerInterceptorOptions(tracingSpan),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
monitorAddress := fmt.Sprintf("127.0.0.1:%d", handle.Port)
|
|
|
|
var config map[config.Key]string
|
|
if runinfo.Target != nil {
|
|
config, err = runinfo.Target.Config.Decrypt(runinfo.Target.Decrypter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
var name string
|
|
if runinfo.Target != nil {
|
|
name = runinfo.Target.Name.String()
|
|
}
|
|
|
|
queryResmon.callInfo = plugin.CallInfo{
|
|
Project: string(runinfo.Proj.Name),
|
|
Stack: name,
|
|
Config: config,
|
|
DryRun: true,
|
|
Parallel: math.MaxInt32,
|
|
MonitorAddress: monitorAddress,
|
|
}
|
|
queryResmon.addr = monitorAddress
|
|
queryResmon.done = handle.Done
|
|
|
|
go d.serve()
|
|
|
|
return queryResmon, nil
|
|
}
|
|
|
|
// queryResmon is a pulumirpc.ResourceMonitor that is meant to run in Pulumi's "query mode". It
|
|
// performs two critical functions:
|
|
//
|
|
// 1. Disallows all resource operations. `queryResmon` intercepts all resource operations and
|
|
// returns an error instead of allowing them to proceed.
|
|
// 2. Services requests for stack snapshots. This is primarily to allow us to allow queries across
|
|
// stack snapshots.
|
|
type queryResmon struct {
|
|
pulumirpc.UnimplementedResourceMonitorServer
|
|
|
|
builtins *builtinProvider // provides builtins such as `getStack`.
|
|
providers ProviderSource // the provider source itself.
|
|
defaultProviders *defaultProviders // the default provider manager.
|
|
addr string // the address the host is listening on.
|
|
cancel chan bool // a channel that can cancel the server.
|
|
done <-chan error // a channel that resolves when the server completes.
|
|
reg *providers.Registry // registry for resource providers.
|
|
callInfo plugin.CallInfo // information for call calls.
|
|
}
|
|
|
|
var _ SourceResourceMonitor = (*queryResmon)(nil)
|
|
|
|
// Query doesn't do anything with the abort channel, so we just construct a new one that we'll never send anything to.
|
|
func (rm *queryResmon) AbortChan() <-chan bool {
|
|
return make(<-chan bool)
|
|
}
|
|
|
|
// Address returns the address at which the monitor's RPC server may be reached.
|
|
func (rm *queryResmon) Address() string {
|
|
return rm.addr
|
|
}
|
|
|
|
// Cancel signals that the engine should be terminated, awaits its termination, and returns any
|
|
// errors that result.
|
|
func (rm *queryResmon) Cancel() error {
|
|
close(rm.cancel)
|
|
return <-rm.done
|
|
}
|
|
|
|
// getProviderReference fetches the provider reference for a resource, read, or invoke from the given package with the
|
|
// given unparsed provider reference. If the unparsed provider reference is empty, this function returns a reference
|
|
// to the default provider for the indicated package.
|
|
func getProviderReference(defaultProviders *defaultProviders, req providers.ProviderRequest,
|
|
rawProviderRef string,
|
|
) (providers.Reference, error) {
|
|
if rawProviderRef != "" {
|
|
ref, err := providers.ParseReference(rawProviderRef)
|
|
if err != nil {
|
|
return providers.Reference{}, fmt.Errorf("could not parse provider reference: %w", err)
|
|
}
|
|
return ref, nil
|
|
}
|
|
|
|
ref, err := defaultProviders.getDefaultProviderRef(req)
|
|
if err != nil {
|
|
return providers.Reference{}, err
|
|
}
|
|
return ref, nil
|
|
}
|
|
|
|
// getProviderFromSource fetches the provider plugin for a resource, read, or invoke from the given
|
|
// package with the given unparsed provider reference. If the unparsed provider reference is empty,
|
|
// this function returns the plugin for the indicated package's default provider.
|
|
func getProviderFromSource(
|
|
providerSource ProviderSource, defaultProviders *defaultProviders,
|
|
req providers.ProviderRequest, rawProviderRef string,
|
|
token tokens.ModuleMember,
|
|
) (plugin.Provider, error) {
|
|
providerRef, err := getProviderReference(defaultProviders, req, rawProviderRef)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getProviderFromSource: %w", err)
|
|
} else if providers.IsDenyDefaultsProvider(providerRef) {
|
|
msg := diag.GetDefaultProviderDenied("Invoke").Message
|
|
return nil, fmt.Errorf(msg, req.Package(), token)
|
|
}
|
|
|
|
provider, ok := providerSource.GetProvider(providerRef)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown provider '%v' -> '%v'", rawProviderRef, providerRef)
|
|
}
|
|
return provider, nil
|
|
}
|
|
|
|
// Invoke performs an invocation of a member located in a resource provider.
|
|
func (rm *queryResmon) Invoke(
|
|
ctx context.Context, req *pulumirpc.ResourceInvokeRequest,
|
|
) (*pulumirpc.InvokeResponse, error) {
|
|
tok := tokens.ModuleMember(req.GetTok())
|
|
label := fmt.Sprintf("QueryResourceMonitor.Invoke(%s)", tok)
|
|
|
|
providerReq, err := parseProviderRequest(
|
|
tok.Package(), req.GetVersion(),
|
|
req.GetPluginDownloadURL(), req.GetPluginChecksums(), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prov, err := getProviderFromSource(rm.reg, rm.defaultProviders, providerReq, req.GetProvider(), tok)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
args, err := plugin.UnmarshalProperties(
|
|
req.GetArgs(), plugin.MarshalOptions{
|
|
Label: label,
|
|
KeepUnknowns: true,
|
|
KeepSecrets: true,
|
|
KeepResources: true,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal %v args: %w", tok, err)
|
|
}
|
|
|
|
// Do the invoke and then return the arguments.
|
|
logging.V(5).Infof("QueryResourceMonitor.Invoke received: tok=%v #args=%v", tok, len(args))
|
|
resp, err := prov.Invoke(ctx, plugin.InvokeRequest{
|
|
Tok: tok,
|
|
Args: args,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invocation of %v returned an error: %w", tok, err)
|
|
}
|
|
mret, err := plugin.MarshalProperties(resp.Properties, plugin.MarshalOptions{
|
|
Label: label,
|
|
KeepUnknowns: true,
|
|
KeepResources: req.GetAcceptResources(),
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal return: %w", err)
|
|
}
|
|
|
|
chkfails := slice.Prealloc[*pulumirpc.CheckFailure](len(resp.Failures))
|
|
for _, failure := range resp.Failures {
|
|
chkfails = append(chkfails, &pulumirpc.CheckFailure{
|
|
Property: string(failure.Property),
|
|
Reason: failure.Reason,
|
|
})
|
|
}
|
|
|
|
return &pulumirpc.InvokeResponse{Return: mret, Failures: chkfails}, nil
|
|
}
|
|
|
|
func (rm *queryResmon) StreamInvoke(
|
|
req *pulumirpc.ResourceInvokeRequest, stream pulumirpc.ResourceMonitor_StreamInvokeServer,
|
|
) error {
|
|
tok := tokens.ModuleMember(req.GetTok())
|
|
label := fmt.Sprintf("QueryResourceMonitor.StreamInvoke(%s)", tok)
|
|
|
|
providerReq, err := parseProviderRequest(
|
|
tok.Package(), req.GetVersion(),
|
|
req.GetPluginDownloadURL(), req.GetPluginChecksums(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
prov, err := getProviderFromSource(rm.reg, rm.defaultProviders, providerReq, req.GetProvider(), tok)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
args, err := plugin.UnmarshalProperties(
|
|
req.GetArgs(), plugin.MarshalOptions{Label: label, KeepUnknowns: true})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to unmarshal %v args: %w", tok, err)
|
|
}
|
|
|
|
// Synchronously do the StreamInvoke and then return the arguments. This will block until the
|
|
// streaming operation completes!
|
|
logging.V(5).Infof("QueryResourceMonitor.StreamInvoke received: tok=%v #args=%v", tok, len(args))
|
|
resp, err := prov.StreamInvoke(context.TODO(), plugin.StreamInvokeRequest{
|
|
Tok: tok,
|
|
Args: args,
|
|
OnNext: func(event resource.PropertyMap) error {
|
|
mret, err := plugin.MarshalProperties(event, plugin.MarshalOptions{
|
|
Label: label,
|
|
KeepUnknowns: true,
|
|
KeepResources: req.GetAcceptResources(),
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal return: %w", err)
|
|
}
|
|
|
|
return stream.Send(&pulumirpc.InvokeResponse{Return: mret})
|
|
},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("streaming invocation of %v returned an error: %w", tok, err)
|
|
}
|
|
|
|
chkfails := slice.Prealloc[*pulumirpc.CheckFailure](len(resp.Failures))
|
|
for _, failure := range resp.Failures {
|
|
chkfails = append(chkfails, &pulumirpc.CheckFailure{
|
|
Property: string(failure.Property),
|
|
Reason: failure.Reason,
|
|
})
|
|
}
|
|
|
|
if len(chkfails) > 0 {
|
|
return stream.Send(&pulumirpc.InvokeResponse{Failures: chkfails})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Call dynamically executes a method in the provider associated with a component resource.
|
|
func (rm *queryResmon) Call(ctx context.Context, req *pulumirpc.ResourceCallRequest) (*pulumirpc.CallResponse, error) {
|
|
tok := tokens.ModuleMember(req.GetTok())
|
|
label := fmt.Sprintf("QueryResourceMonitor.Call(%s)", tok)
|
|
|
|
providerReq, err := parseProviderRequest(
|
|
tok.Package(), req.GetVersion(),
|
|
req.GetPluginDownloadURL(), req.GetPluginChecksums(), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prov, err := getProviderFromSource(rm.reg, rm.defaultProviders, providerReq, req.GetProvider(), tok)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
args, err := plugin.UnmarshalProperties(
|
|
req.GetArgs(), plugin.MarshalOptions{
|
|
Label: label,
|
|
KeepUnknowns: true,
|
|
KeepSecrets: true,
|
|
KeepResources: true,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal %v args: %w", tok, err)
|
|
}
|
|
|
|
argDependencies := map[resource.PropertyKey][]resource.URN{}
|
|
for name, deps := range req.GetArgDependencies() {
|
|
urns := make([]resource.URN, len(deps.Urns))
|
|
for i, urn := range deps.Urns {
|
|
urns[i] = resource.URN(urn)
|
|
}
|
|
argDependencies[resource.PropertyKey(name)] = urns
|
|
}
|
|
options := plugin.CallOptions{
|
|
ArgDependencies: argDependencies,
|
|
}
|
|
|
|
// Do the call and then return the arguments.
|
|
logging.V(5).Infof(
|
|
"QueryResourceMonitor.Call received: tok=%v #args=%v #info=%v #options=%v", tok, len(args), rm.callInfo, options)
|
|
ret, err := prov.Call(ctx, plugin.CallRequest{
|
|
Tok: tok,
|
|
Args: args,
|
|
Info: rm.callInfo,
|
|
Options: options,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("call of %v returned an error: %w", tok, err)
|
|
}
|
|
mret, err := plugin.MarshalProperties(ret.Return, plugin.MarshalOptions{
|
|
Label: label,
|
|
KeepUnknowns: true,
|
|
KeepSecrets: true,
|
|
KeepResources: true,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal return: %w", err)
|
|
}
|
|
|
|
returnDependencies := map[string]*pulumirpc.CallResponse_ReturnDependencies{}
|
|
for name, deps := range ret.ReturnDependencies {
|
|
urns := make([]string, len(deps))
|
|
for i, urn := range deps {
|
|
urns[i] = string(urn)
|
|
}
|
|
returnDependencies[string(name)] = &pulumirpc.CallResponse_ReturnDependencies{Urns: urns}
|
|
}
|
|
|
|
chkfails := slice.Prealloc[*pulumirpc.CheckFailure](len(ret.Failures))
|
|
for _, failure := range ret.Failures {
|
|
chkfails = append(chkfails, &pulumirpc.CheckFailure{
|
|
Property: string(failure.Property),
|
|
Reason: failure.Reason,
|
|
})
|
|
}
|
|
|
|
return &pulumirpc.CallResponse{Return: mret, ReturnDependencies: returnDependencies, Failures: chkfails}, nil
|
|
}
|
|
|
|
// ReadResource reads the current state associated with a resource from its provider plugin.
|
|
func (rm *queryResmon) ReadResource(ctx context.Context,
|
|
req *pulumirpc.ReadResourceRequest,
|
|
) (*pulumirpc.ReadResourceResponse, error) {
|
|
return nil, errors.New("Query mode does not support reading resources")
|
|
}
|
|
|
|
// RegisterResource is invoked by a language process when a new resource has been allocated.
|
|
func (rm *queryResmon) RegisterResource(ctx context.Context,
|
|
req *pulumirpc.RegisterResourceRequest,
|
|
) (*pulumirpc.RegisterResourceResponse, error) {
|
|
return nil, errors.New("Query mode does not support creating, updating, or deleting resources")
|
|
}
|
|
|
|
// RegisterResourceOutputs records some new output properties for a resource that have arrived after its initial
|
|
// provisioning. These will make their way into the eventual checkpoint state file for that resource.
|
|
func (rm *queryResmon) RegisterResourceOutputs(ctx context.Context,
|
|
req *pulumirpc.RegisterResourceOutputsRequest,
|
|
) (*emptypb.Empty, error) {
|
|
return nil, errors.New("Query mode does not support registering resource operations")
|
|
}
|
|
|
|
// SupportsFeature the query resmon is able to have secrets passed to it, which may be arguments to invoke calls.
|
|
func (rm *queryResmon) SupportsFeature(ctx context.Context,
|
|
req *pulumirpc.SupportsFeatureRequest,
|
|
) (*pulumirpc.SupportsFeatureResponse, error) {
|
|
hasSupport := false
|
|
return &pulumirpc.SupportsFeatureResponse{
|
|
HasSupport: hasSupport,
|
|
}, nil
|
|
}
|
|
|
|
// syntheticProviderURN will create a "fake" URN for a resource provider in query mode. Query mode
|
|
// has no stack, no project, and no parent, so there is otherwise no way to generate a principled
|
|
// URN.
|
|
func syntheticProviderURN(goal *resource.Goal) resource.URN {
|
|
return resource.NewURN(
|
|
"query-stack", "query-project", "parent-type", goal.Type, goal.Name)
|
|
}
|