Add support for provider metadata to modules. (#22583)

Implement a new provider_meta block in the terraform block of modules, allowing provider-keyed metadata to be communicated from HCL to provider binaries.

Bundled in this change for minimal protocol version bumping is the addition of markdown support for attribute descriptions and the ability to indicate when an attribute is deprecated, so this information can be shown in the schema dump.

Co-authored-by: Paul Tyng <paul@paultyng.net>
This commit is contained in:
Paddy 2020-03-05 16:53:24 -08:00 committed by GitHub
parent 4654b45d6b
commit e6592dc710
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 2457 additions and 246 deletions

View File

@ -16,6 +16,13 @@ func Provider() terraform.ResourceProvider {
Optional: true,
},
},
ProviderMetaSchema: map[string]*schema.Schema{
// Optionally allow specifying information at a module-level
"foo": {
Type: schema.TypeString,
Optional: true,
},
},
ResourcesMap: map[string]*schema.Resource{
"test_resource": testResource(),
"test_resource_gh12183": testResourceGH12183(),
@ -36,6 +43,7 @@ func Provider() terraform.ResourceProvider {
"test_resource_computed_set": testResourceComputedSet(),
"test_resource_config_mode": testResourceConfigMode(),
"test_resource_nested_id": testResourceNestedId(),
"test_resource_provider_meta": testResourceProviderMeta(),
"test_undeleteable": testResourceUndeleteable(),
"test_resource_required_min": testResourceRequiredMin(),
},

View File

@ -0,0 +1,95 @@
package test
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
)
func testResourceProviderMeta() *schema.Resource {
return &schema.Resource{
Create: testResourceProviderMetaCreate,
Read: testResourceProviderMetaRead,
Update: testResourceProviderMetaUpdate,
Delete: testResourceProviderMetaDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"optional": {
Type: schema.TypeString,
Optional: true,
},
},
}
}
type providerMeta struct {
Foo string `cty:"foo"`
}
func testResourceProviderMetaCreate(d *schema.ResourceData, meta interface{}) error {
d.SetId("testId")
var m providerMeta
err := d.GetProviderMeta(&m)
if err != nil {
return err
}
if m.Foo != "bar" {
return fmt.Errorf("expected provider_meta.foo to be %q, was %q",
"bar", m.Foo)
}
return testResourceProviderMetaRead(d, meta)
}
func testResourceProviderMetaRead(d *schema.ResourceData, meta interface{}) error {
var m providerMeta
err := d.GetProviderMeta(&m)
if err != nil {
return err
}
if m.Foo != "bar" {
return fmt.Errorf("expected provider_meta.foo to be %q, was %q",
"bar", m.Foo)
}
return nil
}
func testResourceProviderMetaUpdate(d *schema.ResourceData, meta interface{}) error {
var m providerMeta
err := d.GetProviderMeta(&m)
if err != nil {
return err
}
if m.Foo != "bar" {
return fmt.Errorf("expected provider_meta.foo to be %q, was %q",
"bar", m.Foo)
}
return testResourceProviderMetaRead(d, meta)
}
func testResourceProviderMetaDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
var m providerMeta
err := d.GetProviderMeta(&m)
if err != nil {
return err
}
if m.Foo != "bar" {
return fmt.Errorf("expected provider_meta.foo to be %q, was %q",
"bar", m.Foo)
}
return nil
}

View File

@ -0,0 +1,29 @@
package test
import (
"strings"
"testing"
"github.com/hashicorp/terraform/helper/resource"
)
func TestResourceProviderMeta_basic(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccCheckResourceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: strings.TrimSpace(`
terraform {
provider_meta "test" {
foo = "bar"
}
}
resource "test_resource_provider_meta" "foo" {
}
`),
},
},
})
}

View File

@ -9,12 +9,23 @@ import (
type attribute struct {
AttributeType json.RawMessage `json:"type,omitempty"`
Description string `json:"description,omitempty"`
DescriptionKind string `json:"description_kind,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
Required bool `json:"required,omitempty"`
Optional bool `json:"optional,omitempty"`
Computed bool `json:"computed,omitempty"`
Sensitive bool `json:"sensitive,omitempty"`
}
func marshalStringKind(sk configschema.StringKind) string {
switch sk {
default:
return "plain"
case configschema.StringMarkdown:
return "markdown"
}
}
func marshalAttribute(attr *configschema.Attribute) *attribute {
// we're not concerned about errors because at this point the schema has
// already been checked and re-checked.
@ -23,9 +34,11 @@ func marshalAttribute(attr *configschema.Attribute) *attribute {
return &attribute{
AttributeType: attrTy,
Description: attr.Description,
DescriptionKind: marshalStringKind(attr.DescriptionKind),
Required: attr.Required,
Optional: attr.Optional,
Computed: attr.Computed,
Sensitive: attr.Sensitive,
Deprecated: attr.Deprecated,
}
}

View File

@ -21,6 +21,7 @@ func TestMarshalAttribute(t *testing.T) {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
DescriptionKind: "plain",
},
},
{ // collection types look a little odd.
@ -29,6 +30,7 @@ func TestMarshalAttribute(t *testing.T) {
AttributeType: json.RawMessage(`["map","string"]`),
Optional: true,
Computed: true,
DescriptionKind: "plain",
},
},
}

View File

@ -7,6 +7,9 @@ import (
type block struct {
Attributes map[string]*attribute `json:"attributes,omitempty"`
BlockTypes map[string]*blockType `json:"block_types,omitempty"`
Description string `json:"description,omitempty"`
DescriptionKind string `json:"description_kind,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
}
type blockType struct {
@ -48,7 +51,12 @@ func marshalBlock(configBlock *configschema.Block) *block {
return &block{}
}
var ret block
ret := block{
Deprecated: configBlock.Deprecated,
Description: configBlock.Description,
DescriptionKind: marshalStringKind(configBlock.DescriptionKind),
}
if len(configBlock.Attributes) > 0 {
attrs := make(map[string]*attribute, len(configBlock.Attributes))
for k, attr := range configBlock.Attributes {

View File

@ -39,20 +39,22 @@ func TestMarshalBlock(t *testing.T) {
},
Want: &block{
Attributes: map[string]*attribute{
"ami": {AttributeType: json.RawMessage(`"string"`), Optional: true},
"id": {AttributeType: json.RawMessage(`"string"`), Optional: true, Computed: true},
"ami": {AttributeType: json.RawMessage(`"string"`), Optional: true, DescriptionKind: "plain"},
"id": {AttributeType: json.RawMessage(`"string"`), Optional: true, Computed: true, DescriptionKind: "plain"},
},
BlockTypes: map[string]*blockType{
"network_interface": {
NestingMode: "list",
Block: &block{
Attributes: map[string]*attribute{
"description": {AttributeType: json.RawMessage(`"string"`), Optional: true},
"device_index": {AttributeType: json.RawMessage(`"string"`), Optional: true},
},
"description": {AttributeType: json.RawMessage(`"string"`), Optional: true, DescriptionKind: "plain"},
"device_index": {AttributeType: json.RawMessage(`"string"`), Optional: true, DescriptionKind: "plain"},
},
DescriptionKind: "plain",
},
},
},
DescriptionKind: "plain",
},
},
}

View File

@ -30,8 +30,10 @@ func TestMarshalProvider(t *testing.T) {
"region": {
AttributeType: json.RawMessage(`"string"`),
Required: true,
DescriptionKind: "plain",
},
},
DescriptionKind: "plain",
},
},
ResourceSchemas: map[string]*schema{
@ -43,10 +45,12 @@ func TestMarshalProvider(t *testing.T) {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
DescriptionKind: "plain",
},
"ami": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
},
BlockTypes: map[string]*blockType{
@ -56,16 +60,20 @@ func TestMarshalProvider(t *testing.T) {
"device_index": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
"description": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
},
DescriptionKind: "plain",
},
NestingMode: "list",
},
},
DescriptionKind: "plain",
},
},
},
@ -78,10 +86,12 @@ func TestMarshalProvider(t *testing.T) {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
Computed: true,
DescriptionKind: "plain",
},
"ami": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
},
BlockTypes: map[string]*blockType{
@ -91,16 +101,20 @@ func TestMarshalProvider(t *testing.T) {
"device_index": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
"description": {
AttributeType: json.RawMessage(`"string"`),
Optional: true,
DescriptionKind: "plain",
},
},
DescriptionKind: "plain",
},
NestingMode: "list",
},
},
DescriptionKind: "plain",
},
},
},

View File

@ -9,14 +9,17 @@
"attributes": {
"ami": {
"type": "string",
"optional": true
"optional": true,
"description_kind": "plain"
},
"id": {
"type": "string",
"optional": true,
"computed": true
}
}
"computed": true,
"description_kind": "plain"
}
},
"description_kind": "plain"
}
}
}

View File

