terraform/docs/plugin-protocol/README.md

214 lines
9.9 KiB
Markdown
Raw Normal View History

# Terraform Plugin Protocol
This directory contains documentation about the physical wire protocol that
Terraform Core uses to communicate with provider plugins.
Most providers are not written directly against this protocol. Instead, prefer
to use an SDK that implements this protocol and write the provider against
the SDK's API.
----
**If you want to write a plugin for Terraform, please refer to
[Extending Terraform](https://www.terraform.io/docs/extend/index.html) instead.**
This documentation is for those who are developing _Terraform SDKs_, rather
than those implementing plugins.
----
From Terraform v0.12.0 onwards, Terraform's plugin protocol is built on
[gRPC](https://grpc.io/). This directory contains `.proto` definitions of
different versions of Terraform's protocol.
Only `.proto` files published as part of Terraform release tags are actually
official protocol versions. If you are reading this directory on the `main`
branch or any other development branch then it may contain protocol definitions
that are not yet finalized and that may change before final release.
## RPC Plugin Model
Terraform plugins are normal executable programs that, when launched, expose
gRPC services on a server accessed via the loopback interface. Terraform Core
discovers and launches plugins, waits for a handshake to be printed on the
plugin's `stdout`, and then connects to the indicated port number as a
gRPC client.
For this reason, we commonly refer to Terraform Core itself as the plugin
"client" and the plugin program itself as the plugin "server". Both of these
processes run locally, with the server process appearing as a child process
of the client. Terraform Core controls the lifecycle of these server processes
and will terminate them when they are no longer required.
The startup and handshake protocol is not currently documented. We hope to
document it here or to link to external documentation on it in future.
## Versioning Strategy
The Plugin Protocol uses a versioning strategy that aims to allow gradual
enhancements to the protocol while retaining compatibility, but also to allow
more significant breaking changes from time to time while allowing old and
new plugins to be used together for some period.
The versioning strategy described below was introduced with protocol version
5.0 in Terraform v0.12. Prior versions of Terraform and prior protocol versions
do not follow this strategy.
The authoritative definition for each protocol version is in this directory
as a Protocol Buffers (protobuf) service definition. The files follow the
naming pattern `tfpluginX.Y.proto`, where X is the major version and Y
is the minor version.
### Major and minor versioning
The minor version increases for each change introducing optional new
functionality that can be ignored by implementations of prior versions. For
example, if a new field were added to an response message, it could be a minor
release as long as Terraform Core can provide some default behavior when that
field is not populated.
The major version increases for any significant change to the protocol where
compatibility is broken. However, Terraform Core and an SDK may both choose
to support multiple major versions at once: the plugin handshake includes a
negotiation step where client and server can work together to select a
mutually-supported major version.
The major version number is encoded into the protobuf package name: major
version 5 uses the package name `tfplugin5`, and one day major version 6
will switch to `tfplugin6`. This change of name allows a plugin server to
implement multiple major versions at once, by exporting multiple gRPC services.
Minor version differences rely instead on feature-detection mechanisms, so they
are not represented directly on the wire and exist primarily as a human
communication tool to help us easily talk about which software supports which
features.
## Version compatibility for Core, SDK, and Providers
A particular version of Terraform Core has both a minimum minor version it
requires and a maximum major version that it supports. A particular version of
Terraform Core may also be able to optionally use a newer minor version when
available, but fall back on older behavior when that functionality is not
available.
Likewise, each provider plugin release is compatible with a set of versions.
The compatible versions for a provider are a list of major and minor version
pairs, such as "4.0", "5.2", which indicates that the provider supports the
baseline features of major version 4 and supports major version 5 including
the enhancements from both minor versions 1 and 2. This provider would
therefore be compatible with a Terraform Core release that supports only
protocol version 5.0, since major version 5 is supported and the optional
5.1 and 5.2 enhancements will be ignored.
If Terraform Core and the plugin do not have at least one mutually-supported
major version, Terraform Core will return an error from `terraform init`
during plugin installation:
```
Provider "aws" v1.0.0 is not compatible with Terraform v0.12.0.
Provider version v2.0.0 is the earliest compatible version.
Select it with the following version constraint:
version = "~> 2.0.0"
```
```
Provider "aws" v3.0.0 is not compatible with Terraform v0.12.0.
Provider version v2.34.0 is the latest compatible version. Select
it with the following constraint:
version = "~> 2.34.0"
Alternatively, upgrade to the latest version of Terraform for compatibility with newer provider releases.
```
The above messages are for plugins installed via `terraform init` from a
Terraform registry, where the registry API allows Terraform Core to recognize
the protocol compatibility for each provider release. For plugins that are
installed manually to a local plugin directory, Terraform Core has no way to
suggest specific versions to upgrade or downgrade to, and so the error message
is more generic:
```
The installed version of provider "example" is not compatible with Terraform v0.12.0.
This provider was loaded from:
/usr/local/bin/terraform-provider-example_v0.1.0
```
## Adding/removing major version support in SDK and Providers
The set of supported major versions is decided by the SDK used by the plugin.
Over time, SDKs will add support for new major versions and phase out support
for older major versions.
In doing so, the SDK developer passes those capabilities and constraints on to
any provider using their SDK, and that will in turn affect the compatibility
of the plugin in ways that affect its semver-based version numbering:
- If an SDK upgrade adds support for a new provider protocol, that will usually
be considered a new feature and thus warrant a new minor version.
- If an SDK upgrade removes support for an old provider protocol, that is
always a breaking change and thus requires a major release of the provider.
For this reason, SDK developers must be clear in their release notes about
the addition and removal of support for major versions.
Terraform Core also makes an assumption about major version support when
it produces actionable error messages for users about incompatibilities:
a particular protocol major version is supported for a single consecutive
range of provider releases, with no "gaps".
## Using the protobuf specifications in an SDK
If you wish to build an SDK for Terraform plugins, an early step will be to
copy one or more `.proto` files from this directory into your own repository
(depending on which protocol versions you intend to support) and use the
`protoc` protocol buffers compiler (with gRPC extensions) to generate suitable
RPC stubs and types for your target language.
For example, if you happen to be targeting Python, you might generate the
stubs using a command like this:
```
protoc --python_out=. --grpc_python_out=. tfplugin5.1.proto
```
You can find out more about the tool usage for each target language in
[the gRPC Quick Start guides](https://grpc.io/docs/quickstart/).
The protobuf specification for a version is immutable after it has been
included in at least one Terraform release. Any changes will be documented in
a new `.proto` file establishing a new protocol version.
The protocol buffer compiler will produce some sort of library object appropriate
for the target language, which depending on the language might be called a
module, or a package, or something else. We recommend to include the protocol
major version in your module or package name so that you can potentially
support multiple versions concurrently in future. For example, if you are
targeting major version 5 you might call your package or module `tfplugin5`.
To upgrade to a newer minor protocol version, copy the new `.proto` file
from this directory into the same location as your previous version, delete
the previous version, and then run the protocol buffers compiler again
against the new `.proto` file. Because minor releases are backward-compatible,
you can simply update your previous stubs in-place rather than creating a
new set alongside.
To support a new _major_ protocol version, create a new package or module
and copy the relevant `.proto` file into it, creating a separate set of stubs
that can in principle allow your SDK to support both major versions at the
same time. We recommend supporting both the previous and current major versions
together for a while across a major version upgrade so that users can avoid
having to upgrade both Terraform Core and all of their providers at the same
time, but you can delete the previous major version stubs once you remove
support for that version.
**Note:** Some of the `.proto` files contain statements about being updated
in-place for minor versions. This reflects an earlier version management
strategy which is no longer followed. The current process is to create a
new file in this directory for each new minor version and consider all
previously-tagged definitions as immutable. The outdated comments in those
files are retained in order to keep the promise of immutability, even though
it is now incorrect.