docs/architecture/plugins.md
(plugins)=
Plugins are Pulumi's core extensibility mechanism, allowing the Pulumi engine to communicate in a uniform manner with various languages, resource providers, and other tools. Generally speaking, plugins are run as separate processes and often (though not always) communicated with over gRPC. Presently, Pulumi supports the following kinds of plugins:
(plugin-loading-execution)= (shimless)=
Plugins may be provided in one of two ways:
pulumi-<kind>-<name>, where <kind> is one of resource, language,
analyzer, converter, or tool, and <name> is a unique name for the
plugin. For example, the AWS resource provider plugin is named
pulumi-resource-aws.PulumiPlugin.yaml file and a set of files that
implement the plugin's functionality. The PulumiPlugin.yaml file specifies
the runtime to be used to execute the provided files. The engine reads this
and spawns the runtime's language plugin to run the plugin.
The language plugin's method is then
used to execute the plugin. This method of running plugins is sometimes
referred to as shimless, since prior to its introduction one would always
have to provide a "shim" executable (such as a shell script or batch file)
that did nothing but spawn the relevant interpreter over the provided files.Right now, there is no unified algorithm for resolving and installing a plugin. The only way to understand how installation works is to look at each location where we install plugins.
The pulumi CLI installs provider plugins in a lot of places:
pulumi installpulumi package addpulumi importpulumi plugin installpulumi package get-schemapulumi package publishEngine behavior depends on what is present in the global cache, but only for plugins in state where the plugin doesn’t specify a version. Installs do not handle plugins that themselves have plugin dependencies at all, but they handle normal dependencies1. You can get subtly different behavior between up-front and lazy installs for packages that are specified in the packages section of a project.
Engine related installs all call engine.EnsurePluginsAreInstalled. For plugins specs
that are passed to that function, we use on disk versions if present, otherwise we fetch
the latest version. After plugins are downloaded, pkg/workspace.InstallPluginContent is
called to install the plugin. This implementation is project aware, meaning that it takes
into account what packages are present in the packages section of your Pulumi.yaml.
The engine also installs plugins as needed when a register resource request comes for a
non-downloaded plugin. Here the engine calls pkg/workspace.InstallPlugin, which is not
project aware.
As of pr#20945 (released in v3.208.0), plugins with local paths correctly have
their dependencies installed before they are installed, but this logic only works for
local paths, it doesn’t work for git based components with dependencies. The root install
function for local packages is pkg/workspace.InstallPlugin, but otherwise we use
engine.EnsurePluginsAreInstalled.
This is the only callsite currently set up to resolve registry packages. Packages are not
installed if there is already a version installed and no version is specified or the
version specified is < the already installed version, unless --exact is passed. The
install is not project aware.
pulumi package add ends up calling packages.ProviderFromSource, which calls
pkg/workspace.InstallPlugin. That means it does not work on packages that depend on
other packages. This implementation is not project aware.
pulumi package get-schema also calls packages.SchemaFromSchemaSource, which calls into
packages.ProviderFromSource, same as pulumi package add.
Also uses packages.SchemaFromSchemaSource, with the same semantics.
Schema binding also winds up calling pkg/workspace.InstallPlugin. This is not project
aware (and it’s not clear it should be here).
When I say install, I mean going from a package descriptor to a running package. To install a given package descriptor, we need to:
This algorithm is implemented for production in pr#21177.
All of the above steps are fallible, which means that a given plugin can be in any of the following states:
(2) can be because the installation hasn't happened yet, or because the installation
failed for some reason. We distinguish between (2) and (3) by the presence of a marker
file: <plugin-name>.partial means present but not installed.
That is, npm install, pip install, etc. ↩