@ -4,6 +4,13 @@ import (
"github.com/zclconf/go-cty/cty"
)
type StringKind int
const (
StringPlain StringKind = iota
StringMarkdown
)
// Block represents a configuration block.
//
// "Block" here is a logical grouping construct, though it happens to map
@ -21,6 +28,11 @@ type Block struct {
// BlockTypes describes any nested block types that may appear directly
// inside the block.
BlockTypes map[string]*NestedBlock
Description string
DescriptionKind StringKind
Deprecated bool
}
// Attribute represents a configuration attribute, within a block.
@ -33,6 +45,7 @@ type Attribute struct {
// one or two sentences, leaving full definition to longer-form
// documentation defined elsewhere.
Description string
DescriptionKind StringKind
// Required, if set to true, specifies that an omitted or null value is
// not permitted.
@ -55,6 +68,8 @@ type Attribute struct {
// future to help Terraform mask sensitive information. (Terraform
// currently achieves this in a limited sense via other mechanisms.)
Sensitive bool
Deprecated bool
}
// NestedBlock represents the embedding of one block within another.

View File

@ -32,6 +32,7 @@ type Module struct {
ProviderConfigs map[string]*Provider
ProviderRequirements map[string]ProviderRequirements
ProviderLocalNames map[addrs.Provider]string
ProviderMetas map[addrs.Provider]*ProviderMeta
Variables map[string]*Variable
Locals map[string]*Local
@ -61,6 +62,7 @@ type File struct {
Backends []*Backend
ProviderConfigs []*Provider
ProviderMetas []*ProviderMeta
RequiredProviders []*RequiredProvider
Variables []*Variable
@ -93,6 +95,7 @@ func NewModule(primaryFiles, overrideFiles []*File) (*Module, hcl.Diagnostics) {
ModuleCalls: map[string]*ModuleCall{},
ManagedResources: map[string]*Resource{},
DataResources: map[string]*Resource{},
ProviderMetas: map[addrs.Provider]*ProviderMeta{},
}
for _, file := range primaryFiles {
@ -195,6 +198,19 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
}
}
for _, pm := range file.ProviderMetas {
// TODO(paddy): pm.Provider is a string, but we need to build an addrs.Provider out of it somehow
if existing, exists := m.ProviderMetas[addrs.NewLegacyProvider(pm.Provider)]; exists {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate provider_meta block",
Detail: fmt.Sprintf("A provider_meta block for provider %q was already declared at %s. Providers may only have one provider_meta block per module.", existing.Provider, existing.DeclRange),
Subject: &pm.DeclRange,
})
}
m.ProviderMetas[addrs.NewLegacyProvider(pm.Provider)] = pm
}
for _, v := range file.Variables {
if existing, exists := m.Variables[v.Name]; exists {
diags = append(diags, &hcl.Diagnostic{

View File

@ -77,6 +77,13 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost
diags = append(diags, reqsDiags...)
file.RequiredProviders = append(file.RequiredProviders, reqs...)
case "provider_meta":
providerCfg, cfgDiags := decodeProviderMetaBlock(innerBlock)
diags = append(diags, cfgDiags...)
if providerCfg != nil {
file.ProviderMetas = append(file.ProviderMetas, providerCfg)
}
default:
// Should never happen because the above cases should be exhaustive
// for all block type names in our schema.
@ -231,6 +238,10 @@ var terraformBlockSchema = &hcl.BodySchema{
{
Type: "required_providers",
},
{
Type: "provider_meta",
LabelNames: []string{"provider"},
},
},
}

22
configs/provider_meta.go Normal file
View File

@ -0,0 +1,22 @@
package configs
import "github.com/hashicorp/hcl/v2"
// ProviderMeta represents a "provider_meta" block inside a "terraform" block
// in a module or file.
type ProviderMeta struct {
Provider string
Config hcl.Body
ProviderRange hcl.Range
DeclRange hcl.Range
}
func decodeProviderMetaBlock(block *hcl.Block) (*ProviderMeta, hcl.Diagnostics) {
return &ProviderMeta{
Provider: block.Labels[0],
ProviderRange: block.LabelRanges[0],
Config: block.Body,
DeclRange: block.DefRange,
}, nil
}

View File

@ -0,0 +1,368 @@
// Terraform Plugin RPC protocol version 5.2
//
// This file defines version 5.2 of the RPC protocol. To implement a plugin
// against this protocol, copy this definition into your own codebase and
// use protoc to generate stubs for your target language.
//
// This file will not be updated. Any minor versions of protocol 5 to follow
// should copy this file and modify the copy while maintaing backwards
// compatibility. Breaking changes, if any are required, will come
// in a subsequent major version with its own separate proto definition.
//
// Note that only the proto files included in a release tag of Terraform are
// official protocol releases. Proto files taken from other commits may include
// incomplete changes or features that did not make it into a final release.
// In all reasonable cases, plugin developers should take the proto file from
// the tag of the most recent release of Terraform, and not from the master
// branch or any other development branch.
//
syntax = "proto3";
package tfplugin5;
// DynamicValue is an opaque encoding of terraform data, with the field name
// indicating the encoding scheme used.
message DynamicValue {
bytes msgpack = 1;
bytes json = 2;
}
message Diagnostic {
enum Severity {
INVALID = 0;
ERROR = 1;
WARNING = 2;
}
Severity severity = 1;
string summary = 2;
string detail = 3;
AttributePath attribute = 4;
}
message AttributePath {
message Step {
oneof selector {
// Set "attribute_name" to represent looking up an attribute
// in the current object value.
string attribute_name = 1;
// Set "element_key_*" to represent looking up an element in
// an indexable collection type.
string element_key_string = 2;
int64 element_key_int = 3;
}
}
repeated Step steps = 1;
}
message Stop {
message Request {
}
message Response {
string Error = 1;
}
}
// RawState holds the stored state for a resource to be upgraded by the
// provider. It can be in one of two formats, the current json encoded format
// in bytes, or the legacy flatmap format as a map of strings.
message RawState {
bytes json = 1;
map<string, string> flatmap = 2;
}
enum StringKind {
PLAIN = 0;
MARKDOWN = 1;
}
// Schema is the configuration schema for a Resource, Provider, or Provisioner.
message Schema {
message Block {
int64 version = 1;
repeated Attribute attributes = 2;
repeated NestedBlock block_types = 3;
string description = 4;
StringKind description_kind = 5;
bool deprecated = 6;
}
message Attribute {
string name = 1;
bytes type = 2;
string description = 3;
bool required = 4;
bool optional = 5;
bool computed = 6;
bool sensitive = 7;
StringKind description_kind = 8;
bool deprecated = 9;
}
message NestedBlock {
enum NestingMode {
INVALID = 0;
SINGLE = 1;
LIST = 2;
SET = 3;
MAP = 4;
GROUP = 5;
}
string type_name = 1;
Block block = 2;
NestingMode nesting = 3;
int64 min_items = 4;
int64 max_items = 5;
}
// The version of the schema.
// Schemas are versioned, so that providers can upgrade a saved resource
// state when the schema is changed.
int64 version = 1;
// Block is the top level configuration block for this schema.
Block block = 2;
}
service Provider {
//////// Information about what a provider supports/expects
rpc GetSchema(GetProviderSchema.Request) returns (GetProviderSchema.Response);
rpc PrepareProviderConfig(PrepareProviderConfig.Request) returns (PrepareProviderConfig.Response);
rpc ValidateResourceTypeConfig(ValidateResourceTypeConfig.Request) returns (ValidateResourceTypeConfig.Response);
rpc ValidateDataSourceConfig(ValidateDataSourceConfig.Request) returns (ValidateDataSourceConfig.Response);
rpc UpgradeResourceState(UpgradeResourceState.Request) returns (UpgradeResourceState.Response);
//////// One-time initialization, called before other functions below
rpc Configure(Configure.Request) returns (Configure.Response);
//////// Managed Resource Lifecycle
rpc ReadResource(ReadResource.Request) returns (ReadResource.Response);
rpc PlanResourceChange(PlanResourceChange.Request) returns (PlanResourceChange.Response);
rpc ApplyResourceChange(ApplyResourceChange.Request) returns (ApplyResourceChange.Response);
rpc ImportResourceState(ImportResourceState.Request) returns (ImportResourceState.Response);
rpc ReadDataSource(ReadDataSource.Request) returns (ReadDataSource.Response);
//////// Graceful Shutdown
rpc Stop(Stop.Request) returns (Stop.Response);
}
message GetProviderSchema {
message Request {
}
message Response {
Schema provider = 1;
map<string, Schema> resource_schemas = 2;
map<string, Schema> data_source_schemas = 3;
repeated Diagnostic diagnostics = 4;
Schema provider_meta = 5;
}
}
message PrepareProviderConfig {
message Request {
DynamicValue config = 1;
}
message Response {
DynamicValue prepared_config = 1;
repeated Diagnostic diagnostics = 2;
}
}
message UpgradeResourceState {
message Request {
string type_name = 1;
// version is the schema_version number recorded in the state file
int64 version = 2;
// raw_state is the raw states as stored for the resource. Core does
// not have access to the schema of prior_version, so it's the
// provider's responsibility to interpret this value using the
// appropriate older schema. The raw_state will be the json encoded
// state, or a legacy flat-mapped format.
RawState raw_state = 3;
}
message Response {
// new_state is a msgpack-encoded data structure that, when interpreted with
// the _current_ schema for this resource type, is functionally equivalent to
// that which was given in prior_state_raw.
DynamicValue upgraded_state = 1;
// diagnostics describes any errors encountered during migration that could not
// be safely resolved, and warnings about any possibly-risky assumptions made
// in the upgrade process.
repeated Diagnostic diagnostics = 2;
}
}
message ValidateResourceTypeConfig {
message Request {
string type_name = 1;
DynamicValue config = 2;
}
message Response {
repeated Diagnostic diagnostics = 1;
}
}
message ValidateDataSourceConfig {
message Request {
string type_name = 1;
DynamicValue config = 2;
}
message Response {
repeated Diagnostic diagnostics = 1;
}
}
message Configure {
message Request {
string terraform_version = 1;
DynamicValue config = 2;
}
message Response {
repeated Diagnostic diagnostics = 1;
}
}
message ReadResource {
message Request {
string type_name = 1;
DynamicValue current_state = 2;
bytes private = 3;
DynamicValue provider_meta = 4;
}
message Response {
DynamicValue new_state = 1;
repeated Diagnostic diagnostics = 2;
bytes private = 3;
}
}
message PlanResourceChange {
message Request {
string type_name = 1;
DynamicValue prior_state = 2;
DynamicValue proposed_new_state = 3;
DynamicValue config = 4;
bytes prior_private = 5;
DynamicValue provider_meta = 6;
}
message Response {
DynamicValue planned_state = 1;
repeated AttributePath requires_replace = 2;
bytes planned_private = 3;
repeated Diagnostic diagnostics = 4;
// This may be set only by the helper/schema "SDK" in the main Terraform
// repository, to request that Terraform Core >=0.12 permit additional
// inconsistencies that can result from the legacy SDK type system
// and its imprecise mapping to the >=0.12 type system.
// The change in behavior implied by this flag makes sense only for the
// specific details of the legacy SDK type system, and are not a general
// mechanism to avoid proper type handling in providers.
//
// ==== DO NOT USE THIS ====
// ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ====
// ==== DO NOT USE THIS ====
bool legacy_type_system = 5;
}
}
message ApplyResourceChange {
message Request {
string type_name = 1;
DynamicValue prior_state = 2;
DynamicValue planned_state = 3;
DynamicValue config = 4;
bytes planned_private = 5;
DynamicValue provider_meta = 6;
}
message Response {
DynamicValue new_state = 1;
bytes private = 2;
repeated Diagnostic diagnostics = 3;
// This may be set only by the helper/schema "SDK" in the main Terraform
// repository, to request that Terraform Core >=0.12 permit additional
// inconsistencies that can result from the legacy SDK type system
// and its imprecise mapping to the >=0.12 type system.
// The change in behavior implied by this flag makes sense only for the
// specific details of the legacy SDK type system, and are not a general
// mechanism to avoid proper type handling in providers.
//
// ==== DO NOT USE THIS ====
// ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ====
// ==== DO NOT USE THIS ====
bool legacy_type_system = 4;
}
}
message ImportResourceState {
message Request {
string type_name = 1;
string id = 2;
}
message ImportedResource {
string type_name = 1;
DynamicValue state = 2;
bytes private = 3;
}
message Response {
repeated ImportedResource imported_resources = 1;
repeated Diagnostic diagnostics = 2;
}
}
message ReadDataSource {
message Request {
string type_name = 1;
DynamicValue config = 2;
DynamicValue provider_meta = 3;
}
message Response {
DynamicValue state = 1;
repeated Diagnostic diagnostics = 2;
}
}
service Provisioner {
rpc GetSchema(GetProvisionerSchema.Request) returns (GetProvisionerSchema.Response);
rpc ValidateProvisionerConfig(ValidateProvisionerConfig.Request) returns (ValidateProvisionerConfig.Response);
rpc ProvisionResource(ProvisionResource.Request) returns (stream ProvisionResource.Response);
rpc Stop(Stop.Request) returns (Stop.Response);
}
message GetProvisionerSchema {
message Request {
}
message Response {
Schema provisioner = 1;
repeated Diagnostic diagnostics = 2;
}
}
message ValidateProvisionerConfig {
message Request {
DynamicValue config = 1;
}
message Response {
repeated Diagnostic diagnostics = 1;
}
}
message ProvisionResource {
message Request {
DynamicValue config = 1;
DynamicValue connection = 2;
}
message Response {
string output = 1;
repeated Diagnostic diagnostics = 2;
}
}

9
go.sum
View File

@ -106,7 +106,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.0 h1:HIgH5xUWXT914HCI671AxuTTqjj64UOFr7pHn48LUTI=
github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
github.com/coreos/etcd v3.3.10+incompatible h1:KjVWqrZ5U0wa3CxY2AxlH6/UcB+PK2td1DcsYhA+HRs=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@ -372,6 +372,8 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
@ -479,6 +481,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb h1:TR699M2v0qoKTOHxeLgp6zPqaQNs74f01a/ob9W0qko=
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -529,11 +532,15 @@ google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=

View File

@ -55,6 +55,10 @@ func (s *GRPCProviderServer) GetSchema(_ context.Context, req *proto.GetProvider
Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlock()),
}
resp.ProviderMeta = &proto.Schema{
Block: convert.ConfigSchemaToProto(s.getProviderMetaSchemaBlock()),
}
for typ, res := range s.provider.ResourcesMap {
resp.ResourceSchemas[typ] = &proto.Schema{
Version: int64(res.SchemaVersion),
@ -76,6 +80,10 @@ func (s *GRPCProviderServer) getProviderSchemaBlock() *configschema.Block {
return schema.InternalMap(s.provider.Schema).CoreConfigSchema()
}
func (s *GRPCProviderServer) getProviderMetaSchemaBlock() *configschema.Block {
return schema.InternalMap(s.provider.ProviderMetaSchema).CoreConfigSchema()
}
func (s *GRPCProviderServer) getResourceSchemaBlock(name string) *configschema.Block {
res := s.provider.ResourcesMap[name]
return res.CoreConfigSchema()
@ -522,6 +530,16 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
}
instanceState.Meta = private
pmSchemaBlock := s.getProviderMetaSchemaBlock()
if pmSchemaBlock != nil && req.ProviderMeta != nil {
providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.Msgpack, pmSchemaBlock.ImpliedType())
if err != nil {
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
return resp, nil
}
instanceState.ProviderMeta = providerSchemaVal
}
newInstanceState, err := res.RefreshWithoutUpgrade(instanceState, s.provider.Meta())
if err != nil {
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
@ -621,6 +639,16 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
priorState.Meta = priorPrivate
pmSchemaBlock := s.getProviderMetaSchemaBlock()
if pmSchemaBlock != nil && req.ProviderMeta != nil {
providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.Msgpack, pmSchemaBlock.ImpliedType())
if err != nil {
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
return resp, nil
}
priorState.ProviderMeta = providerSchemaVal
}
// Ensure there are no nulls that will cause helper/schema to panic.
if err := validateConfigNulls(proposedNewStateVal, nil); err != nil {
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
@ -882,6 +910,16 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
}
}
pmSchemaBlock := s.getProviderMetaSchemaBlock()
if pmSchemaBlock != nil && req.ProviderMeta != nil {
providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.Msgpack, pmSchemaBlock.ImpliedType())
if err != nil {
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
return resp, nil
}
priorState.ProviderMeta = providerSchemaVal
}
newInstanceState, err := s.provider.Apply(info, priorState, diff)
// we record the error here, but continue processing any returned state.
if err != nil {

View File

@ -7,7 +7,7 @@ import (
"sort"
"sync"
"github.com/hashicorp/go-multierror"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/terraform"
)
@ -50,6 +50,14 @@ type Provider struct {
// and must *not* implement Create, Update or Delete.
DataSourcesMap map[string]*Resource
// ProviderMetaSchema is the schema for the configuration of the meta
// information for this provider. If this provider has no meta info,
// this can be omitted. This functionality is currently experimental
// and subject to change or break without warning; it should only be
// used by providers that are collaborating on its use with the
// Terraform team.
ProviderMetaSchema map[string]*Schema
// ConfigureFunc is a function for configuring the provider. If the
// provider doesn't need to be configured, this can be omitted.
//

View File

@ -247,6 +247,9 @@ func (r *Resource) Apply(
if err != nil {
return s, err
}
if s != nil && data != nil {
data.providerMeta = s.ProviderMeta
}
// Instance Diff shoould have the timeout info, need to copy it over to the
// ResourceData meta
@ -437,6 +440,10 @@ func (r *Resource) RefreshWithoutUpgrade(
return s, err
}
if s != nil {
data.providerMeta = s.ProviderMeta
}
exists, err := r.Exists(data, meta)
if err != nil {
return s, err
@ -452,6 +459,10 @@ func (r *Resource) RefreshWithoutUpgrade(
return s, err
}
if s != nil {
data.providerMeta = s.ProviderMeta
}
err = r.Read(data, meta)
state := data.State()
if state != nil && state.ID == "" {

View File

@ -8,6 +8,8 @@ import (
"time"
"github.com/hashicorp/terraform/terraform"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
)
// ResourceData is used to query and set the attributes of a resource.
@ -26,6 +28,7 @@ type ResourceData struct {
diff *terraform.InstanceDiff
meta map[string]interface{}
timeouts *ResourceTimeout
providerMeta cty.Value
// Don't set
multiReader *MultiLevelFieldReader
@ -549,3 +552,10 @@ func (d *ResourceData) get(addr []string, source getSource) getResult {
Schema: schema,
}
}
func (d *ResourceData) GetProviderMeta(dst interface{}) error {
if d.providerMeta.IsNull() {
return nil
}
return gocty.FromCtyValue(d.providerMeta, &dst)
}

View File

@ -24,6 +24,31 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type StringKind int32
const (
StringKind_PLAIN StringKind = 0
StringKind_MARKDOWN StringKind = 1
)
var StringKind_name = map[int32]string{
0: "PLAIN",
1: "MARKDOWN",
}
var StringKind_value = map[string]int32{
"PLAIN": 0,
"MARKDOWN": 1,
}
func (x StringKind) String() string {
return proto.EnumName(StringKind_name, int32(x))
}
func (StringKind) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_17ae6090ff270234, []int{0}
}
type Diagnostic_Severity int32
const (
@ -542,6 +567,9 @@ type Schema_Block struct {
Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
Attributes []*Schema_Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"`
BlockTypes []*Schema_NestedBlock `protobuf:"bytes,3,rep,name=block_types,json=blockTypes,proto3" json:"block_types,omitempty"`
Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"`
DescriptionKind StringKind `protobuf:"varint,5,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin5.StringKind" json:"description_kind,omitempty"`
Deprecated bool `protobuf:"varint,6,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -593,6 +621,27 @@ func (m *Schema_Block) GetBlockTypes() []*Schema_NestedBlock {
return nil
}
func (m *Schema_Block) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
func (m *Schema_Block) GetDescriptionKind() StringKind {
if m != nil {
return m.DescriptionKind
}
return StringKind_PLAIN
}
func (m *Schema_Block) GetDeprecated() bool {
if m != nil {
return m.Deprecated
}
return false
}
type Schema_Attribute struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Type []byte `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
@ -601,6 +650,8 @@ type Schema_Attribute struct {
Optional bool `protobuf:"varint,5,opt,name=optional,proto3" json:"optional,omitempty"`
Computed bool `protobuf:"varint,6,opt,name=computed,proto3" json:"computed,omitempty"`
Sensitive bool `protobuf:"varint,7,opt,name=sensitive,proto3" json:"sensitive,omitempty"`
DescriptionKind StringKind `protobuf:"varint,8,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin5.StringKind" json:"description_kind,omitempty"`
Deprecated bool `protobuf:"varint,9,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -680,6 +731,20 @@ func (m *Schema_Attribute) GetSensitive() bool {
return false
}
func (m *Schema_Attribute) GetDescriptionKind() StringKind {
if m != nil {
return m.DescriptionKind
}
return StringKind_PLAIN
}
func (m *Schema_Attribute) GetDeprecated() bool {
if m != nil {
return m.Deprecated
}
return false
}
type Schema_NestedBlock struct {
TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"`
Block *Schema_Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"`
@ -818,6 +883,7 @@ type GetProviderSchema_Response struct {
ResourceSchemas map[string]*Schema `protobuf:"bytes,2,rep,name=resource_schemas,json=resourceSchemas,proto3" json:"resource_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
DataSourceSchemas map[string]*Schema `protobuf:"bytes,3,rep,name=data_source_schemas,json=dataSourceSchemas,proto3" json:"data_source_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"`
ProviderMeta *Schema `protobuf:"bytes,5,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -876,6 +942,13 @@ func (m *GetProviderSchema_Response) GetDiagnostics() []*Diagnostic {
return nil
}
func (m *GetProviderSchema_Response) GetProviderMeta() *Schema {
if m != nil {
return m.ProviderMeta
}
return nil
}
type PrepareProviderConfig struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@ -1524,6 +1597,7 @@ type ReadResource_Request struct {
TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"`
CurrentState *DynamicValue `protobuf:"bytes,2,opt,name=current_state,json=currentState,proto3" json:"current_state,omitempty"`
Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"`
ProviderMeta *DynamicValue `protobuf:"bytes,4,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1575,6 +1649,13 @@ func (m *ReadResource_Request) GetPrivate() []byte {
return nil
}
func (m *ReadResource_Request) GetProviderMeta() *DynamicValue {
if m != nil {
return m.ProviderMeta
}
return nil
}
type ReadResource_Response struct {
NewState *DynamicValue `protobuf:"bytes,1,opt,name=new_state,json=newState,proto3" json:"new_state,omitempty"`
Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"`
@ -1667,6 +1748,7 @@ type PlanResourceChange_Request struct {
ProposedNewState *DynamicValue `protobuf:"bytes,3,opt,name=proposed_new_state,json=proposedNewState,proto3" json:"proposed_new_state,omitempty"`
Config *DynamicValue `protobuf:"bytes,4,opt,name=config,proto3" json:"config,omitempty"`
PriorPrivate []byte `protobuf:"bytes,5,opt,name=prior_private,json=priorPrivate,proto3" json:"prior_private,omitempty"`
ProviderMeta *DynamicValue `protobuf:"bytes,6,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1732,6 +1814,13 @@ func (m *PlanResourceChange_Request) GetPriorPrivate() []byte {
return nil
}
func (m *PlanResourceChange_Request) GetProviderMeta() *DynamicValue {
if m != nil {
return m.ProviderMeta
}
return nil
}
type PlanResourceChange_Response struct {
PlannedState *DynamicValue `protobuf:"bytes,1,opt,name=planned_state,json=plannedState,proto3" json:"planned_state,omitempty"`
RequiresReplace []*AttributePath `protobuf:"bytes,2,rep,name=requires_replace,json=requiresReplace,proto3" json:"requires_replace,omitempty"`
@ -1851,6 +1940,7 @@ type ApplyResourceChange_Request struct {
PlannedState *DynamicValue `protobuf:"bytes,3,opt,name=planned_state,json=plannedState,proto3" json:"planned_state,omitempty"`
Config *DynamicValue `protobuf:"bytes,4,opt,name=config,proto3" json:"config,omitempty"`
PlannedPrivate []byte `protobuf:"bytes,5,opt,name=planned_private,json=plannedPrivate,proto3" json:"planned_private,omitempty"`
ProviderMeta *DynamicValue `protobuf:"bytes,6,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1916,6 +2006,13 @@ func (m *ApplyResourceChange_Request) GetPlannedPrivate() []byte {
return nil
}
func (m *ApplyResourceChange_Request) GetProviderMeta() *DynamicValue {
if m != nil {
return m.ProviderMeta
}
return nil
}
type ApplyResourceChange_Response struct {
NewState *DynamicValue `protobuf:"bytes,1,opt,name=new_state,json=newState,proto3" json:"new_state,omitempty"`
Private []byte `protobuf:"bytes,2,opt,name=private,proto3" json:"private,omitempty"`
@ -2204,6 +2301,7 @@ var xxx_messageInfo_ReadDataSource proto.InternalMessageInfo
type ReadDataSource_Request struct {
TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"`
Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"`
ProviderMeta *DynamicValue `protobuf:"bytes,3,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -2248,6 +2346,13 @@ func (m *ReadDataSource_Request) GetConfig() *DynamicValue {
return nil
}
func (m *ReadDataSource_Request) GetProviderMeta() *DynamicValue {
if m != nil {
return m.ProviderMeta
}
return nil
}
type ReadDataSource_Response struct {
State *DynamicValue `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"`
@ -2639,6 +2744,7 @@ func (m *ProvisionResource_Response) GetDiagnostics() []*Diagnostic {
}
func init() {
proto.RegisterEnum("tfplugin5.StringKind", StringKind_name, StringKind_value)
proto.RegisterEnum("tfplugin5.Diagnostic_Severity", Diagnostic_Severity_name, Diagnostic_Severity_value)
proto.RegisterEnum("tfplugin5.Schema_NestedBlock_NestingMode", Schema_NestedBlock_NestingMode_name, Schema_NestedBlock_NestingMode_value)
proto.RegisterType((*DynamicValue)(nil), "tfplugin5.DynamicValue")
@ -2704,125 +2810,133 @@ func init() {
func init() { proto.RegisterFile("tfplugin5.proto", fileDescriptor_17ae6090ff270234) }
var fileDescriptor_17ae6090ff270234 = []byte{
// 1880 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xcb, 0x6f, 0x23, 0x49,
0x19, 0x9f, 0xf6, 0x23, 0xb1, 0x3f, 0xe7, 0xe1, 0xd4, 0xcc, 0x0e, 0xa6, 0x77, 0x17, 0x82, 0x79,
0x24, 0xab, 0xdd, 0xf1, 0xac, 0x32, 0xb0, 0xbb, 0x84, 0xd1, 0x8a, 0x6c, 0x26, 0x64, 0x22, 0x66,
0xb2, 0xa1, 0x3c, 0x0f, 0x24, 0xa4, 0xb5, 0x6a, 0xdc, 0x15, 0x4f, 0x33, 0x76, 0x77, 0x6f, 0x75,
0x39, 0x89, 0x85, 0xc4, 0x05, 0xc1, 0x19, 0x09, 0xf1, 0x90, 0x78, 0x5c, 0x40, 0xe2, 0x1f, 0xe0,
0x00, 0xdc, 0x38, 0xf1, 0x0f, 0x70, 0x03, 0x4e, 0x08, 0x6e, 0x9c, 0xe1, 0x82, 0x84, 0xea, 0xd5,
0x5d, 0xb6, 0xdb, 0x4e, 0x4f, 0xb2, 0x23, 0xc4, 0xad, 0xab, 0xbe, 0x5f, 0x7d, 0xdf, 0x57, 0xdf,
0xab, 0xbe, 0xcf, 0x86, 0x55, 0x7e, 0x1c, 0xf5, 0x87, 0x3d, 0x3f, 0xf8, 0x42, 0x2b, 0x62, 0x21,
0x0f, 0x51, 0x35, 0xd9, 0x68, 0xde, 0x86, 0xa5, 0x3b, 0xa3, 0x80, 0x0c, 0xfc, 0xee, 0x23, 0xd2,
0x1f, 0x52, 0xd4, 0x80, 0xc5, 0x41, 0xdc, 0x8b, 0x48, 0xf7, 0x59, 0xc3, 0x59, 0x77, 0x36, 0x97,
0xb0, 0x59, 0x22, 0x04, 0xa5, 0x6f, 0xc6, 0x61, 0xd0, 0x28, 0xc8, 0x6d, 0xf9, 0xdd, 0xfc, 0x9b,
0x03, 0x70, 0xc7, 0x27, 0xbd, 0x20, 0x8c, 0xb9, 0xdf, 0x45, 0xdb, 0x50, 0x89, 0xe9, 0x09, 0x65,
0x3e, 0x1f, 0xc9, 0xd3, 0x2b, 0x5b, 0x9f, 0x68, 0xa5, 0xb2, 0x53, 0x60, 0xab, 0xad, 0x51, 0x38,
0xc1, 0x0b, 0xc1, 0xf1, 0x70, 0x30, 0x20, 0x6c, 0x24, 0x25, 0x54, 0xb1, 0x59, 0xa2, 0xeb, 0xb0,
0xe0, 0x51, 0x4e, 0xfc, 0x7e, 0xa3, 0x28, 0x09, 0x7a, 0x85, 0xde, 0x82, 0x2a, 0xe1, 0x9c, 0xf9,
0x4f, 0x86, 0x9c, 0x36, 0x4a, 0xeb, 0xce, 0x66, 0x6d, 0xab, 0x61, 0x89, 0xdb, 0x31, 0xb4, 0x23,
0xc2, 0x9f, 0xe2, 0x14, 0xda, 0xbc, 0x09, 0x15, 0x23, 0x1f, 0xd5, 0x60, 0xf1, 0xe0, 0xf0, 0xd1,
0xce, 0xbd, 0x83, 0x3b, 0xf5, 0x2b, 0xa8, 0x0a, 0xe5, 0x3d, 0x8c, 0xdf, 0xc7, 0x75, 0x47, 0xec,
0x3f, 0xde, 0xc1, 0x87, 0x07, 0x87, 0xfb, 0xf5, 0x42, 0xf3, 0x2f, 0x0e, 0x2c, 0x8f, 0x71, 0x43,
0xb7, 0xa0, 0x1c, 0x73, 0x1a, 0xc5, 0x0d, 0x67, 0xbd, 0xb8, 0x59, 0xdb, 0x7a, 0x75, 0x96, 0xd8,
0x56, 0x9b, 0xd3, 0x08, 0x2b, 0xac, 0xfb, 0x43, 0x07, 0x4a, 0x62, 0x8d, 0x36, 0x60, 0x25, 0xd1,
0xa6, 0x13, 0x90, 0x01, 0x95, 0xc6, 0xaa, 0xde, 0xbd, 0x82, 0x97, 0x93, 0xfd, 0x43, 0x32, 0xa0,
0xa8, 0x05, 0x88, 0xf6, 0xe9, 0x80, 0x06, 0xbc, 0xf3, 0x8c, 0x8e, 0x3a, 0x31, 0x67, 0x7e, 0xd0,
0x53, 0xe6, 0xb9, 0x7b, 0x05, 0xd7, 0x35, 0xed, 0xab, 0x74, 0xd4, 0x96, 0x14, 0xb4, 0x09, 0xab,
0x36, 0xde, 0x0f, 0xb8, 0x34, 0x59, 0x51, 0x70, 0x4e, 0xc1, 0x07, 0x01, 0x7f, 0x0f, 0x84, 0xa7,
0xfa, 0xb4, 0xcb, 0x43, 0xd6, 0xbc, 0x25, 0xd4, 0x0a, 0x23, 0xb7, 0x0a, 0x8b, 0x98, 0x7e, 0x38,
0xa4, 0x31, 0x77, 0xd7, 0xa1, 0x82, 0x69, 0x1c, 0x85, 0x41, 0x4c, 0xd1, 0x35, 0x28, 0xef, 0x31,
0x16, 0x32, 0xa5, 0x24, 0x56, 0x8b, 0xe6, 0x8f, 0x1c, 0xa8, 0x60, 0x72, 0xda, 0xe6, 0x84, 0xd3,
0x24, 0x34, 0x9c, 0x34, 0x34, 0xd0, 0x36, 0x2c, 0x1e, 0xf7, 0x09, 0x1f, 0x90, 0xa8, 0x51, 0x90,
0x46, 0x5a, 0xb7, 0x8c, 0x64, 0x4e, 0xb6, 0xbe, 0xa2, 0x20, 0x7b, 0x01, 0x67, 0x23, 0x6c, 0x0e,
0xb8, 0xdb, 0xb0, 0x64, 0x13, 0x50, 0x1d, 0x8a, 0xcf, 0xe8, 0x48, 0x2b, 0x20, 0x3e, 0x85, 0x52,
0x27, 0x22, 0x5e, 0x75, 0xac, 0xa8, 0xc5, 0x76, 0xe1, 0x1d, 0xa7, 0xf9, 0x8f, 0x32, 0x2c, 0xb4,
0xbb, 0x4f, 0xe9, 0x80, 0x88, 0x90, 0x3a, 0xa1, 0x2c, 0xf6, 0xb5, 0x66, 0x45, 0x6c, 0x96, 0xe8,
0x06, 0x94, 0x9f, 0xf4, 0xc3, 0xee, 0x33, 0x79, 0xbc, 0xb6, 0xf5, 0x31, 0x4b, 0x35, 0x75, 0xb6,
0xf5, 0x9e, 0x20, 0x63, 0x85, 0x72, 0x7f, 0xe1, 0x40, 0x59, 0x6e, 0xcc, 0x61, 0xf9, 0x25, 0x80,
0xc4, 0x79, 0xb1, 0xbe, 0xf2, 0xcb, 0xd3, 0x7c, 0x93, 0xf0, 0xc0, 0x16, 0x1c, 0xbd, 0x0b, 0x35,
0x29, 0xa9, 0xc3, 0x47, 0x11, 0x8d, 0x1b, 0xc5, 0xa9, 0xa8, 0xd2, 0xa7, 0x0f, 0x69, 0xcc, 0xa9,
0xa7, 0x74, 0x03, 0x79, 0xe2, 0x81, 0x38, 0xe0, 0xfe, 0xd1, 0x81, 0x6a, 0xc2, 0x59, 0xb8, 0x23,
0x8d, 0x2a, 0x2c, 0xbf, 0xc5, 0x9e, 0xe0, 0x6d, 0xb2, 0x57, 0x7c, 0xa3, 0x75, 0xa8, 0x79, 0x34,
0xee, 0x32, 0x3f, 0xe2, 0xe2, 0x42, 0x2a, 0xbb, 0xec, 0x2d, 0xe4, 0x42, 0x85, 0xd1, 0x0f, 0x87,
0x3e, 0xa3, 0x9e, 0xcc, 0xb0, 0x0a, 0x4e, 0xd6, 0x82, 0x16, 0x4a, 0x14, 0xe9, 0x37, 0xca, 0x8a,
0x66, 0xd6, 0x82, 0xd6, 0x0d, 0x07, 0xd1, 0x90, 0x53, 0xaf, 0xb1, 0xa0, 0x68, 0x66, 0x8d, 0x5e,
0x81, 0x6a, 0x4c, 0x83, 0xd8, 0xe7, 0xfe, 0x09, 0x6d, 0x2c, 0x4a, 0x62, 0xba, 0xe1, 0xfe, 0xba,
0x00, 0x35, 0xeb, 0x96, 0xe8, 0x65, 0xa8, 0x0a, 0x5d, 0xad, 0x34, 0xc1, 0x15, 0xb1, 0x21, 0xf3,
0xe3, 0xf9, 0xdc, 0x88, 0x76, 0x61, 0x31, 0xa0, 0x31, 0x17, 0x39, 0x54, 0x94, 0xd5, 0xe9, 0xb5,
0xb9, 0x16, 0x96, 0xdf, 0x7e, 0xd0, 0xbb, 0x1f, 0x7a, 0x14, 0x9b, 0x93, 0x42, 0xa1, 0x81, 0x1f,
0x74, 0x7c, 0x4e, 0x07, 0xb1, 0xb4, 0x49, 0x11, 0x57, 0x06, 0x7e, 0x70, 0x20, 0xd6, 0x92, 0x48,
0xce, 0x34, 0xb1, 0xac, 0x89, 0xe4, 0x4c, 0x12, 0x9b, 0xf7, 0xd5, 0xcd, 0x34, 0xc7, 0xf1, 0xd2,
0x03, 0xb0, 0xd0, 0x3e, 0x38, 0xdc, 0xbf, 0xb7, 0x57, 0x77, 0x50, 0x05, 0x4a, 0xf7, 0x0e, 0xda,
0x0f, 0xea, 0x05, 0xb4, 0x08, 0xc5, 0xf6, 0xde, 0x83, 0x7a, 0x51, 0x7c, 0xdc, 0xdf, 0x39, 0xaa,
0x97, 0x44, 0x89, 0xda, 0xc7, 0xef, 0x3f, 0x3c, 0xaa, 0x97, 0x9b, 0x3f, 0x29, 0xc1, 0xda, 0x3e,
0xe5, 0x47, 0x2c, 0x3c, 0xf1, 0x3d, 0xca, 0x94, 0xfe, 0x76, 0x12, 0xff, 0xab, 0x68, 0x65, 0xf1,
0x0d, 0xa8, 0x44, 0x1a, 0x29, 0xcd, 0x58, 0xdb, 0x5a, 0x9b, 0xba, 0x3c, 0x4e, 0x20, 0x88, 0x42,
0x9d, 0xd1, 0x38, 0x1c, 0xb2, 0x2e, 0xed, 0xc4, 0x92, 0x68, 0x62, 0x7a, 0xdb, 0x3a, 0x36, 0x25,
0xbe, 0x65, 0xe4, 0x89, 0x0f, 0x79, 0x5a, 0xed, 0xc7, 0x2a, 0xc1, 0x57, 0xd9, 0xf8, 0x2e, 0xea,
0xc3, 0x55, 0x8f, 0x70, 0xd2, 0x99, 0x90, 0xa4, 0xe2, 0xff, 0x76, 0x3e, 0x49, 0x77, 0x08, 0x27,
0xed, 0x69, 0x59, 0x6b, 0xde, 0xe4, 0x3e, 0x7a, 0x1b, 0x6a, 0x5e, 0xf2, 0x06, 0x09, 0xe7, 0x09,
0x29, 0x2f, 0x65, 0xbe, 0x50, 0xd8, 0x46, 0xba, 0x0f, 0xe1, 0x5a, 0xd6, 0x7d, 0x32, 0xea, 0xd2,
0x86, 0x5d, 0x97, 0x32, 0x6d, 0x9c, 0x96, 0x2a, 0xf7, 0x31, 0x5c, 0xcf, 0x56, 0xfe, 0x92, 0x8c,
0x9b, 0x7f, 0x76, 0xe0, 0xa5, 0x23, 0x46, 0x23, 0xc2, 0xa8, 0xb1, 0xda, 0x6e, 0x18, 0x1c, 0xfb,
0x3d, 0x77, 0x3b, 0x09, 0x0f, 0x74, 0x13, 0x16, 0xba, 0x72, 0x53, 0xc7, 0x83, 0x9d, 0x3d, 0x76,
0x4b, 0x80, 0x35, 0xcc, 0xfd, 0xae, 0x63, 0xc5, 0xd3, 0x97, 0x61, 0x35, 0x52, 0x12, 0xbc, 0x4e,
0x3e, 0x36, 0x2b, 0x06, 0xaf, 0x54, 0x99, 0xf4, 0x46, 0x21, 0xaf, 0x37, 0x9a, 0xdf, 0x2f, 0xc0,
0xb5, 0x87, 0x51, 0x8f, 0x11, 0x8f, 0x26, 0x5e, 0x11, 0x8f, 0x89, 0xcb, 0xd2, 0xcb, 0xcd, 0x2d,
0x1b, 0x56, 0x11, 0x2f, 0x8c, 0x17, 0xf1, 0x37, 0xa1, 0xca, 0xc8, 0x69, 0x27, 0x16, 0xec, 0x64,
0x8d, 0xa8, 0x6d, 0x5d, 0xcd, 0x78, 0xb6, 0x70, 0x85, 0xe9, 0x2f, 0xf7, 0x3b, 0xb6, 0x51, 0xde,
0x85, 0x95, 0xa1, 0x52, 0xcc, 0xd3, 0x3c, 0xce, 0xb1, 0xc9, 0xb2, 0x81, 0xab, 0x77, 0xf4, 0xc2,
0x26, 0xf9, 0xbd, 0x03, 0xee, 0x23, 0xd2, 0xf7, 0x3d, 0xa1, 0x9c, 0xb6, 0x89, 0x78, 0x19, 0xb4,
0xd7, 0x1f, 0xe7, 0x34, 0x4c, 0x1a, 0x12, 0x85, 0x7c, 0x21, 0xb1, 0x6b, 0x5d, 0x7e, 0x42, 0x79,
0x27, 0xb7, 0xf2, 0xbf, 0x75, 0xa0, 0x61, 0x94, 0x4f, 0xf3, 0xe1, 0xff, 0x42, 0xf5, 0xdf, 0x39,
0x50, 0x55, 0x8a, 0x0e, 0x19, 0x75, 0x7b, 0xa9, 0xae, 0xaf, 0xc3, 0x1a, 0xa7, 0x8c, 0x91, 0xe3,
0x90, 0x0d, 0x3a, 0x76, 0xc7, 0x50, 0xc5, 0xf5, 0x84, 0xf0, 0x48, 0x47, 0xdd, 0xff, 0x46, 0xf7,
0x5f, 0x15, 0x60, 0x09, 0x53, 0xe2, 0x99, 0x78, 0x71, 0xbf, 0x9d, 0xd3, 0xd4, 0xb7, 0x61, 0xb9,
0x3b, 0x64, 0x4c, 0x74, 0x99, 0x2a, 0xc8, 0xcf, 0xd1, 0x7a, 0x49, 0xa3, 0x55, 0x8c, 0x37, 0x60,
0x31, 0x62, 0xfe, 0x89, 0x49, 0xb0, 0x25, 0x6c, 0x96, 0xee, 0x0f, 0xec, 0x54, 0xfa, 0x3c, 0x54,
0x03, 0x7a, 0x9a, 0x2f, 0x8b, 0x2a, 0x01, 0x3d, 0xbd, 0x5c, 0x02, 0xcd, 0xd6, 0xaa, 0xf9, 0x9b,
0x12, 0xa0, 0xa3, 0x3e, 0x09, 0x8c, 0x99, 0x76, 0x9f, 0x92, 0xa0, 0x47, 0xdd, 0xff, 0x38, 0x39,
0xad, 0xf5, 0x0e, 0xd4, 0x22, 0xe6, 0x87, 0x2c, 0x9f, 0xad, 0x40, 0x62, 0xd5, 0x65, 0xf6, 0x00,
0x45, 0x2c, 0x8c, 0xc2, 0x98, 0x7a, 0x9d, 0xd4, 0x16, 0xc5, 0xf9, 0x0c, 0xea, 0xe6, 0xc8, 0xa1,
0xb1, 0x49, 0x1a, 0x5d, 0xa5, 0x5c, 0xd1, 0x85, 0x3e, 0x0d, 0xcb, 0x4a, 0x63, 0x63, 0x91, 0xb2,
0xb4, 0xc8, 0x92, 0xdc, 0x3c, 0xd2, 0xce, 0xfa, 0x79, 0xc1, 0x72, 0xd6, 0x6d, 0x58, 0x8e, 0xfa,
0x24, 0x08, 0xf2, 0x96, 0xbd, 0x25, 0x8d, 0x56, 0x0a, 0xee, 0x8a, 0x5e, 0x43, 0x36, 0x95, 0x71,
0x87, 0xd1, 0xa8, 0x4f, 0xba, 0x54, 0x7b, 0x6e, 0xf6, 0x38, 0xb7, 0x6a, 0x4e, 0x60, 0x75, 0x00,
0x6d, 0xc0, 0xaa, 0x51, 0x61, 0xdc, 0x91, 0x2b, 0x7a, 0x5b, 0x2b, 0x7e, 0xe1, 0x26, 0x00, 0xbd,
0x01, 0xa8, 0x4f, 0x7b, 0xa4, 0x3b, 0x92, 0x4d, 0x7a, 0x27, 0x1e, 0xc5, 0x9c, 0x0e, 0x74, 0xe7,
0x5b, 0x57, 0x14, 0x51, 0x72, 0xdb, 0x72, 0xbf, 0xf9, 0xa7, 0x22, 0x5c, 0xdd, 0x89, 0xa2, 0xfe,
0x68, 0x22, 0x6e, 0xfe, 0xfd, 0xe2, 0xe3, 0x66, 0xca, 0x1b, 0xc5, 0xe7, 0xf1, 0xc6, 0x73, 0x87,
0x4b, 0x86, 0xe5, 0xcb, 0x59, 0x96, 0x77, 0xff, 0x70, 0xf9, 0xfc, 0xb6, 0xd2, 0xb4, 0x30, 0x96,
0xa6, 0x93, 0x6e, 0x2d, 0x5e, 0xd2, 0xad, 0xa5, 0x19, 0x6e, 0xfd, 0x67, 0x01, 0xae, 0x1e, 0x0c,
0xa2, 0x90, 0xf1, 0xf1, 0xd6, 0xe3, 0xad, 0x9c, 0x5e, 0x5d, 0x81, 0x82, 0xef, 0xe9, 0xa1, 0xb5,
0xe0, 0x7b, 0xee, 0x19, 0xd4, 0x15, 0x3b, 0x9a, 0xd4, 0xe1, 0x73, 0x47, 0x9e, 0x5c, 0x01, 0xa1,
0x50, 0x73, 0xaa, 0xed, 0x2f, 0x6d, 0x6f, 0x7c, 0x00, 0xc8, 0xd7, 0x6a, 0x74, 0x4c, 0x8f, 0x6e,
0xde, 0x92, 0x9b, 0x96, 0x88, 0x8c, 0xab, 0xb7, 0x26, 0xf5, 0xc7, 0x6b, 0xfe, 0xc4, 0x4e, 0x7c,
0xf1, 0xc6, 0xe6, 0xaf, 0x0e, 0xac, 0x88, 0x47, 0x2a, 0xed, 0x0b, 0x5e, 0x5c, 0x47, 0xc0, 0xc6,
0xc6, 0xa5, 0x72, 0xae, 0xd0, 0xd4, 0x66, 0xbe, 0xf0, 0xfd, 0x7e, 0xea, 0xc0, 0x35, 0x33, 0xdb,
0x88, 0x5e, 0x20, 0x6b, 0x8e, 0x3b, 0xb3, 0xf4, 0xba, 0x25, 0xaa, 0x42, 0x82, 0x9d, 0x3d, 0xc9,
0xd9, 0xa8, 0x8b, 0x6b, 0xf7, 0x33, 0x07, 0x3e, 0x6e, 0x3a, 0x33, 0x4b, 0xc5, 0x8f, 0x60, 0x96,
0xf8, 0x48, 0x3a, 0x98, 0xbf, 0x3b, 0xb0, 0x96, 0xa8, 0x95, 0xb4, 0x31, 0xf1, 0xc5, 0xd5, 0x42,
0x6f, 0x03, 0x74, 0xc3, 0x20, 0xa0, 0x5d, 0x6e, 0x86, 0x83, 0x79, 0x35, 0x37, 0x85, 0xba, 0xdf,
0xb0, 0xee, 0x73, 0x1d, 0x16, 0xc2, 0x21, 0x8f, 0x86, 0x5c, 0x87, 0xa4, 0x5e, 0x5d, 0xd8, 0x0d,
0x5b, 0x3f, 0xae, 0x42, 0xc5, 0xcc, 0x71, 0xe8, 0xeb, 0x50, 0xdd, 0xa7, 0x5c, 0xff, 0xc2, 0xf5,
0x99, 0x73, 0x46, 0x64, 0x15, 0x40, 0x9f, 0xcd, 0x35, 0x48, 0xa3, 0xfe, 0x8c, 0xa1, 0x11, 0x6d,
0x5a, 0xe7, 0x33, 0x11, 0x89, 0xa4, 0xd7, 0x72, 0x20, 0xb5, 0xb4, 0x6f, 0xcd, 0x9b, 0x58, 0xd0,
0x0d, 0x8b, 0xd1, 0x6c, 0x58, 0x22, 0xb7, 0x95, 0x17, 0xae, 0x85, 0x0f, 0x67, 0x4f, 0x1c, 0xe8,
0xf5, 0x0c, 0x5e, 0x93, 0xa0, 0x44, 0xf0, 0x1b, 0xf9, 0xc0, 0x5a, 0xac, 0x9f, 0x3d, 0xb8, 0xa2,
0x0d, 0x8b, 0x4b, 0x16, 0x20, 0x11, 0xb7, 0x79, 0x3e, 0x50, 0x8b, 0xba, 0x6b, 0x0d, 0x26, 0xe8,
0x15, 0xeb, 0x58, 0xb2, 0x9b, 0x30, 0x7d, 0x75, 0x06, 0x55, 0x73, 0xfa, 0xda, 0xf8, 0x98, 0x80,
0x3e, 0x69, 0x0f, 0xc4, 0x16, 0x21, 0xe1, 0xb7, 0x3e, 0x1b, 0xa0, 0x59, 0x76, 0xb3, 0x5a, 0x6a,
0x64, 0x87, 0xe9, 0x34, 0x39, 0x61, 0xff, 0xb9, 0xf3, 0x60, 0x5a, 0xc8, 0x71, 0x66, 0x03, 0x86,
0xec, 0xe3, 0x19, 0xf4, 0x44, 0xcc, 0xc6, 0xb9, 0xb8, 0x54, 0x4e, 0xc6, 0xb3, 0x38, 0x26, 0x27,
0xeb, 0xd9, 0xcc, 0x92, 0x93, 0x8d, 0xd3, 0x72, 0x1e, 0x4f, 0xbe, 0x84, 0xe8, 0x53, 0x13, 0x86,
0x4e, 0x49, 0x09, 0xf7, 0xe6, 0x3c, 0x88, 0x66, 0xfc, 0x45, 0xf5, 0xfb, 0x3f, 0x1a, 0xfb, 0xf9,
0x94, 0x87, 0x51, 0xc2, 0xa4, 0x31, 0x4d, 0x50, 0x47, 0xb7, 0xbe, 0x57, 0x84, 0x9a, 0xf5, 0x30,
0xa0, 0x0f, 0xec, 0xe2, 0xb4, 0x91, 0x51, 0x76, 0xec, 0x37, 0x2e, 0x33, 0xaa, 0x67, 0x00, 0xb5,
0xaa, 0x67, 0x73, 0xde, 0x23, 0x94, 0x95, 0x8b, 0x53, 0xa8, 0x44, 0xe8, 0x8d, 0x9c, 0x68, 0x2d,
0xf9, 0x49, 0xc6, 0x53, 0x33, 0x56, 0x7e, 0xa7, 0xa8, 0x99, 0xe5, 0x37, 0x0b, 0xa5, 0x24, 0xbc,
0xe9, 0x5c, 0xc2, 0x11, 0x4f, 0x16, 0xe4, 0x1f, 0x7b, 0xb7, 0xfe, 0x1b, 0x00, 0x00, 0xff, 0xff,
0x8a, 0x61, 0xfa, 0xcc, 0xeb, 0x1b, 0x00, 0x00,
// 2010 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xcd, 0x6f, 0x23, 0x49,
0x15, 0x9f, 0x6e, 0xdb, 0x89, 0xfd, 0xec, 0x49, 0x3a, 0x35, 0x1f, 0x98, 0xde, 0x0f, 0x82, 0x61,
0x49, 0x96, 0xdd, 0xf1, 0xac, 0x32, 0x30, 0xbb, 0x84, 0xd1, 0x6a, 0xb3, 0x49, 0xc8, 0x44, 0x33,
0xf1, 0x84, 0xf2, 0xcc, 0x04, 0x09, 0x69, 0xad, 0x1a, 0x77, 0xc5, 0xd3, 0xc4, 0xee, 0xee, 0xad,
0x2e, 0x67, 0x62, 0x71, 0x44, 0x70, 0x46, 0xa0, 0x85, 0x03, 0x70, 0x81, 0x03, 0xe2, 0xc4, 0x0d,
0xf1, 0x75, 0xe1, 0xce, 0x81, 0x3b, 0xdc, 0x56, 0x1c, 0xb9, 0xf0, 0x17, 0xa0, 0xaa, 0xae, 0xee,
0x2e, 0xdb, 0xed, 0xa4, 0x93, 0xec, 0x0a, 0xed, 0xad, 0xeb, 0xbd, 0x5f, 0xbd, 0xf7, 0xea, 0xbd,
0x5f, 0xbd, 0xaa, 0xb2, 0x61, 0x91, 0x1f, 0x06, 0xfd, 0x61, 0xcf, 0xf5, 0xbe, 0xde, 0x0c, 0x98,
0xcf, 0x7d, 0x54, 0x49, 0x04, 0x8d, 0x7b, 0x50, 0xdb, 0x1a, 0x79, 0x64, 0xe0, 0x76, 0x9f, 0x92,
0xfe, 0x90, 0xa2, 0x3a, 0xcc, 0x0f, 0xc2, 0x5e, 0x40, 0xba, 0x47, 0x75, 0x63, 0xd9, 0x58, 0xad,
0xe1, 0x78, 0x88, 0x10, 0x14, 0xbf, 0x17, 0xfa, 0x5e, 0xdd, 0x94, 0x62, 0xf9, 0xdd, 0xf8, 0xd8,
0x00, 0xd8, 0x72, 0x49, 0xcf, 0xf3, 0x43, 0xee, 0x76, 0xd1, 0x3a, 0x94, 0x43, 0x7a, 0x4c, 0x99,
0xcb, 0x47, 0x72, 0xf6, 0xc2, 0xda, 0xab, 0xcd, 0xd4, 0x77, 0x0a, 0x6c, 0xb6, 0x15, 0x0a, 0x27,
0x78, 0xe1, 0x38, 0x1c, 0x0e, 0x06, 0x84, 0x8d, 0xa4, 0x87, 0x0a, 0x8e, 0x87, 0xe8, 0x26, 0xcc,
0x39, 0x94, 0x13, 0xb7, 0x5f, 0x2f, 0x48, 0x85, 0x1a, 0xa1, 0xbb, 0x50, 0x21, 0x9c, 0x33, 0xf7,
0xd9, 0x90, 0xd3, 0x7a, 0x71, 0xd9, 0x58, 0xad, 0xae, 0xd5, 0x35, 0x77, 0x1b, 0xb1, 0x6e, 0x9f,
0xf0, 0xe7, 0x38, 0x85, 0x36, 0x6e, 0x43, 0x39, 0xf6, 0x8f, 0xaa, 0x30, 0xbf, 0xdb, 0x7a, 0xba,
0xf1, 0x70, 0x77, 0xcb, 0xba, 0x82, 0x2a, 0x50, 0xda, 0xc6, 0xf8, 0x11, 0xb6, 0x0c, 0x21, 0x3f,
0xd8, 0xc0, 0xad, 0xdd, 0xd6, 0x8e, 0x65, 0x36, 0xfe, 0x65, 0xc0, 0xd5, 0x31, 0x6b, 0xe8, 0x0e,
0x94, 0x42, 0x4e, 0x83, 0xb0, 0x6e, 0x2c, 0x17, 0x56, 0xab, 0x6b, 0xaf, 0xcc, 0x72, 0xdb, 0x6c,
0x73, 0x1a, 0xe0, 0x08, 0x6b, 0x7f, 0x64, 0x40, 0x51, 0x8c, 0xd1, 0x0a, 0x2c, 0x24, 0xd1, 0x74,
0x3c, 0x32, 0xa0, 0x32, 0x59, 0x95, 0xfb, 0x57, 0xf0, 0xd5, 0x44, 0xde, 0x22, 0x03, 0x8a, 0x9a,
0x80, 0x68, 0x9f, 0x0e, 0xa8, 0xc7, 0x3b, 0x47, 0x74, 0xd4, 0x09, 0x39, 0x73, 0xbd, 0x5e, 0x94,
0x9e, 0xfb, 0x57, 0xb0, 0xa5, 0x74, 0x0f, 0xe8, 0xa8, 0x2d, 0x35, 0x68, 0x15, 0x16, 0x75, 0xbc,
0xeb, 0x71, 0x99, 0xb2, 0x82, 0xb0, 0x9c, 0x82, 0x77, 0x3d, 0xfe, 0x3e, 0x88, 0x4a, 0xf5, 0x69,
0x97, 0xfb, 0xac, 0x71, 0x47, 0x84, 0xe5, 0x07, 0x76, 0x05, 0xe6, 0x31, 0xfd, 0x70, 0x48, 0x43,
0x6e, 0x2f, 0x43, 0x19, 0xd3, 0x30, 0xf0, 0xbd, 0x90, 0xa2, 0xeb, 0x50, 0xda, 0x66, 0xcc, 0x67,
0x51, 0x90, 0x38, 0x1a, 0x34, 0x7e, 0x66, 0x40, 0x19, 0x93, 0x17, 0x6d, 0x4e, 0x38, 0x4d, 0xa8,
0x61, 0xa4, 0xd4, 0x40, 0xeb, 0x30, 0x7f, 0xd8, 0x27, 0x7c, 0x40, 0x82, 0xba, 0x29, 0x93, 0xb4,
0xac, 0x25, 0x29, 0x9e, 0xd9, 0xfc, 0x56, 0x04, 0xd9, 0xf6, 0x38, 0x1b, 0xe1, 0x78, 0x82, 0xbd,
0x0e, 0x35, 0x5d, 0x81, 0x2c, 0x28, 0x1c, 0xd1, 0x91, 0x0a, 0x40, 0x7c, 0x8a, 0xa0, 0x8e, 0x05,
0x5f, 0x15, 0x57, 0xa2, 0xc1, 0xba, 0xf9, 0x8e, 0xd1, 0xf8, 0xfb, 0x3c, 0xcc, 0xb5, 0xbb, 0xcf,
0xe9, 0x80, 0x08, 0x4a, 0x1d, 0x53, 0x16, 0xba, 0x2a, 0xb2, 0x02, 0x8e, 0x87, 0xe8, 0x16, 0x94,
0x9e, 0xf5, 0xfd, 0xee, 0x91, 0x9c, 0x5e, 0x5d, 0xfb, 0x9c, 0x16, 0x5a, 0x34, 0xb7, 0xf9, 0xbe,
0x50, 0xe3, 0x08, 0x65, 0xff, 0xda, 0x84, 0x92, 0x14, 0x9c, 0x62, 0xf2, 0x9b, 0x00, 0x49, 0xf1,
0x42, 0xb5, 0xe4, 0x97, 0xa6, 0xed, 0x26, 0xf4, 0xc0, 0x1a, 0x1c, 0xbd, 0x0b, 0x55, 0xe9, 0xa9,
0xc3, 0x47, 0x01, 0x0d, 0xeb, 0x85, 0x29, 0x56, 0xa9, 0xd9, 0x2d, 0x1a, 0x72, 0xea, 0x44, 0xb1,
0x81, 0x9c, 0xf1, 0x58, 0x4c, 0x40, 0xcb, 0x50, 0x75, 0x68, 0xd8, 0x65, 0x6e, 0xc0, 0x45, 0x68,
0x45, 0x99, 0x14, 0x5d, 0x84, 0xde, 0x03, 0x4b, 0x1b, 0x76, 0x8e, 0x5c, 0xcf, 0xa9, 0x97, 0xe4,
0x16, 0xbd, 0xa1, 0xbb, 0x91, 0x3c, 0x7a, 0xe0, 0x7a, 0x0e, 0x5e, 0xd4, 0xe0, 0x42, 0x80, 0x5e,
0x05, 0x70, 0x68, 0xc0, 0x68, 0x97, 0x70, 0xea, 0xd4, 0xe7, 0x96, 0x8d, 0xd5, 0x32, 0xd6, 0x24,
0xf6, 0xef, 0x4c, 0xa8, 0x24, 0xab, 0x13, 0x94, 0x48, 0x99, 0x8d, 0xe5, 0xb7, 0x90, 0x89, 0xf5,
0xc5, 0x1d, 0x44, 0x7c, 0x4f, 0x46, 0x5e, 0x98, 0x8e, 0xdc, 0x86, 0x32, 0xa3, 0x1f, 0x0e, 0x5d,
0x46, 0x1d, 0xb9, 0xb0, 0x32, 0x4e, 0xc6, 0x42, 0xe7, 0x4b, 0x14, 0xe9, 0xcb, 0xd5, 0x94, 0x71,
0x32, 0x16, 0xba, 0xae, 0x3f, 0x08, 0x86, 0x69, 0xb4, 0xc9, 0x18, 0xbd, 0x0c, 0x95, 0x90, 0x7a,
0xa1, 0xcb, 0xdd, 0x63, 0x5a, 0x9f, 0x97, 0xca, 0x54, 0x90, 0x99, 0xab, 0xf2, 0x25, 0x72, 0x55,
0x99, 0xca, 0xd5, 0x6f, 0x4d, 0xa8, 0x6a, 0xb5, 0x44, 0x2f, 0x41, 0x45, 0x64, 0x43, 0x6b, 0x06,
0xb8, 0x2c, 0x04, 0xb2, 0x0b, 0x9c, 0x8f, 0xac, 0x68, 0x13, 0xe6, 0x3d, 0x1a, 0x72, 0xd1, 0x29,
0x0a, 0x32, 0xe8, 0xd7, 0x4f, 0xe5, 0x91, 0xfc, 0x76, 0xbd, 0xde, 0x9e, 0xef, 0x50, 0x1c, 0xcf,
0x14, 0x01, 0x0d, 0x5c, 0xaf, 0xe3, 0x72, 0x3a, 0x08, 0x65, 0xd6, 0x0b, 0xb8, 0x3c, 0x70, 0xbd,
0x5d, 0x31, 0x96, 0x4a, 0x72, 0xa2, 0x94, 0x25, 0xa5, 0x24, 0x27, 0x52, 0xd9, 0xd8, 0x8b, 0x56,
0xa6, 0x2c, 0x8e, 0x37, 0x58, 0x80, 0xb9, 0xf6, 0x6e, 0x6b, 0xe7, 0xe1, 0xb6, 0x65, 0xa0, 0x32,
0x14, 0x1f, 0xee, 0xb6, 0x1f, 0x5b, 0x26, 0x9a, 0x87, 0x42, 0x7b, 0xfb, 0xb1, 0x55, 0x10, 0x1f,
0x7b, 0x1b, 0xfb, 0x56, 0x51, 0x34, 0xe2, 0x1d, 0xfc, 0xe8, 0xc9, 0xbe, 0x55, 0x6a, 0xfc, 0xa3,
0x08, 0x4b, 0x3b, 0x94, 0xef, 0x33, 0xff, 0xd8, 0x75, 0x28, 0x8b, 0xe2, 0xd7, 0x5b, 0xd5, 0xef,
0x8b, 0x5a, 0xaf, 0xba, 0x05, 0xe5, 0x40, 0x21, 0x65, 0x1a, 0xab, 0x6b, 0x4b, 0x53, 0x8b, 0xc7,
0x09, 0x04, 0x51, 0xb0, 0x18, 0x0d, 0xfd, 0x21, 0xeb, 0xd2, 0x4e, 0x28, 0x95, 0xf1, 0xce, 0x5d,
0xd7, 0xa6, 0x4d, 0xb9, 0x6f, 0xc6, 0xfe, 0xc4, 0x87, 0x9c, 0x1d, 0xc9, 0xc3, 0xa8, 0x8d, 0x2d,
0xb2, 0x71, 0x29, 0xea, 0xc3, 0x35, 0x87, 0x70, 0xd2, 0x99, 0xf0, 0x14, 0xed, 0xf2, 0x7b, 0xf9,
0x3c, 0x6d, 0x11, 0x4e, 0xda, 0xd3, 0xbe, 0x96, 0x9c, 0x49, 0x39, 0x7a, 0x1b, 0xaa, 0x4e, 0x72,
0xd2, 0x8a, 0xe2, 0x09, 0x2f, 0x37, 0x32, 0xcf, 0x61, 0xac, 0x23, 0xd1, 0x5d, 0xb8, 0x1a, 0x67,
0xa6, 0x33, 0xa0, 0x9c, 0xc8, 0xd2, 0x66, 0x66, 0xb0, 0x16, 0xe3, 0xf6, 0x28, 0x27, 0xf6, 0x13,
0xb8, 0x9e, 0x95, 0x87, 0x8c, 0xae, 0xbd, 0xa2, 0x77, 0xed, 0x4c, 0xcb, 0x69, 0x23, 0xb7, 0x0f,
0xe0, 0x66, 0xf6, 0xa2, 0x2f, 0x69, 0xb8, 0xf1, 0x4f, 0x03, 0x6e, 0xec, 0x33, 0x1a, 0x10, 0x46,
0xe3, 0x6c, 0x6f, 0xfa, 0xde, 0xa1, 0xdb, 0xb3, 0xd7, 0x13, 0x5a, 0xa1, 0xdb, 0x30, 0xd7, 0x95,
0x42, 0xc5, 0x23, 0x7d, 0xd7, 0xe9, 0x17, 0x26, 0xac, 0x60, 0xf6, 0x0f, 0x0d, 0x8d, 0x87, 0xef,
0xc1, 0x62, 0x10, 0x79, 0x70, 0x3a, 0xf9, 0xcc, 0x2c, 0xc4, 0xf8, 0x28, 0x94, 0xc9, 0x2a, 0x9a,
0x79, 0xab, 0xd8, 0xf8, 0xb1, 0x09, 0xd7, 0x9f, 0x04, 0x3d, 0x46, 0x1c, 0x9a, 0x54, 0x45, 0x1c,
0xb5, 0x36, 0x4b, 0x17, 0x77, 0x6a, 0xbb, 0xd1, 0x8e, 0x38, 0x73, 0xfc, 0x88, 0x7b, 0x0b, 0x2a,
0x8c, 0xbc, 0xe8, 0x84, 0xc2, 0x9c, 0xec, 0x2d, 0xd5, 0xb5, 0x6b, 0x19, 0x87, 0x3a, 0x2e, 0x33,
0xf5, 0x65, 0xff, 0x40, 0x4f, 0xca, 0xbb, 0xb0, 0x30, 0x8c, 0x02, 0x73, 0x94, 0x8d, 0x33, 0x72,
0x72, 0x35, 0x86, 0x47, 0xb7, 0x8c, 0x0b, 0xa7, 0xe4, 0xcf, 0x06, 0xd8, 0x4f, 0x49, 0xdf, 0x75,
0x44, 0x70, 0x2a, 0x27, 0xe2, 0xdc, 0x54, 0x55, 0x3f, 0xc8, 0x99, 0x98, 0x94, 0x12, 0x66, 0x3e,
0x4a, 0x6c, 0x6a, 0x8b, 0x9f, 0x08, 0xde, 0xc8, 0x1d, 0xfc, 0x1f, 0x0d, 0xa8, 0xc7, 0xc1, 0xa7,
0xfb, 0xe1, 0x33, 0x11, 0xfa, 0x9f, 0x0c, 0xa8, 0x44, 0x81, 0x0e, 0x19, 0xb5, 0x7b, 0x69, 0xac,
0x6f, 0xc0, 0x12, 0xa7, 0x8c, 0x91, 0x43, 0x9f, 0x0d, 0x3a, 0xfa, 0x7d, 0xaa, 0x82, 0xad, 0x44,
0xf1, 0x54, 0xb1, 0xee, 0xff, 0x13, 0xfb, 0xc7, 0x26, 0xd4, 0x30, 0x25, 0x4e, 0xcc, 0x17, 0xfb,
0xaf, 0x46, 0xce, 0x5c, 0xdf, 0x83, 0xab, 0xdd, 0x21, 0x63, 0xe2, 0x12, 0x1e, 0xb1, 0xfc, 0x8c,
0xb0, 0x6b, 0x0a, 0x1d, 0x91, 0xbc, 0x0e, 0xf3, 0x01, 0x73, 0x8f, 0xe3, 0x1d, 0x56, 0xc3, 0xf1,
0x50, 0xd8, 0x1d, 0x6f, 0xcf, 0xc5, 0x33, 0xec, 0x8e, 0x35, 0xe9, 0x9f, 0xea, 0x3b, 0xf1, 0x6b,
0x50, 0xf1, 0xe8, 0x8b, 0x7c, 0x9b, 0xb0, 0xec, 0xd1, 0x17, 0x97, 0xdb, 0x7f, 0xb3, 0xd7, 0xd4,
0xf8, 0x6f, 0x11, 0xd0, 0x7e, 0x9f, 0x78, 0x71, 0x96, 0x37, 0x9f, 0x13, 0xaf, 0x47, 0xed, 0xbf,
0x98, 0x39, 0x73, 0xfd, 0x0e, 0x54, 0x03, 0xe6, 0xfa, 0x2c, 0x5f, 0xa6, 0x41, 0x62, 0xa3, 0xc5,
0x6c, 0x03, 0x0a, 0x98, 0x1f, 0xf8, 0x21, 0x75, 0x3a, 0x69, 0x2e, 0x0a, 0xa7, 0x1b, 0xb0, 0xe2,
0x29, 0xad, 0x38, 0x27, 0x29, 0x39, 0x8b, 0xb9, 0xc8, 0x89, 0xbe, 0x24, 0xaa, 0x28, 0x22, 0x8e,
0x33, 0x52, 0x92, 0x19, 0xa9, 0x49, 0xe1, 0xfe, 0xac, 0x52, 0xcf, 0x9d, 0xa7, 0xd4, 0xbf, 0x32,
0xb5, 0x52, 0x0b, 0x53, 0x7d, 0xe2, 0x79, 0x79, 0x7b, 0x6e, 0x4d, 0xa1, 0xa3, 0xe5, 0x6d, 0x8a,
0x0b, 0x92, 0xbc, 0x6b, 0x87, 0x1d, 0x46, 0x83, 0x3e, 0xe9, 0x52, 0x55, 0xf7, 0xd9, 0x2f, 0xed,
0xc5, 0x78, 0x06, 0x8e, 0x26, 0xa0, 0x15, 0x58, 0x8c, 0x43, 0x18, 0xa7, 0xc1, 0x82, 0x12, 0xc7,
0xcb, 0xbe, 0xf0, 0xcd, 0xe5, 0x4d, 0x40, 0x7d, 0xda, 0x23, 0xdd, 0x91, 0x7c, 0x3f, 0x75, 0xc2,
0x51, 0xc8, 0xe9, 0x40, 0x3d, 0x08, 0xac, 0x48, 0x23, 0xfa, 0x7d, 0x5b, 0xca, 0x1b, 0x3f, 0x29,
0xc2, 0xb5, 0x8d, 0x20, 0xe8, 0x8f, 0x26, 0x58, 0xf7, 0x87, 0x4f, 0x9f, 0x75, 0x53, 0xd5, 0x28,
0x9c, 0xa7, 0x1a, 0xe7, 0x26, 0x5b, 0x46, 0xe6, 0x4b, 0x99, 0x99, 0xbf, 0x1c, 0xe1, 0xfe, 0x76,
0xf9, 0xde, 0xa2, 0xb5, 0x08, 0x73, 0xbc, 0xed, 0x4d, 0x90, 0xa2, 0x70, 0x49, 0x52, 0x14, 0x67,
0x90, 0xe2, 0x3f, 0x26, 0x5c, 0xdb, 0x1d, 0x04, 0x3e, 0xe3, 0xe3, 0xb7, 0xa6, 0xbb, 0x39, 0x39,
0xb1, 0x00, 0xa6, 0xeb, 0xa8, 0x5f, 0x23, 0x4c, 0xd7, 0xb1, 0x4f, 0xc0, 0x8a, 0xcc, 0xd1, 0xe4,
0x08, 0x39, 0xf3, 0x95, 0x97, 0x8b, 0x4e, 0x11, 0x6a, 0x76, 0x4f, 0xb5, 0x7f, 0xa3, 0x57, 0xe3,
0x03, 0x40, 0xae, 0x0a, 0xa3, 0x13, 0x3f, 0x4b, 0xe2, 0x63, 0xf0, 0xb6, 0xe6, 0x22, 0x63, 0xe9,
0xcd, 0xc9, 0xf8, 0xf1, 0x92, 0x3b, 0x21, 0x09, 0x2f, 0x7e, 0x27, 0xfb, 0xa5, 0x09, 0x0b, 0xe2,
0x7c, 0x4d, 0xaf, 0x34, 0xf6, 0x47, 0xc6, 0xa7, 0x74, 0x9b, 0x99, 0xa6, 0x77, 0xe1, 0x3c, 0xf4,
0x66, 0x63, 0x0f, 0xcc, 0x52, 0x2e, 0x66, 0xab, 0x2a, 0x5d, 0x38, 0x3d, 0xbf, 0x30, 0xe0, 0x7a,
0xfc, 0x1a, 0x14, 0xb7, 0xa0, 0xac, 0x97, 0xef, 0x89, 0x16, 0xd7, 0x1d, 0xd1, 0x92, 0x12, 0xec,
0xec, 0xb7, 0xaf, 0x8e, 0xba, 0x44, 0xf1, 0x0c, 0xf8, 0x7c, 0x7c, 0x27, 0xd5, 0x42, 0xfc, 0x04,
0x5e, 0x51, 0x9f, 0xc8, 0xdd, 0xed, 0xdf, 0x06, 0x2c, 0x25, 0x61, 0x25, 0x17, 0xb8, 0xf0, 0xe2,
0x61, 0xa1, 0xb7, 0x01, 0xba, 0xbe, 0xe7, 0xd1, 0x2e, 0x8f, 0x9f, 0x45, 0xa7, 0x35, 0xfc, 0x14,
0x6a, 0x7f, 0x57, 0x5b, 0xcf, 0x4d, 0x98, 0xf3, 0x87, 0x3c, 0x18, 0x72, 0x45, 0x68, 0x35, 0xba,
0x70, 0x19, 0xbe, 0xfa, 0x1a, 0x40, 0xfa, 0x23, 0x14, 0xaa, 0x40, 0x69, 0xff, 0xe1, 0xc6, 0x6e,
0xcb, 0xba, 0x82, 0x6a, 0x50, 0xde, 0xdb, 0xc0, 0x0f, 0xb6, 0x1e, 0x1d, 0xb4, 0x2c, 0x63, 0xed,
0xe7, 0x15, 0x28, 0xc7, 0x0f, 0x5d, 0xf4, 0x1d, 0xa8, 0xec, 0x50, 0xae, 0x7e, 0x20, 0xfd, 0xf2,
0x19, 0xbf, 0x3d, 0x44, 0x3c, 0x7b, 0x2d, 0xd7, 0x2f, 0x14, 0xa8, 0x3f, 0xe3, 0x55, 0x8d, 0x56,
0xb5, 0xf9, 0x99, 0x88, 0xc4, 0xd3, 0xeb, 0x39, 0x90, 0xca, 0xdb, 0xf7, 0x4f, 0x7b, 0xd2, 0xa1,
0x5b, 0x9a, 0xa1, 0xd9, 0xb0, 0xc4, 0x6f, 0x33, 0x2f, 0x5c, 0x39, 0x1f, 0xce, 0x7e, 0x92, 0xa1,
0x37, 0x32, 0x6c, 0x4d, 0x82, 0x12, 0xc7, 0x6f, 0xe6, 0x03, 0x2b, 0xb7, 0x6e, 0xf6, 0xcb, 0x1e,
0xad, 0x68, 0x56, 0xb2, 0x00, 0x89, 0xbb, 0xd5, 0xb3, 0x81, 0xca, 0xd5, 0x7d, 0xed, 0xe5, 0x86,
0x5e, 0xd6, 0xa6, 0x25, 0xd2, 0xc4, 0xe8, 0x2b, 0x33, 0xb4, 0xca, 0xd2, 0xb7, 0xc7, 0xdf, 0x51,
0xe8, 0x0b, 0xfa, 0x2f, 0x06, 0x9a, 0x22, 0xb1, 0xb7, 0x3c, 0x1b, 0xa0, 0x4c, 0x76, 0xb3, 0x1e,
0x0d, 0x48, 0xa7, 0xe9, 0xb4, 0x3a, 0x31, 0xff, 0x95, 0xb3, 0x60, 0xca, 0xc9, 0x61, 0xe6, 0x25,
0x11, 0xe9, 0xd3, 0x33, 0xf4, 0x89, 0x9b, 0x95, 0x33, 0x71, 0xa9, 0x9f, 0x8c, 0xc3, 0x77, 0xcc,
0x4f, 0xd6, 0xe1, 0x9c, 0xe5, 0x27, 0x1b, 0xa7, 0xfc, 0x1c, 0x4c, 0x9e, 0xb7, 0xe8, 0x8b, 0x13,
0x89, 0x4e, 0x55, 0x89, 0xf5, 0xc6, 0x69, 0x10, 0x65, 0xf8, 0x1b, 0xd1, 0xdf, 0x47, 0x68, 0xec,
0x77, 0x69, 0xee, 0x07, 0x89, 0x91, 0xfa, 0xb4, 0x22, 0x9a, 0xba, 0xf6, 0xa3, 0x02, 0x54, 0xb5,
0xf3, 0x03, 0x7d, 0xa0, 0x37, 0xa7, 0x95, 0x8c, 0xb6, 0xa3, 0x1f, 0x85, 0x99, 0xac, 0x9e, 0x01,
0x54, 0xa1, 0x9e, 0x9c, 0x72, 0x6c, 0xa1, 0xac, 0xbd, 0x38, 0x85, 0x4a, 0x9c, 0xde, 0xca, 0x89,
0x56, 0x9e, 0x9f, 0x65, 0x9c, 0x48, 0x63, 0xed, 0x77, 0x4a, 0x9b, 0xd9, 0x7e, 0xb3, 0x50, 0x91,
0x87, 0xb7, 0x8c, 0x4b, 0x14, 0xe2, 0xd9, 0x9c, 0xfc, 0x5f, 0xf8, 0xce, 0xff, 0x02, 0x00, 0x00,
0xff, 0xff, 0xe3, 0x8e, 0xe1, 0x22, 0x2a, 0x1e, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View File

@ -1 +1 @@
../../docs/plugin-protocol/tfplugin5.1.proto
../../docs/plugin-protocol/tfplugin5.2.proto

View File

@ -13,17 +13,24 @@ import (
// ConfigSchemaToProto takes a *configschema.Block and converts it to a
// proto.Schema_Block for a grpc response.
func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block {
block := &proto.Schema_Block{}
block := &proto.Schema_Block{
Description: b.Description,
DescriptionKind: protoStringKind(b.DescriptionKind),
Deprecated: b.Deprecated,
}
for _, name := range sortedKeys(b.Attributes) {
a := b.Attributes[name]
attr := &proto.Schema_Attribute{
Name: name,
Description: a.Description,
DescriptionKind: protoStringKind(a.DescriptionKind),
Optional: a.Optional,
Computed: a.Computed,
Required: a.Required,
Sensitive: a.Sensitive,
Deprecated: a.Deprecated,
}
ty, err := json.Marshal(a.Type)
@ -44,6 +51,15 @@ func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block {
return block
}
func protoStringKind(k configschema.StringKind) proto.StringKind {
switch k {
default:
return proto.StringKind_PLAIN
case configschema.StringMarkdown:
return proto.StringKind_MARKDOWN
}
}
func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Schema_NestedBlock {
var nesting proto.Schema_NestedBlock_NestingMode
switch b.Nesting {
@ -83,15 +99,21 @@ func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block {
block := &configschema.Block{
Attributes: make(map[string]*configschema.Attribute),
BlockTypes: make(map[string]*configschema.NestedBlock),
Description: b.Description,
DescriptionKind: schemaStringKind(b.DescriptionKind),
Deprecated: b.Deprecated,
}
for _, a := range b.Attributes {
attr := &configschema.Attribute{
Description: a.Description,
DescriptionKind: schemaStringKind(a.DescriptionKind),
Required: a.Required,
Optional: a.Optional,
Computed: a.Computed,
Sensitive: a.Sensitive,
Deprecated: a.Deprecated,
}
if err := json.Unmarshal(a.Type, &attr.Type); err != nil {
@ -108,6 +130,15 @@ func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block {
return block
}
func schemaStringKind(k proto.StringKind) configschema.StringKind {
switch k {
default:
return configschema.StringPlain
case proto.StringKind_MARKDOWN:
return configschema.StringMarkdown
}
}
func schemaNestedBlock(b *proto.Schema_NestedBlock) *configschema.NestedBlock {
var nesting configschema.NestingMode
switch b.Nesting {

View File

@ -105,6 +105,13 @@ func (p *GRPCProvider) getDatasourceSchema(name string) providers.Schema {
return dataSchema
}
// getProviderMetaSchema is a helper to extract the schema for the meta info
// defined for a provider,
func (p *GRPCProvider) getProviderMetaSchema() providers.Schema {
schema := p.getSchema()
return schema.ProviderMeta
}
func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) {
log.Printf("[TRACE] GRPCProvider: GetSchema")
p.mu.Lock()
@ -137,6 +144,11 @@ func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) {
}
resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider)
if protoResp.ProviderMeta == nil {
log.Printf("[TRACE] No provider meta schema returned")
} else {
resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta)
}
for name, res := range protoResp.ResourceSchemas {
resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res)
@ -319,6 +331,7 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
log.Printf("[TRACE] GRPCProvider: ReadResource")
resSchema := p.getResourceSchema(r.TypeName)
metaSchema := p.getProviderMetaSchema()
mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
if err != nil {
@ -332,6 +345,15 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi
Private: r.Private,
}
if metaSchema.Block != nil {
metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)
return resp
}
protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
}
protoResp, err := p.client.ReadResource(p.ctx, protoReq)
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)
@ -357,6 +379,7 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
log.Printf("[TRACE] GRPCProvider: PlanResourceChange")
resSchema := p.getResourceSchema(r.TypeName)
metaSchema := p.getProviderMetaSchema()
priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
if err != nil {
@ -384,6 +407,15 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest)
PriorPrivate: r.PriorPrivate,
}
if metaSchema.Block != nil {
metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)
return resp
}
protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
}
protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq)
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)
@ -416,6 +448,7 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
log.Printf("[TRACE] GRPCProvider: ApplyResourceChange")
resSchema := p.getResourceSchema(r.TypeName)
metaSchema := p.getProviderMetaSchema()
priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType())
if err != nil {
@ -441,6 +474,15 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques
PlannedPrivate: r.PlannedPrivate,
}
if metaSchema.Block != nil {
metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)
return resp
}
protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
}
protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq)
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)
@ -506,6 +548,7 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p
log.Printf("[TRACE] GRPCProvider: ReadDataSource")
dataSchema := p.getDatasourceSchema(r.TypeName)
metaSchema := p.getProviderMetaSchema()
config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
if err != nil {
@ -520,6 +563,15 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p
},
}
if metaSchema.Block != nil {
metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType())
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)
return resp
}
protoReq.ProviderMeta = &proto.DynamicValue{Msgpack: metaMP}
}
protoResp, err := p.client.ReadDataSource(p.ctx, protoReq)
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)

View File

@ -3,7 +3,7 @@ package plugin
import (
"net/rpc"
"github.com/hashicorp/go-plugin"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/terraform/terraform"
)

View File

@ -73,6 +73,9 @@ type GetSchemaResponse struct {
// Provider is the schema for the provider itself.
Provider Schema
// ProviderMeta is the schema for the provider's meta info in a module
ProviderMeta Schema
// ResourceTypes map the resource type name to that type's schema.
ResourceTypes map[string]Schema
@ -180,6 +183,12 @@ type ReadResourceRequest struct {
// Private is an opaque blob that will be stored in state along with the
// resource. It is intended only for interpretation by the provider itself.
Private []byte
// ProviderMeta is the configuration for the provider_meta block for the
// module and provider this resource belongs to. Its use is defined by
// each provider, and it should not be used without coordination with
// HashiCorp. It is considered experimental and subject to change.
ProviderMeta cty.Value
}
type ReadResourceResponse struct {
@ -216,6 +225,12 @@ type PlanResourceChangeRequest struct {
// PriorPrivate is the previously saved private data returned from the
// provider during the last apply.
PriorPrivate []byte
// ProviderMeta is the configuration for the provider_meta block for the
// module and provider this resource belongs to. Its use is defined by
// each provider, and it should not be used without coordination with
// HashiCorp. It is considered experimental and subject to change.
ProviderMeta cty.Value
}
type PlanResourceChangeResponse struct {
@ -262,6 +277,12 @@ type ApplyResourceChangeRequest struct {
// PlannedPrivate is the same value as returned by PlanResourceChange.
PlannedPrivate []byte
// ProviderMeta is the configuration for the provider_meta block for the
// module and provider this resource belongs to. Its use is defined by
// each provider, and it should not be used without coordination with
// HashiCorp. It is considered experimental and subject to change.
ProviderMeta cty.Value
}
type ApplyResourceChangeResponse struct {
@ -348,6 +369,12 @@ type ReadDataSourceRequest struct {
// Config is the complete configuration for the requested data source.
Config cty.Value
// ProviderMeta is the configuration for the provider_meta block for the
// module and provider this resource belongs to. Its use is defined by
// each provider, and it should not be used without coordination with
// HashiCorp. It is considered experimental and subject to change.
ProviderMeta cty.Value
}
type ReadDataSourceResponse struct {

View File

@ -29,6 +29,7 @@ import (
"github.com/hashicorp/terraform/states/statefile"
"github.com/hashicorp/terraform/tfdiags"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
)
func TestContext2Apply_basic(t *testing.T) {
@ -10174,7 +10175,6 @@ func TestContext2Apply_invalidIndexRef(t *testing.T) {
},
),
})
diags := c.Validate()
if diags.HasErrors() {
t.Fatalf("unexpected validation failure: %s", diags.Err())
@ -10741,3 +10741,915 @@ func TestContext2Apply_cbdCycle(t *testing.T) {
t.Fatalf("diags: %s", diags.Err())
}
}
func TestContext2Apply_ProviderMeta_apply_set(t *testing.T) {
m := testModule(t, "provider-meta-set")
p := testProvider("test")
p.DiffFn = testDiffFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
arcPMs := map[string]cty.Value{}
p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
arcPMs[req.TypeName] = req.ProviderMeta
s := req.PlannedState.AsValueMap()
s["id"] = cty.StringVal("ID")
return providers.ApplyResourceChangeResponse{
NewState: cty.ObjectVal(s),
}
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
_, diags = ctx.Apply()
assertNoErrors(t, diags)
if !p.ApplyResourceChangeCalled {
t.Fatalf("ApplyResourceChange not called")
}
expectations := map[string]cty.Value{}
if pm, ok := arcPMs["test_resource"]; !ok {
t.Fatalf("sub-module ApplyResourceChange not called")
} else if pm.IsNull() {
t.Fatalf("null ProviderMeta in sub-module ApplyResourceChange")
} else {
expectations["quux-submodule"] = pm
}
if pm, ok := arcPMs["test_instance"]; !ok {
t.Fatalf("root module ApplyResourceChange not called")
} else if pm.IsNull() {
t.Fatalf("null ProviderMeta in root module ApplyResourceChange")
} else {
expectations["quux"] = pm
}
type metaStruct struct {
Baz string `cty:"baz"`
}
for expected, v := range expectations {
var meta metaStruct
err := gocty.FromCtyValue(v, &meta)
if err != nil {
t.Fatalf("Error parsing cty value: %s", err)
}
if meta.Baz != expected {
t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz)
}
}
}
func TestContext2Apply_ProviderMeta_apply_unset(t *testing.T) {
m := testModule(t, "provider-meta-unset")
p := testProvider("test")
p.DiffFn = testDiffFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
arcPMs := map[string]cty.Value{}
p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
arcPMs[req.TypeName] = req.ProviderMeta
s := req.PlannedState.AsValueMap()
s["id"] = cty.StringVal("ID")
return providers.ApplyResourceChangeResponse{
NewState: cty.ObjectVal(s),
}
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
_, diags = ctx.Apply()
assertNoErrors(t, diags)
if !p.ApplyResourceChangeCalled {
t.Fatalf("ApplyResourceChange not called")
}
if pm, ok := arcPMs["test_resource"]; !ok {
t.Fatalf("sub-module ApplyResourceChange not called")
} else if !pm.IsNull() {
t.Fatalf("non-null ProviderMeta in sub-module ApplyResourceChange: %+v", pm)
}
if pm, ok := arcPMs["test_instance"]; !ok {
t.Fatalf("root module ApplyResourceChange not called")
} else if !pm.IsNull() {
t.Fatalf("non-null ProviderMeta in root module ApplyResourceChange: %+v", pm)
}
}
func TestContext2Apply_ProviderMeta_plan_set(t *testing.T) {
m := testModule(t, "provider-meta-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
prcPMs := map[string]cty.Value{}
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
prcPMs[req.TypeName] = req.ProviderMeta
return providers.PlanResourceChangeResponse{
PlannedState: req.ProposedNewState,
}
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
if !p.PlanResourceChangeCalled {
t.Fatalf("PlanResourceChange not called")
}
expectations := map[string]cty.Value{}
if pm, ok := prcPMs["test_resource"]; !ok {
t.Fatalf("sub-module PlanResourceChange not called")
} else if pm.IsNull() {
t.Fatalf("null ProviderMeta in sub-module PlanResourceChange")
} else {
expectations["quux-submodule"] = pm
}
if pm, ok := prcPMs["test_instance"]; !ok {
t.Fatalf("root module PlanResourceChange not called")
} else if pm.IsNull() {
t.Fatalf("null ProviderMeta in root module PlanResourceChange")
} else {
expectations["quux"] = pm
}
type metaStruct struct {
Baz string `cty:"baz"`
}
for expected, v := range expectations {
var meta metaStruct
err := gocty.FromCtyValue(v, &meta)
if err != nil {
t.Fatalf("Error parsing cty value: %s", err)
}
if meta.Baz != expected {
t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz)
}
}
}
func TestContext2Apply_ProviderMeta_plan_unset(t *testing.T) {
m := testModule(t, "provider-meta-unset")
p := testProvider("test")
p.ApplyFn = testApplyFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
prcPMs := map[string]cty.Value{}
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
prcPMs[req.TypeName] = req.ProviderMeta
return providers.PlanResourceChangeResponse{
PlannedState: req.ProposedNewState,
}
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
if !p.PlanResourceChangeCalled {
t.Fatalf("PlanResourceChange not called")
}
if pm, ok := prcPMs["test_resource"]; !ok {
t.Fatalf("sub-module PlanResourceChange not called")
} else if !pm.IsNull() {
t.Fatalf("non-null ProviderMeta in sub-module PlanResourceChange: %+v", pm)
}
if pm, ok := prcPMs["test_instance"]; !ok {
t.Fatalf("root module PlanResourceChange not called")
} else if !pm.IsNull() {
t.Fatalf("non-null ProviderMeta in root module PlanResourceChange: %+v", pm)
}
}
func TestContext2Apply_ProviderMeta_plan_setNoSchema(t *testing.T) {
m := testModule(t, "provider-meta-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
if !diags.HasErrors() {
t.Fatalf("plan supposed to error, has no errors")
}
var rootErr, subErr bool
errorSummary := "The resource test_%s.bar belongs to a provider that doesn't support provider_meta blocks"
for _, diag := range diags {
if diag.Description().Summary != "Provider registry.terraform.io/-/test doesn't support provider_meta" {
t.Errorf("Unexpected error: %+v", diag.Description())
}
switch diag.Description().Detail {
case fmt.Sprintf(errorSummary, "instance"):
rootErr = true
case fmt.Sprintf(errorSummary, "resource"):
subErr = true
default:
t.Errorf("Unexpected error: %s", diag.Description())
}
}
if !rootErr {
t.Errorf("Expected unsupported provider_meta block error for root module, none received")
}
if !subErr {
t.Errorf("Expected unsupported provider_meta block error for sub-module, none received")
}
}
func TestContext2Apply_ProviderMeta_plan_setInvalid(t *testing.T) {
m := testModule(t, "provider-meta-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"quux": {
Type: cty.String,
Required: true,
},
},
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
if !diags.HasErrors() {
t.Fatalf("plan supposed to error, has no errors")
}
var reqErr, invalidErr bool
for _, diag := range diags {
switch diag.Description().Summary {
case "Missing required argument":
if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` {
reqErr = true
} else {
t.Errorf("Unexpected error %+v", diag.Description())
}
case "Unsupported argument":
if diag.Description().Detail == `An argument named "baz" is not expected here.` {
invalidErr = true
} else {
t.Errorf("Unexpected error %+v", diag.Description())
}
default:
t.Errorf("Unexpected error %+v", diag.Description())
}
}
if !reqErr {
t.Errorf("Expected missing required argument error, none received")
}
if !invalidErr {
t.Errorf("Expected unsupported argument error, none received")
}
}
func TestContext2Apply_ProviderMeta_refresh_set(t *testing.T) {
m := testModule(t, "provider-meta-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
rrcPMs := map[string]cty.Value{}
p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
rrcPMs[req.TypeName] = req.ProviderMeta
newState, err := p.GetSchemaReturn.ResourceTypes[req.TypeName].CoerceValue(p.ReadResourceResponse.NewState)
if err != nil {
panic(err)
}
resp := p.ReadResourceResponse
resp.NewState = newState
return resp
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
_, diags = ctx.Apply()
assertNoErrors(t, diags)
_, diags = ctx.Refresh()
assertNoErrors(t, diags)
if !p.ReadResourceCalled {
t.Fatalf("ReadResource not called")
}
expectations := map[string]cty.Value{}
if pm, ok := rrcPMs["test_resource"]; !ok {
t.Fatalf("sub-module ReadResource not called")
} else if pm.IsNull() {
t.Fatalf("null ProviderMeta in sub-module ReadResource")
} else {
expectations["quux-submodule"] = pm
}
if pm, ok := rrcPMs["test_instance"]; !ok {
t.Fatalf("root module ReadResource not called")
} else if pm.IsNull() {
t.Fatalf("null ProviderMeta in root module ReadResource")
} else {
expectations["quux"] = pm
}
type metaStruct struct {
Baz string `cty:"baz"`
}
for expected, v := range expectations {
var meta metaStruct
err := gocty.FromCtyValue(v, &meta)
if err != nil {
t.Fatalf("Error parsing cty value: %s", err)
}
if meta.Baz != expected {
t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz)
}
}
}
func TestContext2Apply_ProviderMeta_refresh_unset(t *testing.T) {
m := testModule(t, "provider-meta-unset")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
_, diags = ctx.Apply()
assertNoErrors(t, diags)
_, diags = ctx.Refresh()
assertNoErrors(t, diags)
if !p.ReadResourceCalled {
t.Fatalf("ReadResource not called")
}
if !p.ReadResourceRequest.ProviderMeta.IsNull() {
t.Fatalf("Expected null ProviderMeta in ReadResource, got %v", p.ReadResourceRequest.ProviderMeta)
}
}
func TestContext2Apply_ProviderMeta_refresh_setNoSchema(t *testing.T) {
m := testModule(t, "provider-meta-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
// we need a schema for plan/apply so they don't error
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
_, diags = ctx.Apply()
assertNoErrors(t, diags)
// drop the schema before refresh, to test that it errors
schema.ProviderMeta = nil
p.GetSchemaReturn = schema
ctx = testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
State: ctx.State(),
})
_, diags = ctx.Refresh()
if !diags.HasErrors() {
t.Fatalf("refresh supposed to error, has no errors")
}
var rootErr, subErr bool
errorSummary := "The resource test_%s.bar belongs to a provider that doesn't support provider_meta blocks"
for _, diag := range diags {
if diag.Description().Summary != "Provider registry.terraform.io/-/test doesn't support provider_meta" {
t.Errorf("Unexpected error: %+v", diag.Description())
}
switch diag.Description().Detail {
case fmt.Sprintf(errorSummary, "instance"):
rootErr = true
case fmt.Sprintf(errorSummary, "resource"):
subErr = true
default:
t.Errorf("Unexpected error: %s", diag.Description())
}
}
if !rootErr {
t.Errorf("Expected unsupported provider_meta block error for root module, none received")
}
if !subErr {
t.Errorf("Expected unsupported provider_meta block error for sub-module, none received")
}
}
func TestContext2Apply_ProviderMeta_refresh_setInvalid(t *testing.T) {
m := testModule(t, "provider-meta-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
// we need a matching schema for plan/apply so they don't error
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
_, diags := ctx.Plan()
assertNoErrors(t, diags)
_, diags = ctx.Apply()
assertNoErrors(t, diags)
// change the schema before refresh, to test that it errors
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"quux": {
Type: cty.String,
Required: true,
},
},
}
p.GetSchemaReturn = schema
ctx = testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
State: ctx.State(),
})
_, diags = ctx.Refresh()
if !diags.HasErrors() {
t.Fatalf("refresh supposed to error, has no errors")
}
var reqErr, invalidErr bool
for _, diag := range diags {
switch diag.Description().Summary {
case "Missing required argument":
if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` {
reqErr = true
} else {
t.Errorf("Unexpected error %+v", diag.Description())
}
case "Unsupported argument":
if diag.Description().Detail == `An argument named "baz" is not expected here.` {
invalidErr = true
} else {
t.Errorf("Unexpected error %+v", diag.Description())
}
default:
t.Errorf("Unexpected error %+v", diag.Description())
}
}
if !reqErr {
t.Errorf("Expected missing required argument error, none received")
}
if !invalidErr {
t.Errorf("Expected unsupported argument error, none received")
}
}
func TestContext2Apply_ProviderMeta_refreshdata_set(t *testing.T) {
m := testModule(t, "provider-meta-data-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
rdsPMs := map[string]cty.Value{}
p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
rdsPMs[req.TypeName] = req.ProviderMeta
switch req.TypeName {
case "test_data_source":
log.Printf("[TRACE] test_data_source RDSR returning")
return providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("yo"),
"foo": cty.StringVal("bar"),
}),
}
case "test_file":
log.Printf("[TRACE] test_file RDSR returning")
return providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("bar"),
"rendered": cty.StringVal("baz"),
"template": cty.StringVal(""),
}),
}
default:
// config drift, oops
log.Printf("[TRACE] unknown request TypeName: %q", req.TypeName)
return providers.ReadDataSourceResponse{}
}
}
_, diags := ctx.Plan()
assertNoErrors(t, diags)
_, diags = ctx.Apply()
assertNoErrors(t, diags)
_, diags = ctx.Refresh()
assertNoErrors(t, diags)
if !p.ReadDataSourceCalled {
t.Fatalf("ReadDataSource not called")
}
expectations := map[string]cty.Value{}
if pm, ok := rdsPMs["test_file"]; !ok {
t.Fatalf("sub-module ReadDataSource not called")
} else if pm.IsNull() {
t.Fatalf("null ProviderMeta in sub-module ReadDataSource")
} else {
expectations["quux-submodule"] = pm
}
if pm, ok := rdsPMs["test_data_source"]; !ok {
t.Fatalf("root module ReadDataSource not called")
} else if pm.IsNull() {
t.Fatalf("null ProviderMeta in root module ReadDataSource")
} else {
expectations["quux"] = pm
}
type metaStruct struct {
Baz string `cty:"baz"`
}
for expected, v := range expectations {
var meta metaStruct
err := gocty.FromCtyValue(v, &meta)
if err != nil {
t.Fatalf("Error parsing cty value: %s", err)
}
if meta.Baz != expected {
t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz)
}
}
}
func TestContext2Apply_ProviderMeta_refreshdata_unset(t *testing.T) {
m := testModule(t, "provider-meta-data-unset")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"baz": {
Type: cty.String,
Required: true,
},
},
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
rdsPMs := map[string]cty.Value{}
p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
rdsPMs[req.TypeName] = req.ProviderMeta
switch req.TypeName {
case "test_data_source":
return providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("yo"),
"foo": cty.StringVal("bar"),
}),
}
case "test_file":
return providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("bar"),
"rendered": cty.StringVal("baz"),
"template": cty.StringVal(""),
}),
}
default:
// config drift, oops
return providers.ReadDataSourceResponse{}
}
}
_, diags := ctx.Refresh()
assertNoErrors(t, diags)
_, diags = ctx.Plan()
assertNoErrors(t, diags)
_, diags = ctx.Apply()
assertNoErrors(t, diags)
if !p.ReadDataSourceCalled {
t.Fatalf("ReadDataSource not called")
}
if pm, ok := rdsPMs["test_file"]; !ok {
t.Fatalf("sub-module ReadDataSource not called")
} else if !pm.IsNull() {
t.Fatalf("non-null ProviderMeta in sub-module ReadDataSource")
}
if pm, ok := rdsPMs["test_data_source"]; !ok {
t.Fatalf("root module ReadDataSource not called")
} else if !pm.IsNull() {
t.Fatalf("non-null ProviderMeta in root module ReadDataSource")
}
}
func TestContext2Apply_ProviderMeta_refreshdata_setNoSchema(t *testing.T) {
m := testModule(t, "provider-meta-data-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
p.ReadDataSourceResponse = providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("yo"),
"foo": cty.StringVal("bar"),
}),
}
_, diags := ctx.Refresh()
if !diags.HasErrors() {
t.Fatalf("refresh supposed to error, has no errors")
}
var rootErr, subErr bool
errorSummary := "The resource data.test_%s.foo belongs to a provider that doesn't support provider_meta blocks"
for _, diag := range diags {
if diag.Description().Summary != "Provider registry.terraform.io/-/test doesn't support provider_meta" {
t.Errorf("Unexpected error: %+v", diag.Description())
}
switch diag.Description().Detail {
case fmt.Sprintf(errorSummary, "data_source"):
rootErr = true
case fmt.Sprintf(errorSummary, "file"):
subErr = true
default:
t.Errorf("Unexpected error: %s", diag.Description())
}
}
if !rootErr {
t.Errorf("Expected unsupported provider_meta block error for root module, none received")
}
if !subErr {
t.Errorf("Expected unsupported provider_meta block error for sub-module, none received")
}
}
func TestContext2Apply_ProviderMeta_refreshdata_setInvalid(t *testing.T) {
m := testModule(t, "provider-meta-data-set")
p := testProvider("test")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
schema := p.GetSchemaReturn
schema.ProviderMeta = &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"quux": {
Type: cty.String,
Required: true,
},
},
}
p.GetSchemaReturn = schema
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[addrs.Provider]providers.Factory{
addrs.NewLegacyProvider("test"): testProviderFuncFixed(p),
},
),
})
p.ReadDataSourceResponse = providers.ReadDataSourceResponse{
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("yo"),
"foo": cty.StringVal("bar"),
}),
}
_, diags := ctx.Refresh()
if !diags.HasErrors() {
t.Fatalf("refresh supposed to error, has no errors")
}
var reqErr, invalidErr bool
for _, diag := range diags {
switch diag.Description().Summary {
case "Missing required argument":
if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` {
reqErr = true
} else {
t.Errorf("Unexpected error %+v", diag.Description())
}
case "Unsupported argument":
if diag.Description().Detail == `An argument named "baz" is not expected here.` {
invalidErr = true
} else {
t.Errorf("Unexpected error %+v", diag.Description())
}
default:
t.Errorf("Unexpected error %+v", diag.Description())
}
}
if !reqErr {
t.Errorf("Expected missing required argument error, none received")
}
if !invalidErr {
t.Errorf("Expected unsupported argument error, none received")
}
}

View File

@ -5,7 +5,7 @@ import (
"log"
"strings"
"github.com/hashicorp/go-multierror"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
@ -28,6 +28,7 @@ type EvalApply struct {
Change **plans.ResourceInstanceChange
ProviderAddr addrs.AbsProviderConfig
Provider *providers.Interface
ProviderMetas map[addrs.Provider]*configs.ProviderMeta
ProviderSchema **ProviderSchema
Output **states.ResourceInstanceObject
CreateNew *bool
@ -76,6 +77,31 @@ func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) {
)
}
metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
if n.ProviderMetas != nil {
log.Printf("[DEBUG] EvalApply: ProviderMeta config value set")
if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil {
// if the provider doesn't support this feature, throw an error
if (*n.ProviderSchema).ProviderMeta == nil {
log.Printf("[DEBUG] EvalApply: no ProviderMeta schema")
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()),
Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
Subject: &m.ProviderRange,
})
} else {
log.Printf("[DEBUG] EvalApply: ProviderMeta schema found")
var configDiags tfdiags.Diagnostics
metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, diags.Err()
}
}
}
}
log.Printf("[DEBUG] %s: applying the planned %s change", n.Addr.Absolute(ctx.Path()), change.Action)
resp := provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{
TypeName: n.Addr.Resource.Type,
@ -83,6 +109,7 @@ func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) {
Config: configVal,
PlannedState: change.After,
PlannedPrivate: change.Private,
ProviderMeta: metaConfigVal,
})
applyDiags := resp.Diagnostics
if n.Config != nil {

View File

@ -93,6 +93,7 @@ type EvalDiff struct {
Config *configs.Resource
Provider *providers.Interface
ProviderAddr addrs.AbsProviderConfig
ProviderMetas map[addrs.Provider]*configs.ProviderMeta
ProviderSchema **ProviderSchema
State **states.ResourceInstanceObject
PreviousDiff **plans.ResourceInstanceChange
@ -140,6 +141,28 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
return nil, diags.Err()
}
metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
if n.ProviderMetas != nil {
if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil {
// if the provider doesn't support this feature, throw an error
if (*n.ProviderSchema).ProviderMeta == nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()),
Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
Subject: &m.ProviderRange,
})
} else {
var configDiags tfdiags.Diagnostics
metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, diags.Err()
}
}
}
}
absAddr := n.Addr.Absolute(ctx.Path())
var priorVal cty.Value
var priorValTainted cty.Value
@ -204,6 +227,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
PriorState: priorVal,
ProposedNewState: proposedNewVal,
PriorPrivate: priorPrivate,
ProviderMeta: metaConfigVal,
})
diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config))
if diags.HasErrors() {
@ -382,6 +406,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
PriorState: nullPriorVal,
ProposedNewState: proposedNewVal,
PriorPrivate: plannedPrivate,
ProviderMeta: metaConfigVal,
})
// We need to tread carefully here, since if there are any warnings
// in here they probably also came out of our previous call to

View File

@ -6,6 +6,7 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/plans"
@ -23,6 +24,7 @@ type EvalReadData struct {
Config *configs.Resource
Provider *providers.Interface
ProviderAddr addrs.AbsProviderConfig
ProviderMetas map[addrs.Provider]*configs.ProviderMeta
ProviderSchema **ProviderSchema
// Planned is set when dealing with data resources that were deferred to
@ -104,6 +106,28 @@ func (n *EvalReadData) Eval(ctx EvalContext) (interface{}, error) {
return nil, diags.Err()
}
metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
if n.ProviderMetas != nil {
if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil {
// if the provider doesn't support this feature, throw an error
if (*n.ProviderSchema).ProviderMeta == nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()),
Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
Subject: &m.ProviderRange,
})
} else {
var configDiags tfdiags.Diagnostics
metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, diags.Err()
}
}
}
}
proposedNewVal := objchange.PlannedDataResourceObject(schema, configVal)
// If our configuration contains any unknown values then we must defer the
@ -205,6 +229,7 @@ func (n *EvalReadData) Eval(ctx EvalContext) (interface{}, error) {
resp := provider.ReadDataSource(providers.ReadDataSourceRequest{
TypeName: n.Addr.Resource.Type,
Config: configVal,
ProviderMeta: metaConfigVal,
})
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config))
if diags.HasErrors() {
@ -306,6 +331,7 @@ type EvalReadDataApply struct {
Addr addrs.ResourceInstance
Provider *providers.Interface
ProviderAddr addrs.AbsProviderConfig
ProviderMeta *configs.ProviderMeta
ProviderSchema **ProviderSchema
Output **states.ResourceInstanceObject
Config *configs.Resource
@ -330,6 +356,26 @@ func (n *EvalReadDataApply) Eval(ctx EvalContext) (interface{}, error) {
return nil, nil
}
metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
if n.ProviderMeta != nil {
// if the provider doesn't support this feature, throw an error
if (*n.ProviderSchema).ProviderMeta == nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()),
Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
Subject: &n.ProviderMeta.ProviderRange,
})
} else {
var configDiags tfdiags.Diagnostics
metaConfigVal, _, configDiags = ctx.EvaluateBlock(n.ProviderMeta.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, diags.Err()
}
}
}
// For the purpose of external hooks we present a data apply as a
// "Refresh" rather than an "Apply" because creating a data source
// is presented to users/callers as a "read" operation.
@ -345,6 +391,7 @@ func (n *EvalReadDataApply) Eval(ctx EvalContext) (interface{}, error) {
resp := provider.ReadDataSource(providers.ReadDataSourceRequest{
TypeName: n.Addr.Resource.Type,
Config: change.After,
ProviderMeta: metaConfigVal,
})
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config))
if diags.HasErrors() {

View File

@ -6,7 +6,9 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/providers"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags"
@ -18,6 +20,7 @@ type EvalRefresh struct {
Addr addrs.ResourceInstance
ProviderAddr addrs.AbsProviderConfig
Provider *providers.Interface
ProviderMetas map[addrs.Provider]*configs.ProviderMeta
ProviderSchema **ProviderSchema
State **states.ResourceInstanceObject
Output **states.ResourceInstanceObject
@ -42,6 +45,31 @@ func (n *EvalRefresh) Eval(ctx EvalContext) (interface{}, error) {
return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type)
}
metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
if n.ProviderMetas != nil {
if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil {
log.Printf("[DEBUG] EvalRefresh: ProviderMeta config value set")
// if the provider doesn't support this feature, throw an error
if (*n.ProviderSchema).ProviderMeta == nil {
log.Printf("[DEBUG] EvalRefresh: no ProviderMeta schema")
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()),
Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
Subject: &m.ProviderRange,
})
} else {
log.Printf("[DEBUG] EvalRefresh: ProviderMeta schema found: %+v", (*n.ProviderSchema).ProviderMeta)
var configDiags tfdiags.Diagnostics
metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, diags.Err()
}
}
}
}
// Call pre-refresh hook
err := ctx.Hook(func(h Hook) (HookAction, error) {
return h.PreRefresh(absAddr, states.CurrentGen, state.Value)
@ -56,6 +84,7 @@ func (n *EvalRefresh) Eval(ctx EvalContext) (interface{}, error) {
TypeName: n.Addr.Resource.Type,
PriorState: priorVal,
Private: state.Private,
ProviderMeta: metaConfigVal,
}
provider := *n.Provider

View File

@ -349,6 +349,7 @@ type EvalValidateResource struct {
Provider *providers.Interface
ProviderSchema **ProviderSchema
Config *configs.Resource
ProviderMetas map[addrs.Provider]*configs.ProviderMeta
// IgnoreWarnings means that warnings will not be passed through. This allows
// "just-in-time" passes of validation to continue execution through warnings.
@ -423,6 +424,40 @@ func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
}
}
// Validate the provider_meta block for the provider this resource
// belongs to, if there is one.
//
// Note: this will return an error for every resource a provider
// uses in a module, if the provider_meta for that module is
// incorrect. The only way to solve this that we've foudn is to
// insert a new ProviderMeta graph node in the graph, and make all
// that provider's resources in the module depend on the node. That's
// an awful heavy hammer to swing for this feature, which should be
// used only in limited cases with heavy coordination with the
// Terraform team, so we're going to defer that solution for a future
// enhancement to this functionality.
/*
if n.ProviderMetas != nil {
if m, ok := n.ProviderMetas[n.ProviderAddr.ProviderConfig.Type]; ok && m != nil {
// if the provider doesn't support this feature, throw an error
if (*n.ProviderSchema).ProviderMeta == nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", cfg.ProviderConfigAddr()),
Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
Subject: &m.ProviderRange,
})
} else {
_, _, metaDiags := ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
diags = diags.Append(metaDiags)
}
}
}
*/
// BUG(paddy): we're not validating provider_meta blocks on EvalValidate right now
// because the ProviderAddr for the resource isn't available on the EvalValidate
// struct.
// Provider entry point varies depending on resource mode, because
// managed resources and data resources are two distinct concepts
// in the provider abstraction.

View File

@ -21,6 +21,7 @@ var (
_ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil)
_ GraphNodeResource = (*NodeRefreshableDataResource)(nil)
_ GraphNodeAttachResourceConfig = (*NodeRefreshableDataResource)(nil)
_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil)
)
// GraphNodeDynamicExpandable
@ -76,6 +77,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
// Add the config and state since we don't do that via transforms
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
a.ProviderMetas = n.ProviderMetas
return &NodeRefreshableDataResourceInstance{
NodeAbstractResourceInstance: a,
@ -182,6 +184,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode {
Config: n.Config,
Provider: &provider,
ProviderAddr: n.ResolvedProvider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
OutputChange: &change,
OutputConfigValue: &configVal,

View File

@ -57,6 +57,9 @@ type NodeAbstractResource struct {
SchemaVersion uint64 // Schema version of "Schema", as decided by the provider
Config *configs.Resource // Config is the resource in the config
// ProviderMetas is the provider_meta configs for the module this resource belongs to
ProviderMetas map[addrs.Provider]*configs.ProviderMeta
ProvisionerSchemas map[string]*configschema.Block
Targets []addrs.Targetable // Set from GraphNodeTargetable
@ -75,6 +78,7 @@ var (
_ GraphNodeAttachResourceConfig = (*NodeAbstractResource)(nil)
_ GraphNodeAttachResourceSchema = (*NodeAbstractResource)(nil)
_ GraphNodeAttachProvisionerSchema = (*NodeAbstractResource)(nil)
_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil)
_ GraphNodeTargetable = (*NodeAbstractResource)(nil)
_ dag.GraphNodeDotter = (*NodeAbstractResource)(nil)
)
@ -115,6 +119,7 @@ var (
_ GraphNodeAttachResourceConfig = (*NodeAbstractResourceInstance)(nil)
_ GraphNodeAttachResourceSchema = (*NodeAbstractResourceInstance)(nil)
_ GraphNodeAttachProvisionerSchema = (*NodeAbstractResourceInstance)(nil)
_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResourceInstance)(nil)
_ GraphNodeTargetable = (*NodeAbstractResourceInstance)(nil)
_ dag.GraphNodeDotter = (*NodeAbstractResourceInstance)(nil)
)
@ -404,6 +409,11 @@ func (n *NodeAbstractResource) AttachResourceSchema(schema *configschema.Block,
n.SchemaVersion = version
}
// GraphNodeAttachProviderMetaConfigs impl
func (n *NodeAbstractResource) AttachProviderMetaConfigs(c map[addrs.Provider]*configs.ProviderMeta) {
n.ProviderMetas = c
}
// GraphNodeDotter impl.
func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{

View File

@ -180,6 +180,7 @@ func (n *NodeApplyableResourceInstance) evalTreeDataResource(addr addrs.AbsResou
Planned: &change, // setting this indicates that the result must be complete
Provider: &provider,
ProviderAddr: n.ResolvedProvider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
OutputState: &state,
},
@ -287,6 +288,7 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
Config: n.Config,
Provider: &provider,
ProviderAddr: n.ResolvedProvider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
State: &state,
PreviousDiff: &diff,
@ -350,6 +352,7 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
Change: &diffApply,
Provider: &provider,
ProviderAddr: n.ResolvedProvider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
Output: &state,
Error: &err,

View File

@ -246,6 +246,7 @@ func (n *NodeDestroyResourceInstance) EvalTree() EvalNode {
Change: &changeApply,
Provider: &provider,
ProviderAddr: n.ResolvedProvider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
Output: &state,
Error: &err,

View File

@ -97,6 +97,7 @@ func (n *NodePlanDeposedResourceInstanceObject) EvalTree() EvalNode {
Addr: addr.Resource,
ProviderAddr: n.ResolvedProvider,
Provider: &provider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
State: &state,
Output: &state,

View File

@ -104,6 +104,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
a.ResolvedProvider = n.ResolvedProvider
a.Schema = n.Schema
a.ProvisionerSchemas = n.ProvisionerSchemas
a.ProviderMetas = n.ProviderMetas
return &NodePlannableResourceInstance{
NodeAbstractResourceInstance: a,
@ -122,6 +123,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
a.ResolvedProvider = n.ResolvedProvider
a.Schema = n.Schema
a.ProvisionerSchemas = n.ProvisionerSchemas
a.ProviderMetas = n.ProviderMetas
return &NodePlannableResourceInstanceOrphan{
NodeAbstractResourceInstance: a,

View File

@ -116,6 +116,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResou
Config: n.Config,
Provider: &provider,
ProviderAddr: n.ResolvedProvider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
ForcePlanRead: true, // _always_ produce a Read change, even if the config seems ready
OutputChange: &change,
@ -174,6 +175,7 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
CreateBeforeDestroy: n.ForceCreateBeforeDestroy,
Provider: &provider,
ProviderAddr: n.ResolvedProvider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
State: &state,
OutputChange: &change,

View File

@ -85,6 +85,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
a.Config = n.Config
a.ResolvedProvider = n.ResolvedProvider
a.Dependencies = n.Dependencies
a.ProviderMetas = n.ProviderMetas
return &NodeRefreshableManagedResourceInstance{
NodeAbstractResourceInstance: a,
@ -237,6 +238,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN
Addr: addr.Resource,
ProviderAddr: n.ResolvedProvider,
Provider: &provider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
State: &state,
Output: &state,

View File

@ -21,6 +21,7 @@ var (
_ GraphNodeReferencer = (*NodeValidatableResource)(nil)
_ GraphNodeResource = (*NodeValidatableResource)(nil)
_ GraphNodeAttachResourceConfig = (*NodeValidatableResource)(nil)
_ GraphNodeAttachProviderMetaConfigs = (*NodeValidatableResource)(nil)
)
// GraphNodeEvalable
@ -45,6 +46,7 @@ func (n *NodeValidatableResource) EvalTree() EvalNode {
&EvalValidateResource{
Addr: addr.Resource,
Provider: &provider,
ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema,
Config: config,
ConfigVal: &configVal,

View File

@ -118,6 +118,7 @@ func (p *MockProvider) getSchema() providers.GetSchemaResponse {
}
if p.GetSchemaReturn != nil {
ret.Provider.Block = p.GetSchemaReturn.Provider
ret.ProviderMeta.Block = p.GetSchemaReturn.ProviderMeta
for n, s := range p.GetSchemaReturn.DataSources {
ret.DataSources[n] = providers.Schema{
Block: s,

View File

@ -164,6 +164,10 @@ func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *con
}
schemas[fqn] = s
if resp.ProviderMeta.Block != nil {
s.ProviderMeta = resp.ProviderMeta.Block
}
}
if config != nil {
@ -246,6 +250,7 @@ func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *conf
// resource types and data sources used by that configuration.
type ProviderSchema struct {
Provider *configschema.Block
ProviderMeta *configschema.Block
ResourceTypes map[string]*configschema.Block
DataSources map[string]*configschema.Block

View File

@ -1634,6 +1634,8 @@ type InstanceState struct {
// and collections.
Meta map[string]interface{} `json:"meta"`
ProviderMeta cty.Value
// Tainted is used to mark a resource for recreation.
Tainted bool `json:"tainted"`

View File

@ -0,0 +1,13 @@
data "test_data_source" "foo" {
foo = "bar"
}
terraform {
provider_meta "test" {
baz = "quux"
}
}
module "my_module" {
source = "./my-module"
}

View File

@ -0,0 +1,9 @@
data "test_file" "foo" {
id = "bar"
}
terraform {
provider_meta "test" {
baz = "quux-submodule"
}
}

View File

@ -0,0 +1,7 @@
data "test_data_source" "foo" {
foo = "bar"
}
module "my_module" {
source = "./my-module"
}

View File

@ -0,0 +1,3 @@
data "test_file" "foo" {
id = "bar"
}

View File

@ -0,0 +1,13 @@
resource "test_instance" "bar" {
foo = "bar"
}
terraform {
provider_meta "test" {
baz = "quux"
}
}
module "my_module" {
source = "./my-module"
}

View File

@ -0,0 +1,9 @@
resource "test_resource" "bar" {
value = "bar"
}
terraform {
provider_meta "test" {
baz = "quux-submodule"
}
}

View File

@ -0,0 +1,7 @@
resource "test_instance" "bar" {
foo = "bar"
}
module "my_module" {
source = "./my-module"
}

View File

@ -0,0 +1,3 @@
resource "test_resource" "bar" {
value = "bar"
}

View File

@ -0,0 +1,15 @@
package terraform
import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
)
// GraphNodeAttachProviderMetaConfigs is an interface that must be implemented
// by nodes that want provider meta configurations attached.
type GraphNodeAttachProviderMetaConfigs interface {
GraphNodeResource
// Sets the configuration
AttachProviderMetaConfigs(map[addrs.Provider]*configs.ProviderMeta)
}

View File

@ -56,6 +56,24 @@ func (t *AttachResourceConfigTransformer) Transform(g *Graph) error {
log.Printf("[TRACE] AttachResourceConfigTransformer: attaching to %q (%T) config from %s", dag.VertexName(v), v, r.DeclRange)
arn.AttachResourceConfig(r)
// attach the provider_meta info
if gnapmc, ok := v.(GraphNodeAttachProviderMetaConfigs); ok {
log.Printf("[TRACE] AttachResourceConfigTransformer: attaching provider meta configs to %s", dag.VertexName(v))
if config == nil {
log.Printf("[TRACE] AttachResourceConfigTransformer: no config set on the transformer for %s", dag.VertexName(v))
continue
}
if config.Module == nil {
log.Printf("[TRACE] AttachResourceConfigTransformer: no module in config for %s", dag.VertexName(v))
continue
}
if config.Module.ProviderMetas == nil {
log.Printf("[TRACE] AttachResourceConfigTransformer: no provider metas defined for %s", dag.VertexName(v))
continue
}
gnapmc.AttachProviderMetaConfigs(config.Module.ProviderMetas)
}
}
for _, r := range config.Module.DataResources {
rAddr := r.Addr()
@ -67,6 +85,24 @@ func (t *AttachResourceConfigTransformer) Transform(g *Graph) error {
log.Printf("[TRACE] AttachResourceConfigTransformer: attaching to %q (%T) config from %#v", dag.VertexName(v), v, r.DeclRange)
arn.AttachResourceConfig(r)
// attach the provider_meta info
if gnapmc, ok := v.(GraphNodeAttachProviderMetaConfigs); ok {
log.Printf("[TRACE] AttachResourceConfigTransformer: attaching provider meta configs to %s", dag.VertexName(v))
if config == nil {
log.Printf("[TRACE] AttachResourceConfigTransformer: no config set on the transformer for %s", dag.VertexName(v))
continue
}
if config.Module == nil {
log.Printf("[TRACE] AttachResourceConfigTransformer: no module in config for %s", dag.VertexName(v))
continue
}
if config.Module.ProviderMetas == nil {
log.Printf("[TRACE] AttachResourceConfigTransformer: no provider metas defined for %s", dag.VertexName(v))
continue
}
gnapmc.AttachProviderMetaConfigs(config.Module.ProviderMetas)
}
}
}

View File

@ -179,3 +179,11 @@ The introduction and completion of experiments is reported in
[Terraform's changelog](https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md),
so you can watch the release notes there to discover which experiment keywords,
if any, are available in a particular Terraform release.
## Passing Metadata to Providers
The `terraform` block can have a nested `provider_meta` block for each
provider a module is using, if the provider defines a schema for it. This
allows the provider to receive module-specific information. No interpolations
are performed on this block. For more information, see the
[`provider_meta` page](/docs/internals/provider-meta.html).

View File

@ -0,0 +1,71 @@
---
layout: "docs"
page_title: "Provider Metadata"
sidebar_current: "docs-internals-provider-meta"
description: |-
For advanced use cases, modules can provide some pre-defined metadata for providers.
---
# Provider Metadata
In some situations it's beneficial for a provider to offer an interface
through which modules can pass it information unrelated to the resources
in the module, but scoped on a per-module basis. The provider metadata
functionality allows a provider to do this in a straightforward way.
~> **Advanced Topic!** This page covers technical details
of Terraform. You don't need to understand these details to
effectively use Terraform. The details are documented here for
module authors and provider developers working on advanced
functionality.
~> **Experimental Feature!** This functionality is still considered
experimental, and anyone taking advantage of it should [coordinate
with the Terraform team](https://github.com/hashicorp/terraform/issues/new)
to help the team understand how the feature is being used and to make
sure their use case is taken into account as the feature develops.
## Defining the Schema
Before a provider can receive information from a module, the provider
must strictly define the data it can accept. You can do this by setting
the `ProviderMeta` property on your `schema.Provider` struct. Its value
functions similarly to the provider config: a map of strings to the
`schema.Schema` describing the values those strings accept.
## Using the Data
When Terraform calls your provider, you can use the `schema.ResourceData`
that your `Create`, `Read`, and `Update` functions already use to get
access to the provider metadata being passed. First define a struct
that matches your schema, then call the `GetProviderSchema` method on
your `schema.ResourceData`, passing a pointer to a variable of that type.
The variable will be populated with the provider metadata, and will return
an error if there was an issue with parsing the data into the struct.
## Specifying Data in Modules
To include data in your modules, create a `provider_meta` nested block under
your module's `terraform` block, with the name of the provider it's trying
to pass information to:
```hcl
terraform {
provider_meta "my-provider" {
hello = "world"
}
}
```
The `provider_meta` block must match the schema the provider has defined.
## Versioning Your Modules
Any module taking advantage of this functionality must make sure that the
provider metadata supplied matches the schema defined in the provider, and
that the version of Terraform that is being run has support for the provider
metadata functionality. It's therefore recommended that any module taking
advantage of this functionality should specify a minimum Terraform version of
0.13.0 or higher, and a minimum version of each of the providers it specifies
metadata as the first version the schema being used was supported by the
provider.

View File

@ -472,6 +472,10 @@
<li<%= sidebar_current("docs-internals-plugins") %>>
<a href="/docs/internals/internal-plugins.html">Internal Plugins</a>
</li>
<li<%= sidebar_current("docs-internals-provider-meta") %>>
<a href="/docs/internals/provider-meta.html">Provider Metadata</a>
</li>
</ul>
</li>