2018-05-22 19:43:36 +00:00
|
|
|
// Copyright 2016-2018, 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.
|
2017-06-01 18:41:24 +00:00
|
|
|
|
Overhaul resources, planning, and environments
This change, part of pulumi/lumi#90, overhauls quite a bit of the
core resource, planning, environments, and related areas.
The biggest amount of movement comes from the splitting of pkg/resource
into multiple sub-packages. This results in:
- pkg/resource: just the core resource data structures.
- pkg/resource/deployment: all planning and deployment logic.
- pkg/resource/environment: all environment, configuration, and
serialized checkpoint structures and logic.
- pkg/resource/plugin: all dynamically loaded analyzer and
provider logic, including the actual loading and RPC mechanisms.
This also splits the resource abstraction up. We now have:
- resource.Resource: a shared interface.
- resource.Object: a resource that is connected to a live object
that will periodically observe mutations due to ongoing
evaluation of computations. Snapshots of its state may be
taken; however, this is purely a "pre-planning" abstraction.
- resource.State: a snapshot of a resource's state that is frozen.
In other words, it is no longer connected to a live object.
This is what will store provider outputs (ID and properties),
and is what may be serialized into a deployment record.
The branch is in a half-baked state as of this change; more changes
are to come...
2017-06-08 23:37:40 +00:00
|
|
|
package plugin
|
2017-06-01 18:41:24 +00:00
|
|
|
|
|
|
|
import (
|
2022-10-04 08:58:01 +00:00
|
|
|
"encoding/json"
|
2022-07-22 13:17:43 +00:00
|
|
|
"fmt"
|
2018-05-12 03:59:01 +00:00
|
|
|
"os"
|
2022-07-22 13:17:43 +00:00
|
|
|
"path/filepath"
|
2022-06-09 21:57:56 +00:00
|
|
|
"sync"
|
2018-05-12 03:59:01 +00:00
|
|
|
|
2018-02-06 17:57:32 +00:00
|
|
|
"github.com/blang/semver"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
|
|
"github.com/pkg/errors"
|
2017-06-01 18:41:24 +00:00
|
|
|
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
|
2022-12-14 12:21:30 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/env"
|
2021-03-17 13:20:05 +00:00
|
|
|
"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/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/workspace"
|
2017-06-01 18:41:24 +00:00
|
|
|
)
|
|
|
|
|
2017-06-10 01:34:37 +00:00
|
|
|
// A Host hosts provider plugins and makes them easily accessible by package name.
|
|
|
|
type Host interface {
|
2017-06-21 17:31:06 +00:00
|
|
|
// ServerAddr returns the address at which the host's RPC interface may be found.
|
|
|
|
ServerAddr() string
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
|
2018-04-10 19:03:11 +00:00
|
|
|
// Log logs a message, including errors and warnings. Messages can have a resource URN
|
|
|
|
// associated with them. If no urn is provided, the message is global.
|
2018-08-31 19:33:01 +00:00
|
|
|
Log(sev diag.Severity, urn resource.URN, msg string, streamID int32)
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
|
Implement status sinks
This commit reverts most of #1853 and replaces it with functionally
identical logic, using the notion of status message-specific sinks.
In other words, where the original commit implemented ephemeral status
messages by adding an `isStatus` parameter to most of the logging
methdos in pulumi/pulumi, this implements ephemeral status messages as a
parallel logging sink, which emits _only_ ephemeral status messages.
The original commit message in that PR was:
> Allow log events to be marked "status" events
>
> This commit will introduce a field, IsStatus to LogRequest. A "status"
> logging event will be displayed in the Info column of the main
> display, but will not be printed out at the end, when resource
> operations complete.
>
> For example, for complex resource initialization, we'd like to display
> a series of intermediate results: [1/4] Service object created, for
> example. We'd like these to appear in the Info column, but not at the
> end, where they are not helpful to the user.
2018-08-31 20:12:40 +00:00
|
|
|
// LogStatus logs a status message message, including errors and warnings. Status messages show
|
|
|
|
// up in the `Info` column of the progress display, but not in the final output. Messages can
|
|
|
|
// have a resource URN associated with them. If no urn is provided, the message is global.
|
|
|
|
LogStatus(sev diag.Severity, urn resource.URN, msg string, streamID int32)
|
|
|
|
|
2019-06-30 23:34:39 +00:00
|
|
|
// Analyzer fetches the analyzer with a given name, possibly lazily allocating the plugins for
|
|
|
|
// it. If an analyzer could not be found, or an error occurred while creating it, a non-nil
|
|
|
|
// error is returned.
|
2017-06-01 18:41:24 +00:00
|
|
|
Analyzer(nm tokens.QName) (Analyzer, error)
|
2019-06-30 23:34:39 +00:00
|
|
|
|
|
|
|
// PolicyAnalyzer boots the nodejs analyzer plugin located at a given path. This is useful
|
|
|
|
// because policy analyzers generally do not need to be "discovered" -- the engine is given a
|
|
|
|
// set of policies that are required to be run during an update, so they tend to be in a
|
|
|
|
// well-known place.
|
2019-12-16 22:51:02 +00:00
|
|
|
PolicyAnalyzer(name tokens.QName, path string, opts *PolicyAnalyzerOptions) (Analyzer, error)
|
2019-06-30 23:34:39 +00:00
|
|
|
|
|
|
|
// ListAnalyzers returns a list of all analyzer plugins known to the plugin host.
|
|
|
|
ListAnalyzers() []Analyzer
|
|
|
|
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
// Provider loads a new copy of the provider for a given package. If a provider for this package could not be
|
|
|
|
// found, or an error occurs while creating it, a non-nil error is returned.
|
2018-02-06 17:57:32 +00:00
|
|
|
Provider(pkg tokens.Package, version *semver.Version) (Provider, error)
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
// CloseProvider closes the given provider plugin and deregisters it from this host.
|
|
|
|
CloseProvider(provider Provider) error
|
2017-08-30 01:24:12 +00:00
|
|
|
// LanguageRuntime fetches the language runtime plugin for a given language, lazily allocating if necessary. If
|
|
|
|
// an implementation of this language runtime wasn't found, on an error occurs, a non-nil error is returned.
|
2024-01-25 23:28:58 +00:00
|
|
|
LanguageRuntime(runtime string, info ProgramInfo) (LanguageRuntime, error)
|
2017-06-01 18:41:24 +00:00
|
|
|
|
2018-03-12 23:27:39 +00:00
|
|
|
// EnsurePlugins ensures all plugins in the given array are loaded and ready to use. If any plugins are missing,
|
|
|
|
// and/or there are errors loading one or more plugins, a non-nil error is returned.
|
2022-08-26 14:51:14 +00:00
|
|
|
EnsurePlugins(plugins []workspace.PluginSpec, kinds Flags) error
|
2022-10-11 10:56:29 +00:00
|
|
|
|
2022-06-14 06:27:11 +00:00
|
|
|
// ResolvePlugin resolves a plugin kind, name, and optional semver to a candidate plugin to load.
|
|
|
|
ResolvePlugin(kind workspace.PluginKind, name string, version *semver.Version) (*workspace.PluginInfo, error)
|
2017-12-01 21:50:32 +00:00
|
|
|
|
2022-08-18 14:31:10 +00:00
|
|
|
GetProjectPlugins() []workspace.ProjectPlugin
|
2022-07-22 13:17:43 +00:00
|
|
|
|
2018-07-12 04:20:26 +00:00
|
|
|
// SignalCancellation asks all resource providers to gracefully shut down and abort any ongoing
|
|
|
|
// operations. Operation aborted in this way will return an error (e.g., `Update` and `Create`
|
|
|
|
// will either a creation error or an initialization error. SignalCancellation is advisory and
|
|
|
|
// non-blocking; it is up to the host to decide how long to wait after SignalCancellation is
|
|
|
|
// called before (e.g.) hard-closing any gRPC connection.
|
|
|
|
SignalCancellation() error
|
|
|
|
|
2017-06-01 18:41:24 +00:00
|
|
|
// Close reclaims any resources associated with the host.
|
|
|
|
Close() error
|
|
|
|
}
|
|
|
|
|
2017-06-10 01:34:37 +00:00
|
|
|
// NewDefaultHost implements the standard plugin logic, using the standard installation root to find them.
|
2022-07-25 11:34:49 +00:00
|
|
|
func NewDefaultHost(ctx *Context, runtimeOptions map[string]interface{},
|
2023-03-31 10:22:50 +00:00
|
|
|
disableProviderPreview bool, plugins *workspace.Plugins, config map[config.Key]string,
|
2023-03-03 16:36:39 +00:00
|
|
|
) (Host, error) {
|
2022-07-22 13:17:43 +00:00
|
|
|
// Create plugin info from providers
|
2022-08-18 14:31:10 +00:00
|
|
|
projectPlugins := make([]workspace.ProjectPlugin, 0)
|
2022-07-22 13:17:43 +00:00
|
|
|
if plugins != nil {
|
|
|
|
for _, providerOpts := range plugins.Providers {
|
2024-02-22 11:43:18 +00:00
|
|
|
info, err := parsePluginOpts(ctx.Root, providerOpts, workspace.ResourcePlugin)
|
2022-07-22 13:17:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
projectPlugins = append(projectPlugins, info)
|
|
|
|
}
|
|
|
|
for _, languageOpts := range plugins.Languages {
|
2024-02-22 11:43:18 +00:00
|
|
|
info, err := parsePluginOpts(ctx.Root, languageOpts, workspace.LanguagePlugin)
|
2022-07-22 13:17:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
projectPlugins = append(projectPlugins, info)
|
|
|
|
}
|
|
|
|
for _, analyzerOpts := range plugins.Analyzers {
|
2024-02-22 11:43:18 +00:00
|
|
|
info, err := parsePluginOpts(ctx.Root, analyzerOpts, workspace.AnalyzerPlugin)
|
2022-07-22 13:17:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
projectPlugins = append(projectPlugins, info)
|
|
|
|
}
|
|
|
|
}
|
2020-10-09 20:13:55 +00:00
|
|
|
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
host := &defaultHost{
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
ctx: ctx,
|
|
|
|
runtimeOptions: runtimeOptions,
|
|
|
|
analyzerPlugins: make(map[tokens.QName]*analyzerPlugin),
|
|
|
|
languagePlugins: make(map[string]*languagePlugin),
|
|
|
|
resourcePlugins: make(map[Provider]*resourcePlugin),
|
|
|
|
reportedResourcePlugins: make(map[string]struct{}),
|
2022-05-31 15:57:28 +00:00
|
|
|
languageLoadRequests: make(chan pluginLoadRequest),
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
loadRequests: make(chan pluginLoadRequest),
|
2020-10-09 20:13:55 +00:00
|
|
|
disableProviderPreview: disableProviderPreview,
|
2023-03-31 10:22:50 +00:00
|
|
|
config: config,
|
2022-06-09 21:57:56 +00:00
|
|
|
closer: new(sync.Once),
|
2022-07-22 13:17:43 +00:00
|
|
|
projectPlugins: projectPlugins,
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
|
2017-06-21 17:31:06 +00:00
|
|
|
// Fire up a gRPC server to listen for requests. This acts as a RPC interface that plugins can use
|
|
|
|
// to "phone home" in case there are things the host must do on behalf of the plugins (like log, etc).
|
|
|
|
svr, err := newHostServer(host, ctx)
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-06-21 17:31:06 +00:00
|
|
|
host.server = svr
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
// Start a goroutine we'll use to satisfy load requests serially and avoid race conditions.
|
|
|
|
go func() {
|
|
|
|
for req := range host.loadRequests {
|
|
|
|
req.result <- req.load()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2022-05-31 15:57:28 +00:00
|
|
|
// Start another goroutine we'll use to satisfy load language plugin requests, this is so other plugins
|
|
|
|
// can be started up by a language plugin.
|
|
|
|
go func() {
|
|
|
|
for req := range host.languageLoadRequests {
|
|
|
|
req.result <- req.load()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
return host, nil
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
|
|
|
|
2024-02-22 11:43:18 +00:00
|
|
|
func parsePluginOpts(
|
|
|
|
root string, providerOpts workspace.PluginOptions, k workspace.PluginKind,
|
|
|
|
) (workspace.ProjectPlugin, error) {
|
2023-01-13 20:47:58 +00:00
|
|
|
handleErr := func(msg string, a ...interface{}) (workspace.ProjectPlugin, error) {
|
|
|
|
return workspace.ProjectPlugin{},
|
|
|
|
fmt.Errorf("parsing plugin options for '%s': %w", providerOpts.Name, fmt.Errorf(msg, a...))
|
|
|
|
}
|
|
|
|
if providerOpts.Name == "" {
|
|
|
|
return handleErr("name must not be empty")
|
|
|
|
}
|
2022-07-22 13:17:43 +00:00
|
|
|
var v *semver.Version
|
|
|
|
if providerOpts.Version != "" {
|
|
|
|
ver, err := semver.Parse(providerOpts.Version)
|
|
|
|
if err != nil {
|
2022-08-18 14:31:10 +00:00
|
|
|
return workspace.ProjectPlugin{}, err
|
2022-07-22 13:17:43 +00:00
|
|
|
}
|
|
|
|
v = &ver
|
|
|
|
}
|
|
|
|
|
2023-01-13 20:47:58 +00:00
|
|
|
stat, err := os.Stat(providerOpts.Path)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return handleErr("no folder at path '%s'", providerOpts.Path)
|
|
|
|
} else if err != nil {
|
|
|
|
return handleErr("checking provider folder: %w", err)
|
|
|
|
} else if !stat.IsDir() {
|
|
|
|
return handleErr("provider folder '%s' is not a directory", providerOpts.Path)
|
2022-07-22 13:17:43 +00:00
|
|
|
}
|
|
|
|
|
2024-02-22 11:43:18 +00:00
|
|
|
// The path is relative to the project root. Make it absolute here so we don't need to track that everywhere its used.
|
|
|
|
path := providerOpts.Path
|
|
|
|
if !filepath.IsAbs(path) {
|
|
|
|
path, err = filepath.Abs(filepath.Join(root, path))
|
|
|
|
if err != nil {
|
|
|
|
return handleErr("getting absolute path for plugin path %s: %w", providerOpts.Path, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-18 14:31:10 +00:00
|
|
|
pluginInfo := workspace.ProjectPlugin{
|
2022-07-22 13:17:43 +00:00
|
|
|
Name: providerOpts.Name,
|
2024-02-22 11:43:18 +00:00
|
|
|
Path: path,
|
2022-07-22 13:17:43 +00:00
|
|
|
Kind: k,
|
|
|
|
Version: v,
|
|
|
|
}
|
|
|
|
return pluginInfo, nil
|
|
|
|
}
|
|
|
|
|
2019-12-16 22:51:02 +00:00
|
|
|
// PolicyAnalyzerOptions includes a bag of options to pass along to a policy analyzer.
|
|
|
|
type PolicyAnalyzerOptions struct {
|
2022-09-02 09:47:38 +00:00
|
|
|
Organization string
|
|
|
|
Project string
|
|
|
|
Stack string
|
|
|
|
Config map[config.Key]string
|
|
|
|
DryRun bool
|
2019-12-16 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
type pluginLoadRequest struct {
|
|
|
|
load func() error
|
|
|
|
result chan<- error
|
|
|
|
}
|
|
|
|
|
2017-06-10 01:34:37 +00:00
|
|
|
type defaultHost struct {
|
2022-10-04 08:58:01 +00:00
|
|
|
ctx *Context // the shared context for this host.
|
|
|
|
|
|
|
|
// the runtime options for the project, passed to resource providers to support dynamic providers.
|
|
|
|
runtimeOptions map[string]interface{}
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
analyzerPlugins map[tokens.QName]*analyzerPlugin // a cache of analyzer plugins and their processes.
|
|
|
|
languagePlugins map[string]*languagePlugin // a cache of language plugins and their processes.
|
|
|
|
resourcePlugins map[Provider]*resourcePlugin // the set of loaded resource plugins.
|
|
|
|
reportedResourcePlugins map[string]struct{} // the set of unique resource plugins we'll report.
|
2022-05-31 15:57:28 +00:00
|
|
|
languageLoadRequests chan pluginLoadRequest // a channel used to satisfy language load requests.
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
loadRequests chan pluginLoadRequest // a channel used to satisfy plugin load requests.
|
|
|
|
server *hostServer // the server's RPC machinery.
|
2020-10-09 20:13:55 +00:00
|
|
|
disableProviderPreview bool // true if provider plugins should disable provider preview
|
2023-03-31 10:22:50 +00:00
|
|
|
config map[config.Key]string // the configuration map for the stack, if any.
|
2022-06-09 21:57:56 +00:00
|
|
|
|
2023-08-09 08:28:02 +00:00
|
|
|
// Used to synchronize shutdown with in-progress plugin loads.
|
|
|
|
pluginLock sync.RWMutex
|
|
|
|
|
2022-07-22 13:17:43 +00:00
|
|
|
closer *sync.Once
|
2022-08-18 14:31:10 +00:00
|
|
|
projectPlugins []workspace.ProjectPlugin
|
2018-02-06 17:57:32 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 04:20:26 +00:00
|
|
|
var _ Host = (*defaultHost)(nil)
|
|
|
|
|
2018-02-06 17:57:32 +00:00
|
|
|
type analyzerPlugin struct {
|
|
|
|
Plugin Analyzer
|
|
|
|
Info workspace.PluginInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
type languagePlugin struct {
|
|
|
|
Plugin LanguageRuntime
|
|
|
|
Info workspace.PluginInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
type resourcePlugin struct {
|
|
|
|
Plugin Provider
|
|
|
|
Info workspace.PluginInfo
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
}
|
|
|
|
|
2017-06-21 17:31:06 +00:00
|
|
|
func (host *defaultHost) ServerAddr() string {
|
|
|
|
return host.server.Address()
|
|
|
|
}
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
|
2018-08-31 19:33:01 +00:00
|
|
|
func (host *defaultHost) Log(sev diag.Severity, urn resource.URN, msg string, streamID int32) {
|
|
|
|
host.ctx.Diag.Logf(sev, diag.StreamMessage(urn, msg, streamID))
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
}
|
|
|
|
|
Implement status sinks
This commit reverts most of #1853 and replaces it with functionally
identical logic, using the notion of status message-specific sinks.
In other words, where the original commit implemented ephemeral status
messages by adding an `isStatus` parameter to most of the logging
methdos in pulumi/pulumi, this implements ephemeral status messages as a
parallel logging sink, which emits _only_ ephemeral status messages.
The original commit message in that PR was:
> Allow log events to be marked "status" events
>
> This commit will introduce a field, IsStatus to LogRequest. A "status"
> logging event will be displayed in the Info column of the main
> display, but will not be printed out at the end, when resource
> operations complete.
>
> For example, for complex resource initialization, we'd like to display
> a series of intermediate results: [1/4] Service object created, for
> example. We'd like these to appear in the Info column, but not at the
> end, where they are not helpful to the user.
2018-08-31 20:12:40 +00:00
|
|
|
func (host *defaultHost) LogStatus(sev diag.Severity, urn resource.URN, msg string, streamID int32) {
|
|
|
|
host.ctx.StatusDiag.Logf(sev, diag.StreamMessage(urn, msg, streamID))
|
|
|
|
}
|
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
// loadPlugin sends an appropriate load request to the plugin loader and returns the loaded plugin (if any) and error.
|
2023-08-09 08:28:02 +00:00
|
|
|
func (host *defaultHost) loadPlugin(
|
|
|
|
loadRequestChannel chan pluginLoadRequest, load func() (interface{}, error),
|
|
|
|
) (interface{}, error) {
|
2018-03-09 19:02:23 +00:00
|
|
|
var plugin interface{}
|
|
|
|
|
2023-08-09 08:28:02 +00:00
|
|
|
locked := host.pluginLock.TryRLock()
|
|
|
|
if !locked {
|
|
|
|
// If we couldn't get a read lock that must be because we're shutting down, so just return an error.
|
|
|
|
return nil, errors.New("plugin host is shutting down")
|
|
|
|
}
|
|
|
|
defer host.pluginLock.RUnlock()
|
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
result := make(chan error)
|
2022-05-31 15:57:28 +00:00
|
|
|
loadRequestChannel <- pluginLoadRequest{
|
2018-03-09 19:02:23 +00:00
|
|
|
load: func() error {
|
|
|
|
p, err := load()
|
|
|
|
plugin = p
|
|
|
|
return err
|
|
|
|
},
|
|
|
|
result: result,
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
2018-03-09 19:02:23 +00:00
|
|
|
return plugin, <-result
|
|
|
|
}
|
2017-06-01 18:41:24 +00:00
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
func (host *defaultHost) Analyzer(name tokens.QName) (Analyzer, error) {
|
2023-08-09 08:28:02 +00:00
|
|
|
plugin, err := host.loadPlugin(host.loadRequests, func() (interface{}, error) {
|
2018-03-09 19:02:23 +00:00
|
|
|
// First see if we already loaded this plugin.
|
|
|
|
if plug, has := host.analyzerPlugins[name]; has {
|
2023-02-15 01:06:56 +00:00
|
|
|
contract.Assertf(plug != nil, "analyzer plugin %v was loaded but is nil", name)
|
2018-03-09 19:02:23 +00:00
|
|
|
return plug.Plugin, nil
|
2017-12-01 21:50:32 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
// If not, try to load and bind to a plugin.
|
|
|
|
plug, err := NewAnalyzer(host, host.ctx, name)
|
|
|
|
if err == nil && plug != nil {
|
|
|
|
info, infoerr := plug.GetPluginInfo()
|
|
|
|
if infoerr != nil {
|
|
|
|
return nil, infoerr
|
|
|
|
}
|
2017-12-01 21:50:32 +00:00
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
// Memoize the result.
|
|
|
|
host.analyzerPlugins[name] = &analyzerPlugin{Plugin: plug, Info: info}
|
|
|
|
}
|
|
|
|
|
|
|
|
return plug, err
|
|
|
|
})
|
|
|
|
if plugin == nil || err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return plugin.(Analyzer), nil
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
|
|
|
|
2019-12-16 22:51:02 +00:00
|
|
|
func (host *defaultHost) PolicyAnalyzer(name tokens.QName, path string, opts *PolicyAnalyzerOptions) (Analyzer, error) {
|
2023-08-09 08:28:02 +00:00
|
|
|
plugin, err := host.loadPlugin(host.loadRequests, func() (interface{}, error) {
|
2019-06-30 23:34:39 +00:00
|
|
|
// First see if we already loaded this plugin.
|
|
|
|
if plug, has := host.analyzerPlugins[name]; has {
|
2023-02-15 01:06:56 +00:00
|
|
|
contract.Assertf(plug != nil, "analyzer plugin %v was loaded but is nil", name)
|
2019-06-30 23:34:39 +00:00
|
|
|
return plug.Plugin, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not, try to load and bind to a plugin.
|
2019-12-16 22:51:02 +00:00
|
|
|
plug, err := NewPolicyAnalyzer(host, host.ctx, name, path, opts)
|
2019-06-30 23:34:39 +00:00
|
|
|
if err == nil && plug != nil {
|
|
|
|
info, infoerr := plug.GetPluginInfo()
|
|
|
|
if infoerr != nil {
|
|
|
|
return nil, infoerr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Memoize the result.
|
|
|
|
host.analyzerPlugins[name] = &analyzerPlugin{Plugin: plug, Info: info}
|
|
|
|
}
|
|
|
|
|
|
|
|
return plug, err
|
|
|
|
})
|
|
|
|
if plugin == nil || err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return plugin.(Analyzer), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (host *defaultHost) ListAnalyzers() []Analyzer {
|
|
|
|
analyzers := []Analyzer{}
|
|
|
|
for _, analyzer := range host.analyzerPlugins {
|
|
|
|
analyzers = append(analyzers, analyzer.Plugin)
|
|
|
|
}
|
|
|
|
return analyzers
|
|
|
|
}
|
|
|
|
|
2018-02-06 17:57:32 +00:00
|
|
|
func (host *defaultHost) Provider(pkg tokens.Package, version *semver.Version) (Provider, error) {
|
2023-08-09 08:28:02 +00:00
|
|
|
plugin, err := host.loadPlugin(host.loadRequests, func() (interface{}, error) {
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
// Try to load and bind to a plugin.
|
2023-03-31 10:22:50 +00:00
|
|
|
|
|
|
|
result := make(map[string]string)
|
|
|
|
for k, v := range host.config {
|
|
|
|
if tokens.Package(k.Namespace()) != pkg {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
result[k.Name()] = v
|
|
|
|
}
|
|
|
|
jsonConfig, err := json.Marshal(result)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Could not marshal config to JSON: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
plug, err := NewProvider(
|
|
|
|
host, host.ctx, pkg, version,
|
|
|
|
host.runtimeOptions, host.disableProviderPreview, string(jsonConfig))
|
2018-03-09 19:02:23 +00:00
|
|
|
if err == nil && plug != nil {
|
|
|
|
info, infoerr := plug.GetPluginInfo()
|
|
|
|
if infoerr != nil {
|
|
|
|
return nil, infoerr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Warn if the plugin version was not what we expected
|
2022-12-14 12:21:30 +00:00
|
|
|
if version != nil && !env.Dev.Value() {
|
2018-04-05 23:37:50 +00:00
|
|
|
if info.Version == nil || !info.Version.GTE(*version) {
|
2018-03-09 19:02:23 +00:00
|
|
|
var v string
|
|
|
|
if info.Version != nil {
|
|
|
|
v = info.Version.String()
|
|
|
|
}
|
|
|
|
host.ctx.Diag.Warningf(
|
2018-04-10 19:03:11 +00:00
|
|
|
diag.Message("", /*urn*/
|
2018-04-05 23:37:50 +00:00
|
|
|
"resource plugin %s is expected to have version >=%s, but has %s; "+
|
|
|
|
"the wrong version may be on your path, or this may be a bug in the plugin"),
|
2018-03-09 19:02:23 +00:00
|
|
|
info.Name, version.String(), v)
|
2018-02-06 17:57:32 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-01 21:50:32 +00:00
|
|
|
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
// Record the result and add the plugin's info to our list of loaded plugins if it's the first copy of its
|
|
|
|
// kind.
|
|
|
|
key := info.Name
|
|
|
|
if info.Version != nil {
|
|
|
|
key += info.Version.String()
|
2018-03-07 00:09:42 +00:00
|
|
|
}
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
_, alreadyReported := host.reportedResourcePlugins[key]
|
|
|
|
if !alreadyReported {
|
|
|
|
host.reportedResourcePlugins[key] = struct{}{}
|
2018-03-09 19:02:23 +00:00
|
|
|
}
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
host.resourcePlugins[plug] = &resourcePlugin{Plugin: plug, Info: info}
|
2018-03-07 00:09:42 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
return plug, err
|
|
|
|
})
|
|
|
|
if plugin == nil || err != nil {
|
|
|
|
return nil, err
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
2018-03-09 19:02:23 +00:00
|
|
|
return plugin.(Provider), nil
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
|
|
|
|
2024-01-25 23:28:58 +00:00
|
|
|
func (host *defaultHost) LanguageRuntime(runtime string, info ProgramInfo,
|
2023-03-03 16:36:39 +00:00
|
|
|
) (LanguageRuntime, error) {
|
2022-05-31 15:57:28 +00:00
|
|
|
// Language runtimes use their own loading channel not the main one
|
2023-08-09 08:28:02 +00:00
|
|
|
plugin, err := host.loadPlugin(host.languageLoadRequests, func() (interface{}, error) {
|
2022-10-04 08:58:01 +00:00
|
|
|
// Key our cached runtime plugins by the runtime name and the options
|
2024-01-25 23:28:58 +00:00
|
|
|
jsonOptions, err := json.Marshal(info.Options())
|
2022-10-04 08:58:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not marshal runtime options to JSON: %w", err)
|
|
|
|
}
|
|
|
|
|
2024-01-25 23:28:58 +00:00
|
|
|
key := runtime + ":" + info.RootDirectory() + ":" + info.ProgramDirectory() + ":" + string(jsonOptions)
|
2022-10-04 08:58:01 +00:00
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
// First see if we already loaded this plugin.
|
2022-10-04 08:58:01 +00:00
|
|
|
if plug, has := host.languagePlugins[key]; has {
|
2023-02-15 01:06:56 +00:00
|
|
|
contract.Assertf(plug != nil, "language plugin %v was loaded but is nil", key)
|
2018-03-09 19:02:23 +00:00
|
|
|
return plug.Plugin, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not, allocate a new one.
|
2024-01-25 23:28:58 +00:00
|
|
|
plug, err := NewLanguageRuntime(host, host.ctx, runtime, host.ctx.Pwd, info)
|
2018-03-09 19:02:23 +00:00
|
|
|
if err == nil && plug != nil {
|
|
|
|
info, infoerr := plug.GetPluginInfo()
|
|
|
|
if infoerr != nil {
|
|
|
|
return nil, infoerr
|
|
|
|
}
|
2017-12-01 21:50:32 +00:00
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
// Memoize the result.
|
2022-10-04 08:58:01 +00:00
|
|
|
host.languagePlugins[key] = &languagePlugin{Plugin: plug, Info: info}
|
2018-02-06 17:57:32 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 19:02:23 +00:00
|
|
|
return plug, err
|
|
|
|
})
|
|
|
|
if plugin == nil || err != nil {
|
|
|
|
return nil, err
|
2017-12-01 21:50:32 +00:00
|
|
|
}
|
2018-03-09 19:02:23 +00:00
|
|
|
return plugin.(LanguageRuntime), nil
|
2017-12-01 21:50:32 +00:00
|
|
|
}
|
|
|
|
|
2018-03-12 23:27:39 +00:00
|
|
|
// EnsurePlugins ensures all plugins in the given array are loaded and ready to use. If any plugins are missing,
|
|
|
|
// and/or there are errors loading one or more plugins, a non-nil error is returned.
|
2022-08-26 14:51:14 +00:00
|
|
|
func (host *defaultHost) EnsurePlugins(plugins []workspace.PluginSpec, kinds Flags) error {
|
2018-02-06 17:57:32 +00:00
|
|
|
// Use a multieerror to track failures so we can return one big list of all failures at the end.
|
|
|
|
var result error
|
|
|
|
for _, plugin := range plugins {
|
|
|
|
switch plugin.Kind {
|
|
|
|
case workspace.AnalyzerPlugin:
|
2018-05-15 03:32:53 +00:00
|
|
|
if kinds&AnalyzerPlugins != 0 {
|
|
|
|
if _, err := host.Analyzer(tokens.QName(plugin.Name)); err != nil {
|
|
|
|
result = multierror.Append(result,
|
|
|
|
errors.Wrapf(err, "failed to load analyzer plugin %s", plugin.Name))
|
|
|
|
}
|
2018-02-06 17:57:32 +00:00
|
|
|
}
|
|
|
|
case workspace.LanguagePlugin:
|
2018-05-15 03:32:53 +00:00
|
|
|
if kinds&LanguagePlugins != 0 {
|
2022-10-04 08:58:01 +00:00
|
|
|
// Pass nil options here, we just need to check the language plugin is loadable. We can't use
|
|
|
|
// host.runtimePlugins because there might be other language plugins reported here (e.g
|
|
|
|
// shimless multi-language providers). Pass the host root for the plugin directory, it
|
|
|
|
// shouldn't matter because we're starting with no options but it's a directory we've already
|
|
|
|
// got hold of.
|
2024-01-25 23:28:58 +00:00
|
|
|
info := NewProgramInfo(host.ctx.Root, host.ctx.Pwd, ".", nil)
|
|
|
|
if _, err := host.LanguageRuntime(plugin.Name, info); err != nil {
|
2018-05-15 03:32:53 +00:00
|
|
|
result = multierror.Append(result,
|
|
|
|
errors.Wrapf(err, "failed to load language plugin %s", plugin.Name))
|
|
|
|
}
|
2018-02-06 17:57:32 +00:00
|
|
|
}
|
|
|
|
case workspace.ResourcePlugin:
|
2018-05-15 03:32:53 +00:00
|
|
|
if kinds&ResourcePlugins != 0 {
|
|
|
|
if _, err := host.Provider(tokens.Package(plugin.Name), plugin.Version); err != nil {
|
|
|
|
result = multierror.Append(result,
|
|
|
|
errors.Wrapf(err, "failed to load resource plugin %s", plugin.Name))
|
|
|
|
}
|
2018-02-06 17:57:32 +00:00
|
|
|
}
|
2024-02-05 08:35:48 +00:00
|
|
|
case workspace.ConverterPlugin, workspace.ToolPlugin:
|
|
|
|
contract.Failf("unexpected plugin kind: %s", plugin.Kind)
|
2018-02-06 17:57:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2022-06-14 06:27:11 +00:00
|
|
|
func (host *defaultHost) ResolvePlugin(
|
2023-03-03 16:36:39 +00:00
|
|
|
kind workspace.PluginKind, name string, version *semver.Version,
|
|
|
|
) (*workspace.PluginInfo, error) {
|
2023-08-07 12:15:57 +00:00
|
|
|
return workspace.GetPluginInfo(host.ctx.Diag, kind, name, version, host.GetProjectPlugins())
|
2022-07-22 13:17:43 +00:00
|
|
|
}
|
|
|
|
|
2022-08-18 14:31:10 +00:00
|
|
|
func (host *defaultHost) GetProjectPlugins() []workspace.ProjectPlugin {
|
2022-07-22 13:17:43 +00:00
|
|
|
return host.projectPlugins
|
2022-06-14 06:27:11 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 04:20:26 +00:00
|
|
|
func (host *defaultHost) SignalCancellation() error {
|
2018-09-10 22:18:25 +00:00
|
|
|
// NOTE: we're abusing loadPlugin in order to ensure proper synchronization.
|
2023-08-09 08:28:02 +00:00
|
|
|
_, err := host.loadPlugin(host.loadRequests, func() (interface{}, error) {
|
2018-09-10 22:18:25 +00:00
|
|
|
var result error
|
|
|
|
for _, plug := range host.resourcePlugins {
|
|
|
|
if err := plug.Plugin.SignalCancellation(); err != nil {
|
|
|
|
result = multierror.Append(result, errors.Wrapf(err,
|
|
|
|
"Error signaling cancellation to resource provider '%s'", plug.Info.Name))
|
|
|
|
}
|
2018-07-12 04:20:26 +00:00
|
|
|
}
|
2018-09-10 22:18:25 +00:00
|
|
|
return nil, result
|
|
|
|
})
|
|
|
|
return err
|
2018-07-12 04:20:26 +00:00
|
|
|
}
|
|
|
|
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
func (host *defaultHost) CloseProvider(provider Provider) error {
|
|
|
|
// NOTE: we're abusing loadPlugin in order to ensure proper synchronization.
|
2023-08-09 08:28:02 +00:00
|
|
|
_, err := host.loadPlugin(host.loadRequests, func() (interface{}, error) {
|
Implement first-class providers. (#1695)
### First-Class Providers
These changes implement support for first-class providers. First-class
providers are provider plugins that are exposed as resources via the
Pulumi programming model so that they may be explicitly and multiply
instantiated. Each instance of a provider resource may be configured
differently, and configuration parameters may be source from the
outputs of other resources.
### Provider Plugin Changes
In order to accommodate the need to verify and diff provider
configuration and configure providers without complete configuration
information, these changes adjust the high-level provider plugin
interface. Two new methods for validating a provider's configuration
and diffing changes to the same have been added (`CheckConfig` and
`DiffConfig`, respectively), and the type of the configuration bag
accepted by `Configure` has been changed to a `PropertyMap`.
These changes have not yet been reflected in the provider plugin gRPC
interface. We will do this in a set of follow-up changes. Until then,
these methods are implemented by adapters:
- `CheckConfig` validates that all configuration parameters are string
or unknown properties. This is necessary because existing plugins
only accept string-typed configuration values.
- `DiffConfig` either returns "never replace" if all configuration
values are known or "must replace" if any configuration value is
unknown. The justification for this behavior is given
[here](https://github.com/pulumi/pulumi/pull/1695/files#diff-a6cd5c7f337665f5bb22e92ca5f07537R106)
- `Configure` converts the config bag to a legacy config map and
configures the provider plugin if all config values are known. If any
config value is unknown, the underlying plugin is not configured and
the provider may only perform `Check`, `Read`, and `Invoke`, all of
which return empty results. We justify this behavior becuase it is
only possible during a preview and provides the best experience we
can manage with the existing gRPC interface.
### Resource Model Changes
Providers are now exposed as resources that participate in a stack's
dependency graph. Like other resources, they are explicitly created,
may have multiple instances, and may have dependencies on other
resources. Providers are referred to using provider references, which
are a combination of the provider's URN and its ID. This design
addresses the need during a preview to refer to providers that have not
yet been physically created and therefore have no ID.
All custom resources that are not themselves providers must specify a
single provider via a provider reference. The named provider will be
used to manage that resource's CRUD operations. If a resource's
provider reference changes, the resource must be replaced. Though its
URN is not present in the resource's dependency list, the provider
should be treated as a dependency of the resource when topologically
sorting the dependency graph.
Finally, `Invoke` operations must now specify a provider to use for the
invocation via a provider reference.
### Engine Changes
First-class providers support requires a few changes to the engine:
- The engine must have some way to map from provider references to
provider plugins. It must be possible to add providers from a stack's
checkpoint to this map and to register new/updated providers during
the execution of a plan in response to CRUD operations on provider
resources.
- In order to support updating existing stacks using existing Pulumi
programs that may not explicitly instantiate providers, the engine
must be able to manage the "default" providers for each package
referenced by a checkpoint or Pulumi program. The configuration for
a "default" provider is taken from the stack's configuration data.
The former need is addressed by adding a provider registry type that is
responsible for managing all of the plugins required by a plan. In
addition to loading plugins froma checkpoint and providing the ability
to map from a provider reference to a provider plugin, this type serves
as the provider plugin for providers themselves (i.e. it is the
"provider provider").
The latter need is solved via two relatively self-contained changes to
plan setup and the eval source.
During plan setup, the old checkpoint is scanned for custom resources
that do not have a provider reference in order to compute the set of
packages that require a default provider. Once this set has been
computed, the required default provider definitions are conjured and
prepended to the checkpoint's resource list. Each resource that
requires a default provider is then updated to refer to the default
provider for its package.
While an eval source is running, each custom resource registration,
resource read, and invoke that does not name a provider is trapped
before being returned by the source iterator. If no default provider
for the appropriate package has been registered, the eval source
synthesizes an appropriate registration, waits for it to complete, and
records the registered provider's reference. This reference is injected
into the original request, which is then processed as usual. If a
default provider was already registered, the recorded reference is
used and no new registration occurs.
### SDK Changes
These changes only expose first-class providers from the Node.JS SDK.
- A new abstract class, `ProviderResource`, can be subclassed and used
to instantiate first-class providers.
- A new field in `ResourceOptions`, `provider`, can be used to supply
a particular provider instance to manage a `CustomResource`'s CRUD
operations.
- A new type, `InvokeOptions`, can be used to specify options that
control the behavior of a call to `pulumi.runtime.invoke`. This type
includes a `provider` field that is analogous to
`ResourceOptions.provider`.
2018-08-07 00:50:29 +00:00
|
|
|
if err := provider.Close(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
delete(host.resourcePlugins, provider)
|
|
|
|
return nil, nil
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-06-09 21:57:56 +00:00
|
|
|
func (host *defaultHost) Close() (err error) {
|
|
|
|
host.closer.Do(func() {
|
2023-08-09 08:28:02 +00:00
|
|
|
// Wait for all plugins to finish loading, we do this by taking a Write lock on the pluginLock. This
|
|
|
|
// won't take until all read locks are released (indicating that no plugins are currently loading) and
|
|
|
|
// it will then block further read locks from being taken (preventing any new plugins from loading).
|
|
|
|
host.pluginLock.Lock()
|
|
|
|
// N.B We purposefully do not unlock this.
|
|
|
|
|
2022-06-09 21:57:56 +00:00
|
|
|
// Close all plugins.
|
|
|
|
for _, plug := range host.analyzerPlugins {
|
|
|
|
if err := plug.Plugin.Close(); err != nil {
|
|
|
|
logging.V(5).Infof("Error closing '%s' analyzer plugin during shutdown; ignoring: %v", plug.Info.Name, err)
|
|
|
|
}
|
2018-02-06 17:57:32 +00:00
|
|
|
}
|
2022-06-09 21:57:56 +00:00
|
|
|
for _, plug := range host.resourcePlugins {
|
|
|
|
if err := plug.Plugin.Close(); err != nil {
|
|
|
|
logging.V(5).Infof("Error closing '%s' resource plugin during shutdown; ignoring: %v", plug.Info.Name, err)
|
|
|
|
}
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
2022-06-09 21:57:56 +00:00
|
|
|
for _, plug := range host.languagePlugins {
|
|
|
|
if err := plug.Plugin.Close(); err != nil {
|
|
|
|
logging.V(5).Infof("Error closing '%s' language plugin during shutdown; ignoring: %v", plug.Info.Name, err)
|
|
|
|
}
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
|
2022-06-09 21:57:56 +00:00
|
|
|
// Empty out all maps.
|
|
|
|
host.analyzerPlugins = make(map[tokens.QName]*analyzerPlugin)
|
|
|
|
host.languagePlugins = make(map[string]*languagePlugin)
|
|
|
|
host.resourcePlugins = make(map[Provider]*resourcePlugin)
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 02:45:07 +00:00
|
|
|
|
2022-06-09 21:57:56 +00:00
|
|
|
// Shut down the plugin loader.
|
|
|
|
close(host.languageLoadRequests)
|
|
|
|
close(host.loadRequests)
|
2018-03-09 19:02:23 +00:00
|
|
|
|
2022-06-09 21:57:56 +00:00
|
|
|
// Finally, shut down the host's gRPC server.
|
|
|
|
err = host.server.Cancel()
|
|
|
|
})
|
|
|
|
return err
|
2017-06-01 18:41:24 +00:00
|
|
|
}
|
2018-05-15 03:32:53 +00:00
|
|
|
|
|
|
|
// Flags can be used to filter out plugins during loading that aren't necessary.
|
|
|
|
type Flags int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// AnalyzerPlugins is used to only load analyzers.
|
|
|
|
AnalyzerPlugins Flags = 1 << iota
|
|
|
|
// LanguagePlugins is used to only load language plugins.
|
|
|
|
LanguagePlugins
|
|
|
|
// ResourcePlugins is used to only load resource provider plugins.
|
|
|
|
ResourcePlugins
|
|
|
|
)
|
|
|
|
|
|
|
|
// AllPlugins uses flags to ensure that all plugin kinds are loaded.
|
|
|
|
var AllPlugins = AnalyzerPlugins | LanguagePlugins | ResourcePlugins
|
2020-09-15 00:40:17 +00:00
|
|
|
|
|
|
|
// GetRequiredPlugins lists a full set of plugins that will be required by the given program.
|
2024-01-25 23:28:58 +00:00
|
|
|
func GetRequiredPlugins(
|
|
|
|
host Host,
|
|
|
|
runtime string,
|
|
|
|
project string,
|
|
|
|
info ProgramInfo,
|
|
|
|
kinds Flags) (
|
|
|
|
[]workspace.PluginSpec, error,
|
|
|
|
) {
|
2022-08-26 14:51:14 +00:00
|
|
|
var plugins []workspace.PluginSpec
|
2020-09-15 00:40:17 +00:00
|
|
|
|
|
|
|
if kinds&LanguagePlugins != 0 {
|
|
|
|
// First make sure the language plugin is present. We need this to load the required resource plugins.
|
|
|
|
// TODO: we need to think about how best to version this. For now, it always picks the latest.
|
2024-01-25 23:28:58 +00:00
|
|
|
lang, err := host.LanguageRuntime(runtime, info)
|
2020-09-15 00:40:17 +00:00
|
|
|
if err != nil {
|
2024-01-25 23:28:58 +00:00
|
|
|
return nil, errors.Wrapf(err, "failed to load language plugin %s", runtime)
|
2020-09-15 00:40:17 +00:00
|
|
|
}
|
2022-08-26 14:51:14 +00:00
|
|
|
plugins = append(plugins, workspace.PluginSpec{
|
2024-01-25 23:28:58 +00:00
|
|
|
Name: runtime,
|
2020-09-15 00:40:17 +00:00
|
|
|
Kind: workspace.LanguagePlugin,
|
|
|
|
})
|
|
|
|
|
|
|
|
if kinds&ResourcePlugins != 0 {
|
|
|
|
// Use the language plugin to compute this project's set of plugin dependencies.
|
|
|
|
// TODO: we want to support loading precisely what the project needs, rather than doing a static scan of resolved
|
|
|
|
// packages. Doing this requires that we change our RPC interface and figure out how to configure plugins
|
|
|
|
// later than we do (right now, we do it up front, but at that point we don't know the version).
|
|
|
|
deps, err := lang.GetRequiredPlugins(info)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "failed to discover plugin requirements")
|
|
|
|
}
|
|
|
|
plugins = append(plugins, deps...)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If we can't load the language plugin, we can't discover the resource plugins.
|
|
|
|
contract.Assertf(kinds&ResourcePlugins != 0,
|
|
|
|
"cannot load resource plugins without also loading the language plugin")
|
|
|
|
}
|
|
|
|
|
|
|
|
return plugins, nil
|
|
|
|
}
|