├── .gitignore ├── .licenserignore ├── .github ├── CODEOWNERS └── workflows │ ├── go.yml │ └── release.yml ├── .template.env ├── examples ├── data-sources │ └── tsb_organization │ │ └── data-source.tf ├── provider │ └── provider.tf ├── resources │ ├── tsb_tenant │ │ └── resource.tf │ ├── tsb_service_account │ │ └── resource.tf │ └── tsb_user │ │ └── resource.tf └── README.md ├── terraform-registry-manifest.json ├── docs ├── data-sources │ └── organization.md ├── index.md └── resources │ ├── user.md │ ├── tenant.md │ └── service_account.md ├── tools └── tools.go ├── pkg └── provider.go ├── internal ├── provider │ ├── resources │ │ ├── user │ │ │ ├── import.go │ │ │ ├── delete.go │ │ │ ├── model.go │ │ │ ├── create.go │ │ │ ├── update.go │ │ │ ├── resource.go │ │ │ ├── read.go │ │ │ └── user_test.go │ │ ├── tenant │ │ │ ├── import.go │ │ │ ├── model.go │ │ │ ├── delete.go │ │ │ ├── create.go │ │ │ ├── update.go │ │ │ ├── resource.go │ │ │ ├── read.go │ │ │ └── tenant_test.go │ │ └── serviceaccount │ │ │ ├── import.go │ │ │ ├── delete.go │ │ │ ├── update.go │ │ │ ├── read.go │ │ │ ├── create.go │ │ │ ├── serviceaccount_test.go │ │ │ └── serviceaccount.go │ ├── datasources │ │ └── organization │ │ │ ├── read.go │ │ │ ├── organization_test.go │ │ │ └── organization.go │ ├── validators │ │ └── address_includes_port.go │ ├── test │ │ └── provider.go │ └── provider.go └── helpers │ ├── basic_auth.go │ ├── fqn_test.go │ ├── fqn.go │ └── clients.go ├── README.md ├── generator ├── import.go ├── model.go ├── delete.go ├── update.go ├── main.go ├── resource.go ├── create.go ├── read.go └── helpers.go ├── Makefile ├── .goreleaser.yml ├── main.go ├── go.mod ├── LICENSE └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | dist/ 3 | vendor/ -------------------------------------------------------------------------------- /.licenserignore: -------------------------------------------------------------------------------- 1 | examples/**/*.tf 2 | .goreleaser.yml -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @tetratelabs/tsb-api-owners @tetratelabs/platform-owners -------------------------------------------------------------------------------- /.template.env: -------------------------------------------------------------------------------- 1 | export TSB_ADDRESS= 2 | export TSB_ORGANIZATION= 3 | export TSB_USERNAME=admin 4 | export TSB_PASSWORD= -------------------------------------------------------------------------------- /examples/data-sources/tsb_organization/data-source.tf: -------------------------------------------------------------------------------- 1 | data "tsb_organization" "example" { 2 | id = "organizations/tetrate" 3 | } -------------------------------------------------------------------------------- /terraform-registry-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "metadata": { 4 | "protocol_versions": ["6.0"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/provider/provider.tf: -------------------------------------------------------------------------------- 1 | provider "tsb" { 2 | address = "tsb.tetrate.com:443" 3 | basic_auth = { 4 | username = "someusername" 5 | password = "supersecretpassword" 6 | } 7 | } -------------------------------------------------------------------------------- /examples/resources/tsb_tenant/resource.tf: -------------------------------------------------------------------------------- 1 | resource "tsb_tenant" "example" { 2 | parent = "organizations/tetrate" 3 | name = "tenantone" 4 | display_name = "Tenant One" 5 | description = "I'm a tenant" 6 | security_domain = "production" 7 | } 8 | -------------------------------------------------------------------------------- /examples/resources/tsb_service_account/resource.tf: -------------------------------------------------------------------------------- 1 | resource "tsb_service_account" "example" { 2 | name = "ciautomation" 3 | parent = "organizations/tetrate" 4 | display_name = "CI Automation" 5 | description = "Used by our CI tool to create resources" 6 | key_encoding = "JWT" 7 | } 8 | -------------------------------------------------------------------------------- /examples/resources/tsb_user/resource.tf: -------------------------------------------------------------------------------- 1 | resource "tsb_user" "example" { 2 | parent = "organizations/tetrate" 3 | name = "terryform" 4 | display_name = "Terry Form" 5 | login_name = "terryform" 6 | first_name = "Terry" 7 | last_name = "Form" 8 | email = "terry.form@tetrate.io" 9 | } 10 | -------------------------------------------------------------------------------- /docs/data-sources/organization.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "tsb_organization Data Source - terraform-provider-tsb" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # tsb_organization (Data Source) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | data "tsb_organization" "example" { 17 | id = "organizations/tetrate" 18 | } 19 | ``` 20 | 21 | 22 | ## Schema 23 | 24 | ### Required 25 | 26 | - `id` (String) Fully-qualified name of the resource. 27 | 28 | ### Optional 29 | 30 | - `display_name` (String) User friendly name for the resource. 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This directory contains examples that are mostly used for documentation, but can also be run/tested manually via the Terraform CLI. 4 | 5 | The document generation tool looks for files in the following locations by default. All other *.tf files besides the ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or ar testable even if some parts are not relevant for the documentation. 6 | 7 | * **provider/provider.tf** example file for the provider index page 8 | * **data-sources//data-source.tf** example file for the named data source page 9 | * **resources//resource.tf** example file for the named data source page 10 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build tools 16 | // +build tools 17 | 18 | package tools 19 | 20 | import ( 21 | _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" 22 | ) 23 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "tsb Provider" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # tsb Provider 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | provider "tsb" { 17 | address = "tsb.tetrate.com:443" 18 | basic_auth = { 19 | username = "someusername" 20 | password = "supersecretpassword" 21 | } 22 | } 23 | ``` 24 | 25 | 26 | ## Schema 27 | 28 | ### Required 29 | 30 | - `address` (String) The address that the management plane can be reached at. Must include port. 31 | - `basic_auth` (Attributes) The basic auth credentials to communicate with a TSB management plane. (see [below for nested schema](#nestedatt--basic_auth)) 32 | 33 | 34 | ### Nested Schema for `basic_auth` 35 | 36 | Required: 37 | 38 | - `password` (String, Sensitive) 39 | - `username` (String) 40 | -------------------------------------------------------------------------------- /pkg/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pkg 16 | 17 | import ( 18 | framework "github.com/hashicorp/terraform-plugin-framework/provider" 19 | 20 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider" 21 | ) 22 | 23 | var version string = "0.0.1" 24 | 25 | func NewProvider() framework.Provider { 26 | return provider.New(version)() 27 | } 28 | -------------------------------------------------------------------------------- /internal/provider/resources/user/import.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package user 16 | 17 | import ( 18 | "context" 19 | path "github.com/hashicorp/terraform-plugin-framework/path" 20 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 21 | ) 22 | 23 | func (r *UserResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { 24 | resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) 25 | } 26 | -------------------------------------------------------------------------------- /internal/provider/resources/tenant/import.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tenant 16 | 17 | import ( 18 | "context" 19 | path "github.com/hashicorp/terraform-plugin-framework/path" 20 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 21 | ) 22 | 23 | func (r *TenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { 24 | resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) 25 | } 26 | -------------------------------------------------------------------------------- /internal/provider/resources/serviceaccount/import.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serviceaccount 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/hashicorp/terraform-plugin-framework/path" 21 | "github.com/hashicorp/terraform-plugin-framework/resource" 22 | ) 23 | 24 | func (r *ServiceAccountResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { 25 | resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) 26 | } 27 | -------------------------------------------------------------------------------- /internal/provider/resources/tenant/model.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tenant 16 | 17 | import types "github.com/hashicorp/terraform-plugin-framework/types" 18 | 19 | // tfsdk typed model definition 20 | type TenantModel struct { 21 | DisplayName types.String `tfsdk:"display_name"` 22 | Id types.String `tfsdk:"id"` 23 | Name types.String `tfsdk:"name"` 24 | Parent types.String `tfsdk:"parent"` 25 | SecurityDomain types.String `tfsdk:"security_domain"` 26 | Description types.String `tfsdk:"description"` 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [WIP] TSB Terraform Provider 2 | 3 | This provider uses the Terraform Plugin Framework (v3) NOT the Terraform Plugin SDK (v1 + v2). The scaffolding repository for this can be found [here](https://github.com/hashicorp/terraform-provider-scaffolding-framework). 4 | 5 | ## Requirements 6 | 7 | - [Terraform](https://www.terraform.io/downloads.html) >= 0.13.x 8 | - [Go](https://golang.org/doc/install) >= 1.18 9 | 10 | ## Developing the Provider 11 | 12 | To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory. 13 | 14 | To generate or update documentation, run `go generate`. 15 | 16 | In order to run the full suite of Acceptance tests you need a running TSB instance. Configure a .env file from the template and run `make test`. 17 | 18 | ```sh 19 | $ make test 20 | ``` 21 | 22 | ## Directory Structure 23 | 24 | - `docs` documentation autogenerated from a combination of `examples` and Schemas. 25 | - `examples` example Terraform configuration, used by docs generation code. 26 | - `internal/provider` the code that takes Terraform configuration and creates resources in TSB. 27 | - `tools` ensures the Go modules are present for doc generation. 28 | -------------------------------------------------------------------------------- /generator/import.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | j "github.com/dave/jennifer/jen" 19 | ) 20 | 21 | func genImport(r resource) *j.File { 22 | f := j.NewFile(r.lowerName) 23 | 24 | f.Func(). 25 | Parens(j.Id("r").Op("*").Add(r.structId)).Id("ImportState"). 26 | Params(j.Id("ctx").Qual("context", "Context"), j.Id("req").Qual(Resource, "ImportStateRequest"), j.Id("resp").Op("*").Qual(Resource, "ImportStateResponse")). 27 | Block( 28 | j.Qual(Resource, "ImportStatePassthroughID").Call(j.Id("ctx"), j.Qual(Path, "Root").Call(j.Lit("id")), j.Id("req"), j.Id("resp")), 29 | ) 30 | 31 | return f 32 | } 33 | -------------------------------------------------------------------------------- /internal/provider/resources/user/delete.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package user 16 | 17 | import ( 18 | "context" 19 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 20 | v2 "github.com/tetrateio/api/tsb/v2" 21 | ) 22 | 23 | func (r *UserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 24 | var model UserModel 25 | resp.Diagnostics.Append(req.State.Get(ctx, &model)...) 26 | if resp.Diagnostics.HasError() { 27 | return 28 | } 29 | if _, err := r.client.DeleteUser(ctx, &v2.DeleteUserRequest{Fqn: model.Id.ValueString()}); err != nil { 30 | resp.Diagnostics.AddError("Error deleting User", "DeleteUser request failed: "+err.Error()) 31 | return 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /internal/provider/resources/tenant/delete.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tenant 16 | 17 | import ( 18 | "context" 19 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 20 | v2 "github.com/tetrateio/api/tsb/v2" 21 | ) 22 | 23 | func (r *TenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 24 | var model TenantModel 25 | resp.Diagnostics.Append(req.State.Get(ctx, &model)...) 26 | if resp.Diagnostics.HasError() { 27 | return 28 | } 29 | if _, err := r.client.DeleteTenant(ctx, &v2.DeleteTenantRequest{Fqn: model.Id.ValueString()}); err != nil { 30 | resp.Diagnostics.AddError("Error deleting Tenant", "DeleteTenant request failed: "+err.Error()) 31 | return 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /internal/provider/resources/user/model.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package user 16 | 17 | import ( 18 | types "github.com/hashicorp/terraform-plugin-framework/types" 19 | // prototypes "github.com/tetrateio/api/protoc-plugins/protoc-gen-terraform/pkg/types" 20 | ) 21 | 22 | // tfsdk typed model definition 23 | type UserModel struct { 24 | LastName types.String `tfsdk:"last_name"` 25 | FirstName types.String `tfsdk:"first_name"` 26 | Id types.String `tfsdk:"id"` 27 | DisplayName types.String `tfsdk:"display_name"` 28 | LoginName types.String `tfsdk:"login_name"` 29 | Name types.String `tfsdk:"name"` 30 | SourceType types.String `tfsdk:"source_type"` 31 | // SourceType prototypes.Enum `tfsdk:"source_type"` 32 | Email types.String `tfsdk:"email"` 33 | Parent types.String `tfsdk:"parent"` 34 | } 35 | -------------------------------------------------------------------------------- /internal/provider/resources/serviceaccount/delete.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serviceaccount 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/hashicorp/terraform-plugin-framework/resource" 21 | tsbv2 "github.com/tetrateio/api/tsb/v2" 22 | ) 23 | 24 | func (r *ServiceAccountResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { 25 | // Load state into the model 26 | var model serviceAccountResourceModel 27 | resp.Diagnostics.Append(req.State.Get(ctx, &model)...) 28 | if resp.Diagnostics.HasError() { 29 | return 30 | } 31 | 32 | if _, err := r.client.DeleteServiceAccount(ctx, &tsbv2.DeleteServiceAccountRequest{Fqn: model.Id.ValueString()}); err != nil { 33 | resp.Diagnostics.AddError("Error deleting ServiceAccount", "DeleteServiceAccount request failed: "+err.Error()) 34 | return 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /internal/helpers/basic_auth.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package helpers 16 | 17 | import ( 18 | "context" 19 | "encoding/base64" 20 | 21 | "github.com/hashicorp/terraform-plugin-framework/types" 22 | ) 23 | 24 | // BasicAuth is an HTTP Basic credentials provider for gRPC 25 | type BasicAuth struct { 26 | Username types.String `tfsdk:"username"` 27 | Password types.String `tfsdk:"password"` 28 | } 29 | 30 | // GetRequestMetadata implements credentials.PerRPCCredentials 31 | func (b BasicAuth) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { 32 | auth := b.Username.ValueString() + ":" + b.Password.ValueString() 33 | enc := base64.StdEncoding.EncodeToString([]byte(auth)) 34 | return map[string]string{"authorization": "Basic " + enc}, nil 35 | } 36 | 37 | // RequireTransportSecurity implements credentials.PerRPCCredentials 38 | func (BasicAuth) RequireTransportSecurity() bool { 39 | return false 40 | } 41 | -------------------------------------------------------------------------------- /docs/resources/user.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "tsb_user Resource - terraform-provider-tsb" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # tsb_user (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "tsb_user" "example" { 17 | parent = "organizations/tetrate" 18 | name = "terryform" 19 | display_name = "Terry Form" 20 | login_name = "terryform" 21 | first_name = "Terry" 22 | last_name = "Form" 23 | email = "terry.form@tetrate.io" 24 | } 25 | ``` 26 | 27 | 28 | ## Schema 29 | 30 | ### Required 31 | 32 | - `login_name` (String) The username used in the login credentials. 33 | - `name` (String) Short name of the User resource. 34 | - `parent` (String) The parent id for the User resource. 35 | 36 | ### Optional 37 | 38 | - `display_name` (String) User friendly name for the resource. 39 | - `email` (String) Email for the user where alerts and other notifications will be sent. 40 | - `first_name` (String) The first name of the user. 41 | - `last_name` (String) The last name of the user, if any. 42 | 43 | ### Read-Only 44 | 45 | - `id` (String) Fully-qualified name of the User resource. 46 | - `source_type` (String) Where the user comes from. It can be a local user that exists only in TSB (type LOCAL) or it can be a user that has been synchronized from the Identity Provider (for example: type LDAP). 47 | 48 | 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Tetrate 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | default: build 16 | 17 | .PHONY: docs 18 | docs: 19 | go install 20 | go generate ./... 21 | 22 | build: 23 | go build -v ./... 24 | 25 | check: docs licenser format 26 | [ -z "`git status -uno --porcelain`" ] || (git status && echo 'Check failed. This could be a failed check or dirty git state.'; exit 1) 27 | 28 | format: 29 | go fmt ./... 30 | 31 | licenser: 32 | licenser apply Tetrate -r . 33 | 34 | generator: 35 | go run generator/*.go 36 | 37 | gen: generator licenser 38 | 39 | # WARNING!!! THESE CREATE ACTUAL RESOURCES 40 | # Run acceptance tests 41 | # Need to set the following env vars: 42 | # export TSB_ADDRESS=: 43 | # export TSB_ORGANIZATION= 44 | # export TSB_USERNAME=admin 45 | # export TSB_PASSWORD= 46 | # GRPC debugging can be enabled as follows: 47 | # GRPC_GO_LOG_VERBOSITY_LEVEL=99 GRPC_GO_LOG_SEVERITY_LEVEL=info 48 | .PHONY: test 49 | test: 50 | source .env && TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 1m 51 | -------------------------------------------------------------------------------- /generator/model.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "strings" 19 | 20 | j "github.com/dave/jennifer/jen" 21 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 22 | "github.com/samber/lo" 23 | "golang.org/x/text/cases" 24 | "golang.org/x/text/language" 25 | ) 26 | 27 | func genModel(r resource) *j.File { 28 | f := j.NewFile(r.lowerName) 29 | 30 | fields := lo.MapToSlice( 31 | lo.MapValues(r.Schema.Attributes, func(attribute schema.Attribute, name string) j.Code { 32 | return j.Id(snakeToCamel(name)).Qual(Types, "String").Tag(map[string]string{"tfsdk": name}) 33 | }), 34 | func(_ string, code j.Code) j.Code { return code }, 35 | ) 36 | 37 | f.Comment("// tfsdk typed model definition") 38 | f.Type().Add(r.modelId).Struct(fields...) 39 | 40 | return f 41 | } 42 | 43 | func snakeToCamel(s string) string { 44 | words := strings.Split(s, "_") 45 | caser := cases.Title(language.English) 46 | for i := range words { 47 | words[i] = caser.String(words[i]) 48 | } 49 | return strings.Join(words, "") 50 | } 51 | -------------------------------------------------------------------------------- /internal/provider/datasources/organization/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package organization 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/hashicorp/terraform-plugin-framework/datasource" 21 | types "github.com/hashicorp/terraform-plugin-framework/types" 22 | tsbv2 "github.com/tetrateio/api/tsb/v2" 23 | ) 24 | 25 | func (d *OrganizationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 26 | // Load request into the model 27 | var model organizationDataSourceModel 28 | resp.Diagnostics.Append(req.Config.Get(ctx, &model)...) 29 | if resp.Diagnostics.HasError() { 30 | return 31 | } 32 | 33 | org, err := d.client.GetOrganization(ctx, &tsbv2.GetOrganizationRequest{Fqn: model.Id.ValueString()}) 34 | if err != nil { 35 | resp.Diagnostics.AddError("Error reading Organization", "GetOrganization request failed: "+err.Error()) 36 | return 37 | } 38 | 39 | model.Id = types.StringValue(org.Fqn) 40 | model.DisplayName = types.StringValue(org.DisplayName) 41 | 42 | // Save model into Terraform model 43 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 44 | } 45 | -------------------------------------------------------------------------------- /docs/resources/tenant.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "tsb_tenant Resource - terraform-provider-tsb" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # tsb_tenant (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "tsb_tenant" "example" { 17 | parent = "organizations/tetrate" 18 | name = "tenantone" 19 | display_name = "Tenant One" 20 | description = "I'm a tenant" 21 | security_domain = "production" 22 | } 23 | ``` 24 | 25 | 26 | ## Schema 27 | 28 | ### Required 29 | 30 | - `name` (String) Short name of the Tenant resource. 31 | - `parent` (String) The parent id for the Tenant resource. 32 | 33 | ### Optional 34 | 35 | - `description` (String) A description of the resource. 36 | - `display_name` (String) User friendly name for the resource. 37 | - `security_domain` (String) Security domains can be used to group different resources under the same security domain. Although security domain is not resource itself currently, it follows a fqn format `organizations/myorg/securitydomains/mysecuritydomain`, and a child cannot override any ancestor's security domain. Once a security domain is assigned to a _Tenant_, all the children resources will belong to that security domain in the same way a _Workspace_ belongs to a _Tenant_, a _Workspace_ will also belong to the security domain assigned to the _Tenant_. Security domains can also be used to define _Security settings Authorization rules_ in which you can allow or deny request from or to a security domain. 38 | 39 | ### Read-Only 40 | 41 | - `id` (String) Fully-qualified name of the Tenant resource. 42 | 43 | 44 | -------------------------------------------------------------------------------- /internal/provider/resources/tenant/create.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tenant 16 | 17 | import ( 18 | "context" 19 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 20 | types "github.com/hashicorp/terraform-plugin-framework/types" 21 | v2 "github.com/tetrateio/api/tsb/v2" 22 | ) 23 | 24 | func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 25 | var model TenantModel 26 | resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) 27 | if resp.Diagnostics.HasError() { 28 | return 29 | } 30 | request := &v2.CreateTenantRequest{ 31 | Name: model.Name.ValueString(), 32 | Parent: model.Parent.ValueString(), 33 | Tenant: &v2.Tenant{ 34 | Description: model.Description.ValueString(), 35 | DisplayName: model.DisplayName.ValueString(), 36 | SecurityDomain: model.SecurityDomain.ValueString(), 37 | }, 38 | } 39 | tenant, err := r.client.CreateTenant(ctx, request) 40 | if err != nil { 41 | resp.Diagnostics.AddError("Error creating Tenant", "CreateTenant request failed: "+err.Error()) 42 | return 43 | } 44 | model.Id = types.StringValue(tenant.Fqn) 45 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 46 | } 47 | -------------------------------------------------------------------------------- /internal/provider/resources/tenant/update.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tenant 16 | 17 | import ( 18 | "context" 19 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 20 | v2 "github.com/tetrateio/api/tsb/v2" 21 | ) 22 | 23 | func (r *TenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 24 | var model TenantModel 25 | resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) 26 | if resp.Diagnostics.HasError() { 27 | return 28 | } 29 | request := &v2.GetTenantRequest{Fqn: model.Id.ValueString()} 30 | tenant, err := r.client.GetTenant(ctx, request) 31 | if err != nil { 32 | resp.Diagnostics.AddError("Error updating Tenant", "GetTenantRequest failed: "+err.Error()) 33 | return 34 | } 35 | updateTo := &v2.Tenant{ 36 | Description: model.Description.ValueString(), 37 | DisplayName: model.DisplayName.ValueString(), 38 | Etag: tenant.Etag, 39 | Fqn: model.Id.ValueString(), 40 | SecurityDomain: model.SecurityDomain.ValueString(), 41 | } 42 | _, err = r.client.UpdateTenant(ctx, updateTo) 43 | if err != nil { 44 | resp.Diagnostics.AddError("Error updating Tenant", "UpdateTenant failed: "+err.Error()) 45 | return 46 | } 47 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 48 | } 49 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | # this is just an example and not a requirement for provider building/publishing 4 | - go mod tidy 5 | builds: 6 | - env: 7 | # goreleaser does not work with CGO, it could also complicate 8 | # usage by users in CI/CD systems like Terraform Cloud where 9 | # they are unable to install libraries. 10 | - CGO_ENABLED=0 11 | mod_timestamp: '{{ .CommitTimestamp }}' 12 | flags: 13 | - -trimpath 14 | ldflags: 15 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' 16 | goos: 17 | - freebsd 18 | - windows 19 | - linux 20 | - darwin 21 | goarch: 22 | - amd64 23 | - '386' 24 | - arm 25 | - arm64 26 | ignore: 27 | - goos: darwin 28 | goarch: '386' 29 | binary: '{{ .ProjectName }}_v{{ .Version }}' 30 | archives: 31 | - format: zip 32 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 33 | checksum: 34 | extra_files: 35 | - glob: 'terraform-registry-manifest.json' 36 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' 37 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 38 | algorithm: sha256 39 | signs: 40 | - artifacts: checksum 41 | args: 42 | # if you are using this in a GitHub action or some other automated pipeline, you 43 | # need to pass the batch flag to indicate its not interactive. 44 | - "--batch" 45 | - "--local-user" 46 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 47 | - "--output" 48 | - "${signature}" 49 | - "--detach-sign" 50 | - "${artifact}" 51 | release: 52 | extra_files: 53 | - glob: 'terraform-registry-manifest.json' 54 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' 55 | # If you want to manually examine the release before its live, uncomment this line: 56 | # draft: true 57 | changelog: 58 | skip: true 59 | -------------------------------------------------------------------------------- /internal/provider/resources/user/create.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package user 16 | 17 | import ( 18 | "context" 19 | 20 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 21 | types "github.com/hashicorp/terraform-plugin-framework/types" 22 | v2 "github.com/tetrateio/api/tsb/v2" 23 | ) 24 | 25 | func (r *UserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 26 | var model UserModel 27 | resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) 28 | if resp.Diagnostics.HasError() { 29 | return 30 | } 31 | request := &v2.CreateUserRequest{ 32 | Name: model.Name.ValueString(), 33 | Parent: model.Parent.ValueString(), 34 | User: &v2.User{ 35 | DisplayName: model.DisplayName.ValueString(), 36 | Email: model.Email.ValueString(), 37 | FirstName: model.FirstName.ValueString(), 38 | LastName: model.LastName.ValueString(), 39 | LoginName: model.LoginName.ValueString(), 40 | SourceType: v2.SourceType(v2.SourceType_value[model.SourceType.ValueString()]), 41 | }, 42 | } 43 | user, err := r.client.CreateUser(ctx, request) 44 | if err != nil { 45 | resp.Diagnostics.AddError("Error creating User", "CreateUser request failed: "+err.Error()) 46 | return 47 | } 48 | model.Id = types.StringValue(user.Fqn) 49 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 50 | } 51 | -------------------------------------------------------------------------------- /generator/delete.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/dave/jennifer/jen" 19 | j "github.com/dave/jennifer/jen" 20 | ) 21 | 22 | func genDelete(r resource) *j.File { 23 | f := j.NewFile(r.lowerName) 24 | 25 | f.Func(). 26 | Parens(j.Id("r").Op("*").Add(r.structId)).Id("Delete"). 27 | Params(j.Id("ctx").Qual("context", "Context"), j.Id("req").Qual(Resource, "DeleteRequest"), j.Id("resp").Op("*").Qual(Resource, "DeleteResponse")). 28 | Block( 29 | j.Var().Id("model").Add(r.modelId), 30 | j.Id("resp").Dot("Diagnostics").Dot("Append").Call( 31 | j.Id("req").Dot("State").Dot("Get").Call(j.Id("ctx"), jen.Op("&").Id("model")).Op("..."), 32 | ), 33 | j.If(j.Id("resp").Dot("Diagnostics").Dot("HasError").Call()).Block(j.Return()), 34 | j.If( 35 | j.List(j.Id("_"), j.Id("err")).Op(":=").Id("r").Dot("client").Dot("Delete"+r.Name).Call( 36 | j.Id("ctx"), 37 | j.Op("&").Qual(r.PkgImportPath, "Delete"+r.Name+"Request").Values( 38 | j.Dict{j.Id("Fqn"): jen.Id("model").Dot("Id").Dot("ValueString").Call()}, 39 | ), 40 | ), 41 | j.Err().Op("!=").Nil(), 42 | ).Block( 43 | j.Id("resp").Dot("Diagnostics").Dot("AddError").Call( 44 | j.Lit("Error deleting "+r.Name), 45 | j.Lit("Delete"+r.Name+" request failed: ").Op("+").Err().Dot("Error").Call(), 46 | ), 47 | j.Return(), 48 | ), 49 | ) 50 | 51 | return f 52 | } 53 | -------------------------------------------------------------------------------- /internal/provider/resources/user/update.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package user 16 | 17 | import ( 18 | "context" 19 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 20 | v2 "github.com/tetrateio/api/tsb/v2" 21 | ) 22 | 23 | func (r *UserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 24 | var model UserModel 25 | resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) 26 | if resp.Diagnostics.HasError() { 27 | return 28 | } 29 | request := &v2.GetUserRequest{Fqn: model.Id.ValueString()} 30 | user, err := r.client.GetUser(ctx, request) 31 | if err != nil { 32 | resp.Diagnostics.AddError("Error updating User", "GetUserRequest failed: "+err.Error()) 33 | return 34 | } 35 | updateTo := &v2.User{ 36 | DisplayName: model.DisplayName.ValueString(), 37 | Email: model.Email.ValueString(), 38 | Etag: user.Etag, 39 | FirstName: model.FirstName.ValueString(), 40 | Fqn: model.Id.ValueString(), 41 | LastName: model.LastName.ValueString(), 42 | LoginName: model.LoginName.ValueString(), 43 | SourceType: v2.SourceType(v2.SourceType_value[model.SourceType.ValueString()]), 44 | } 45 | _, err = r.client.UpdateUser(ctx, updateTo) 46 | if err != nil { 47 | resp.Diagnostics.AddError("Error updating User", "UpdateUser failed: "+err.Error()) 48 | return 49 | } 50 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | pull_request: 8 | branches: [ "*" ] 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set up Go 16 | uses: actions/setup-go@v3 17 | with: 18 | go-version: 1.19 19 | - name: Install licenser 20 | run: 'go install github.com/liamawhite/licenser@v0.7.0' 21 | - name: Configure SSH Agent 22 | uses: webfactory/ssh-agent@v0.4.1 23 | with: 24 | ssh-private-key: ${{ secrets.TETRATE_CI_SSH_PRIVATE_KEY }} 25 | - name: Disable Strict Host Key Checking for SSH 26 | run: mkdir -p ~/.ssh && echo -e "\tStrictHostKeyChecking no" >> ~/.ssh/config && ssh-keyscan github.com >> ~/.ssh/known_hosts 27 | - name: Pull over SSH instead of HTTPS 28 | working-directory: . 29 | run: echo -e '[url "git@github.com:"]\n\tinsteadOf = https://github.com/' | sudo tee /etc/gitconfig 30 | 31 | - name: Verify licenses 32 | run: make check 33 | env: 34 | GOPRIVATE: github.com/tetrateio/* 35 | 36 | build: 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v3 40 | 41 | - name: Set up Go 42 | uses: actions/setup-go@v3 43 | with: 44 | go-version: 1.19 45 | - name: Configure SSH Agent 46 | uses: webfactory/ssh-agent@v0.4.1 47 | with: 48 | ssh-private-key: ${{ secrets.TETRATE_CI_SSH_PRIVATE_KEY }} 49 | - name: Disable Strict Host Key Checking for SSH 50 | run: mkdir -p ~/.ssh && echo -e "\tStrictHostKeyChecking no" >> ~/.ssh/config && ssh-keyscan github.com >> ~/.ssh/known_hosts 51 | - name: Pull over SSH instead of HTTPS 52 | working-directory: . 53 | run: echo -e '[url "git@github.com:"]\n\tinsteadOf = https://github.com/' | sudo tee /etc/gitconfig 54 | 55 | - name: Build 56 | run: make build 57 | env: 58 | GOPRIVATE: github.com/tetrateio/* 59 | 60 | 61 | -------------------------------------------------------------------------------- /internal/provider/resources/user/resource.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package user 16 | 17 | import ( 18 | "context" 19 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 20 | v2 "github.com/tetrateio/api/tsb/v2" 21 | helpers "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 22 | ) 23 | 24 | // Ensure provider defined types fully satisfy framework interfaces 25 | var _ resource.Resource = &UserResource{} 26 | var _ resource.ResourceWithImportState = &UserResource{} 27 | 28 | // Constructor 29 | func NewResource() resource.Resource { 30 | return &UserResource{} 31 | } 32 | 33 | // Resource struct definition 34 | type UserResource struct { 35 | client v2.TeamsClient 36 | } 37 | 38 | // Implement metadata function from Terraform resource interface 39 | func (r *UserResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 40 | resp.TypeName = req.ProviderTypeName + "_user" 41 | } 42 | 43 | // Implement configure function from Terraform resource interface 44 | func (r *UserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 45 | clients := helpers.BuildClientsResource(req, resp) 46 | if resp.Diagnostics.HasError() || clients == nil { 47 | return 48 | } 49 | r.client = clients.User 50 | } 51 | 52 | // Implement schema function from Terraform resource interface 53 | func (*UserResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { 54 | resp.Schema = v2.UserSchema() 55 | } 56 | -------------------------------------------------------------------------------- /internal/provider/resources/tenant/resource.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tenant 16 | 17 | import ( 18 | "context" 19 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 20 | v2 "github.com/tetrateio/api/tsb/v2" 21 | helpers "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 22 | ) 23 | 24 | // Ensure provider defined types fully satisfy framework interfaces 25 | var _ resource.Resource = &TenantResource{} 26 | var _ resource.ResourceWithImportState = &TenantResource{} 27 | 28 | // Constructor 29 | func NewResource() resource.Resource { 30 | return &TenantResource{} 31 | } 32 | 33 | // Resource struct definition 34 | type TenantResource struct { 35 | client v2.TenantsClient 36 | } 37 | 38 | // Implement metadata function from Terraform resource interface 39 | func (r *TenantResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 40 | resp.TypeName = req.ProviderTypeName + "_tenant" 41 | } 42 | 43 | // Implement configure function from Terraform resource interface 44 | func (r *TenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 45 | clients := helpers.BuildClientsResource(req, resp) 46 | if resp.Diagnostics.HasError() || clients == nil { 47 | return 48 | } 49 | r.client = clients.Tenant 50 | } 51 | 52 | // Implement schema function from Terraform resource interface 53 | func (*TenantResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { 54 | resp.Schema = v2.TenantSchema() 55 | } 56 | -------------------------------------------------------------------------------- /internal/provider/resources/tenant/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tenant 16 | 17 | import ( 18 | "context" 19 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 20 | types "github.com/hashicorp/terraform-plugin-framework/types" 21 | v2 "github.com/tetrateio/api/tsb/v2" 22 | api "github.com/tetrateio/tetrate/pkg/api" 23 | helpers "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 24 | ) 25 | 26 | func (r *TenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 27 | var model TenantModel 28 | resp.Diagnostics.Append(req.State.Get(ctx, &model)...) 29 | if resp.Diagnostics.HasError() { 30 | return 31 | } 32 | request := &v2.GetTenantRequest{Fqn: model.Id.ValueString()} 33 | tenant, err := r.client.GetTenant(ctx, request) 34 | if err != nil { 35 | resp.Diagnostics.AddError("Error reading Tenant", "GetTenantRequest failed: "+err.Error()) 36 | return 37 | } 38 | meta, err := helpers.FromFQN(api.TenantKind, model.Id.ValueString()) 39 | if err != nil { 40 | resp.Diagnostics.AddError("Error readingTenant", "FQN parsing failed: "+err.Error()) 41 | return 42 | } 43 | model.Id = types.StringValue(tenant.Fqn) 44 | model.Name = types.StringValue(meta.Name) 45 | model.Parent = types.StringValue(helpers.ParentFQN(api.TenantKind, meta)) 46 | model.DisplayName = types.StringValue(tenant.DisplayName) 47 | model.SecurityDomain = types.StringValue(tenant.SecurityDomain) 48 | model.Description = types.StringValue(tenant.Description) 49 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 50 | } 51 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "context" 19 | "flag" 20 | "log" 21 | 22 | "github.com/hashicorp/terraform-plugin-framework/providerserver" 23 | 24 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider" 25 | ) 26 | 27 | // Run "go generate" to format example terraform files and generate the docs for the registry/website 28 | 29 | // If you do not have terraform installed, you can remove the formatting command, but its suggested to 30 | // ensure the documentation is formatted properly. 31 | //go:generate terraform fmt -recursive ./examples/ 32 | 33 | // Run the docs generation tool, check its repository for more information on how it works and how docs 34 | // can be customized. 35 | //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs 36 | 37 | var ( 38 | // these will be set by the goreleaser configuration 39 | // to appropriate values for the compiled binary 40 | version string = "dev" 41 | 42 | // goreleaser can also pass the specific commit if you want 43 | // commit string = "" 44 | ) 45 | 46 | func main() { 47 | var debug bool 48 | 49 | flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") 50 | flag.Parse() 51 | 52 | opts := providerserver.ServeOpts{ 53 | // TODO: Update this string with the published name of your provider. 54 | Address: "registry.terraform.io/tetratelabs/tsb", 55 | Debug: debug, 56 | } 57 | 58 | err := providerserver.Serve(context.Background(), provider.New(version), opts) 59 | 60 | if err != nil { 61 | log.Fatal(err.Error()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /internal/provider/datasources/organization/organization_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package organization_test 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "testing" 21 | "text/template" 22 | 23 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 24 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/test" 25 | ) 26 | 27 | func TestAccOrganizationDataSource(t *testing.T) { 28 | org := orgConfig{Id: fmt.Sprintf("organizations/%s", test.AccOrganizationName), Name: test.AccOrganizationName} 29 | resource.Test(t, resource.TestCase{ 30 | PreCheck: func() { test.AccPreCheck(t) }, 31 | ProtoV6ProviderFactories: test.AccProtoV6ProviderFactories, 32 | Steps: []resource.TestStep{ 33 | // Read testing 34 | { 35 | Config: test.BuildConfig(t, org.Block(t)), 36 | Check: resource.ComposeAggregateTestCheckFunc( 37 | resource.TestCheckResourceAttr("data.tsb_organization."+test.AccOrganizationName, "id", org.Id), 38 | resource.TestCheckResourceAttrSet("data.tsb_organization."+test.AccOrganizationName, "display_name"), 39 | ), 40 | }, 41 | }, 42 | }) 43 | } 44 | 45 | type orgConfig struct { 46 | Name string 47 | Id string 48 | Description string 49 | DisplayName string 50 | } 51 | 52 | func (c orgConfig) Block(t *testing.T) string { 53 | tmpl, _ := template.New("org").Parse(orgTmpl) 54 | w := bytes.NewBuffer(nil) 55 | if err := tmpl.Execute(w, c); err != nil { 56 | t.Fatal("unable to build org block: %w", err) 57 | } 58 | return w.String() 59 | } 60 | 61 | const orgTmpl = ` 62 | data "tsb_organization" "{{.Name}}" { 63 | id = "{{.Id}}" 64 | } 65 | ` 66 | -------------------------------------------------------------------------------- /internal/provider/resources/user/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package user 16 | 17 | import ( 18 | "context" 19 | 20 | resource "github.com/hashicorp/terraform-plugin-framework/resource" 21 | types "github.com/hashicorp/terraform-plugin-framework/types" 22 | v2 "github.com/tetrateio/api/tsb/v2" 23 | api "github.com/tetrateio/tetrate/pkg/api" 24 | helpers "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 25 | ) 26 | 27 | func (r *UserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 28 | var model UserModel 29 | resp.Diagnostics.Append(req.State.Get(ctx, &model)...) 30 | if resp.Diagnostics.HasError() { 31 | return 32 | } 33 | request := &v2.GetUserRequest{Fqn: model.Id.ValueString()} 34 | user, err := r.client.GetUser(ctx, request) 35 | if err != nil { 36 | resp.Diagnostics.AddError("Error reading User", "GetUserRequest failed: "+err.Error()) 37 | return 38 | } 39 | meta, err := helpers.FromFQN(api.UserKind, model.Id.ValueString()) 40 | if err != nil { 41 | resp.Diagnostics.AddError("Error readingUser", "FQN parsing failed: "+err.Error()) 42 | return 43 | } 44 | model.Id = types.StringValue(user.Fqn) 45 | model.Name = types.StringValue(meta.Name) 46 | model.Parent = types.StringValue(helpers.ParentFQN(api.UserKind, meta)) 47 | model.FirstName = types.StringValue(user.FirstName) 48 | model.SourceType = types.StringValue(v2.SourceType_name[int32(user.SourceType)]) 49 | model.DisplayName = types.StringValue(user.DisplayName) 50 | model.LastName = types.StringValue(user.LastName) 51 | model.LoginName = types.StringValue(user.LoginName) 52 | model.Email = types.StringValue(user.Email) 53 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 54 | } 55 | -------------------------------------------------------------------------------- /generator/update.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | j "github.com/dave/jennifer/jen" 19 | "github.com/samber/lo" 20 | ) 21 | 22 | func genUpdate(r resource) *j.File { 23 | f := j.NewFile(r.lowerName) 24 | 25 | f.Func(). 26 | Parens(j.Id("r").Op("*").Add(r.structId)).Id("Update"). 27 | Params(j.Id("ctx").Qual("context", "Context"), j.Id("req").Qual(Resource, "UpdateRequest"), j.Id("resp").Op("*").Qual(Resource, "UpdateResponse")). 28 | Block( 29 | mergeCode( 30 | r.LoadPlanIntoModel(), 31 | 32 | // Get the latest etag 33 | j.Id("request").Op(":=").Op("&").Qual(r.PkgImportPath, "Get"+r.Name+"Request").Values(j.Dict{ 34 | j.Id("Fqn"): j.Id("model").Dot("Id").Dot("ValueString").Call(), 35 | }), 36 | j.List(j.Id(r.lowerName), j.Id("err")).Op(":=").Id("r").Dot("client").Dot("Get"+r.Name).Call( 37 | j.Id("ctx"), 38 | j.Id("request"), 39 | ), 40 | handleError("Error updating "+r.Name, "Get"+r.Name+"Request failed"), 41 | 42 | // Do the actual request 43 | j.Id("updateTo").Op(":=").Op("&").Qual(r.PkgImportPath, r.Name).Values(buildUpdateRequest(r)), 44 | j.List(j.Op("_"), j.Id("err")).Op("=").Id("r").Dot("client").Dot("Update"+r.Name).Call( 45 | j.Id("ctx"), 46 | j.Id("updateTo"), 47 | ), 48 | handleError("Error updating "+r.Name, "Update"+r.Name+" failed"), 49 | 50 | saveState(), 51 | )..., 52 | ) 53 | 54 | return f 55 | } 56 | 57 | func buildUpdateRequest(r resource) j.Dict { 58 | res := buildResource(lo.OmitByKeys(r.Schema.Attributes, []string{"parent", "id", "name"}), []string{}, r) 59 | 60 | res[j.Id("Fqn")] = j.Id("model").Dot("Id").Dot("ValueString").Call() 61 | res[j.Id("Etag")] = j.Id(r.lowerName).Dot("Etag") 62 | 63 | return res 64 | } 65 | -------------------------------------------------------------------------------- /internal/provider/resources/serviceaccount/update.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serviceaccount 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/hashicorp/terraform-plugin-framework/resource" 21 | tsbv2 "github.com/tetrateio/api/tsb/v2" 22 | ) 23 | 24 | func (r *ServiceAccountResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { 25 | // Load plan into the model 26 | var model serviceAccountResourceModel 27 | resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) 28 | if resp.Diagnostics.HasError() { 29 | return 30 | } 31 | 32 | // Load current state so we can persist keys 33 | var state serviceAccountResourceModel 34 | resp.Diagnostics.Append(req.State.Get(ctx, &state)...) 35 | if resp.Diagnostics.HasError() { 36 | return 37 | } 38 | model.Keys = state.Keys 39 | 40 | // We need to run a get to retreive the latest etag and set fqn as its not going to be in the plan (only state) 41 | tenant, err := r.client.GetServiceAccount(ctx, &tsbv2.GetServiceAccountRequest{Fqn: model.Id.ValueString()}) 42 | if err != nil { 43 | resp.Diagnostics.AddError("Error updating ServiceAccount", "GetServiceAccount request failed: "+err.Error()) 44 | return 45 | } 46 | 47 | // Do the actual update 48 | _, err = r.client.UpdateServiceAccount(ctx, &tsbv2.ServiceAccount{ 49 | Fqn: model.Id.ValueString(), 50 | DisplayName: model.DisplayName.ValueString(), 51 | Etag: tenant.Etag, 52 | Description: model.Description.ValueString(), 53 | }) 54 | if err != nil { 55 | resp.Diagnostics.AddError("Error updating ServiceAccount", "UpdateServiceAccount request failed: "+err.Error()) 56 | return 57 | } 58 | 59 | // Save updated data into Terraform state 60 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 61 | } 62 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This GitHub action can publish assets for release when a tag is created. 2 | # Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). 3 | # 4 | # This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your 5 | # private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` 6 | # secret. If you would rather own your own GPG handling, please fork this action 7 | # or use an alternative one for key handling. 8 | # 9 | # You will need to pass the `--batch` flag to `gpg` in your signing step 10 | # in `goreleaser` to indicate this is being used in a non-interactive mode. 11 | # 12 | name: release 13 | on: 14 | push: 15 | tags: 16 | - 'v*' 17 | permissions: 18 | contents: write 19 | jobs: 20 | goreleaser: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 25 | - name: Unshallow 26 | run: git fetch --prune --unshallow 27 | - name: Set up Go 28 | uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0 29 | with: 30 | go-version-file: 'go.mod' 31 | cache: true 32 | - name: Configure SSH Agent 33 | uses: webfactory/ssh-agent@v0.4.1 34 | with: 35 | ssh-private-key: ${{ secrets.TETRATE_CI_SSH_PRIVATE_KEY }} 36 | - name: Disable Strict Host Key Checking for SSH 37 | run: mkdir -p ~/.ssh && echo -e "\tStrictHostKeyChecking no" >> ~/.ssh/config && ssh-keyscan github.com >> ~/.ssh/known_hosts 38 | - name: Pull over SSH instead of HTTPS 39 | working-directory: . 40 | run: echo -e '[url "git@github.com:"]\n\tinsteadOf = https://github.com/' | sudo tee /etc/gitconfig 41 | - name: Import GPG key 42 | uses: crazy-max/ghaction-import-gpg@111c56156bcc6918c056dbef52164cfa583dc549 # v5.2.0 43 | id: import_gpg 44 | with: 45 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} 46 | passphrase: ${{ secrets.PASSPHRASE }} 47 | - name: Run GoReleaser 48 | uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4.2.0 49 | with: 50 | version: latest 51 | args: release --rm-dist 52 | env: 53 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 54 | # GitHub sets this automatically 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | -------------------------------------------------------------------------------- /generator/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "os" 19 | "path/filepath" 20 | 21 | tsbv2 "github.com/tetrateio/api/tsb/v2" 22 | ) 23 | 24 | var resources = []resource{ 25 | *NewResource( 26 | WithName("Tenant"), 27 | WithPkgImportPath(TsbV2), 28 | WithClient("TenantsClient"), 29 | WithSchema(tsbv2.TenantSchema()), 30 | ), 31 | *NewResource( 32 | WithName("User"), 33 | WithPkgImportPath(TsbV2), 34 | WithClient("TeamsClient"), 35 | WithSchema(tsbv2.UserSchema()), 36 | ), 37 | } 38 | 39 | const ( 40 | Resource = "github.com/hashicorp/terraform-plugin-framework/resource" 41 | Types = "github.com/hashicorp/terraform-plugin-framework/types" 42 | Path = "github.com/hashicorp/terraform-plugin-framework/path" 43 | TsbV2 = "github.com/tetrateio/api/tsb/v2" 44 | Helpers = "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 45 | PkgAPI = "github.com/tetrateio/tetrate/pkg/api" 46 | ) 47 | 48 | func main() { 49 | // Must be ran from root 50 | repoRoot, _ := os.Getwd() 51 | 52 | for _, r := range resources { 53 | // Make the dir 54 | resourceDir := filepath.Join(repoRoot, "internal", "provider", "resources", r.lowerName) 55 | os.MkdirAll(resourceDir, 0755) 56 | 57 | // Write the files 58 | os.WriteFile(filepath.Join(resourceDir, "resource.go"), []byte(genResource(r).GoString()), 0644) 59 | os.WriteFile(filepath.Join(resourceDir, "model.go"), []byte(genModel(r).GoString()), 0644) 60 | os.WriteFile(filepath.Join(resourceDir, "import.go"), []byte(genImport(r).GoString()), 0644) 61 | os.WriteFile(filepath.Join(resourceDir, "create.go"), []byte(genCreate(r).GoString()), 0644) 62 | os.WriteFile(filepath.Join(resourceDir, "read.go"), []byte(genRead(r).GoString()), 0644) 63 | os.WriteFile(filepath.Join(resourceDir, "update.go"), []byte(genUpdate(r).GoString()), 0644) 64 | os.WriteFile(filepath.Join(resourceDir, "delete.go"), []byte(genDelete(r).GoString()), 0644) 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /internal/provider/resources/serviceaccount/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serviceaccount 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/hashicorp/terraform-plugin-framework/resource" 21 | types "github.com/hashicorp/terraform-plugin-framework/types" 22 | tsbv2 "github.com/tetrateio/api/tsb/v2" 23 | "github.com/tetrateio/tetrate/pkg/api" 24 | "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 25 | ) 26 | 27 | func (r *ServiceAccountResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 28 | // Load state into the model 29 | var model serviceAccountResourceModel 30 | resp.Diagnostics.Append(req.State.Get(ctx, &model)...) 31 | if resp.Diagnostics.HasError() { 32 | return 33 | } 34 | 35 | serviceAccount, err := r.client.GetServiceAccount( 36 | ctx, 37 | &tsbv2.GetServiceAccountRequest{ 38 | Fqn: model.Id.ValueString(), 39 | KeyEncoding: tsbv2.ServiceAccount_KeyPair_Encoding(tsbv2.ServiceAccount_KeyPair_Encoding_value[model.KeyEncoding.ValueString()]), 40 | }, 41 | ) 42 | if err != nil { 43 | resp.Diagnostics.AddError("Error reading ServiceAccount", "GetServiceAccount request failed: "+err.Error()) 44 | return 45 | } 46 | 47 | meta, err := helpers.FromFQN(api.ServiceAccountKind, model.Id.ValueString()) 48 | if err != nil { 49 | resp.Diagnostics.AddError("Error reading ServiceAccount", "FQN parsing failed: "+err.Error()) 50 | return 51 | } 52 | 53 | model.Id = types.StringValue(serviceAccount.Fqn) 54 | model.Name = types.StringValue(meta.Name) 55 | model.Parent = types.StringValue(helpers.ParentFQN(api.ServiceAccountKind, meta)) 56 | model.Description = types.StringValue(serviceAccount.Description) 57 | model.DisplayName = types.StringValue(serviceAccount.DisplayName) 58 | // Keys cannot be read, they are only returned on create so don't touch them! 59 | 60 | // Save model into Terraform model 61 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 62 | } 63 | -------------------------------------------------------------------------------- /internal/helpers/fqn_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package helpers 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | 22 | types "github.com/tetrateio/api/tsb/types/v2" 23 | 24 | "github.com/tetrateio/tetrate/pkg/api" 25 | ) 26 | 27 | func TestFromFQN(t *testing.T) { 28 | tests := []struct { 29 | kind string 30 | fqn string 31 | want *types.ObjectMeta 32 | wantErr bool 33 | }{ 34 | // Edge cases 35 | {fqn: "/organizations/test", kind: api.OrganizationKind, want: &types.ObjectMeta{Name: "test"}}, 36 | {fqn: "organizations/test", kind: api.TenantKind, wantErr: true}, 37 | {fqn: "organizations/test/tenants/test", kind: api.OrganizationKind, wantErr: true}, 38 | {fqn: "test", kind: api.OrganizationKind, wantErr: true}, 39 | {fqn: "", kind: api.OrganizationKind, wantErr: true}, 40 | {fqn: "/", kind: api.OrganizationKind, wantErr: true}, 41 | {kind: "not supported", wantErr: true}, 42 | 43 | // Real cases 44 | {fqn: "organizations/test", kind: api.OrganizationKind, want: &types.ObjectMeta{Name: "test"}}, 45 | {fqn: "organizations/test/tenants/test", kind: api.TenantKind, want: &types.ObjectMeta{Organization: "test", Name: "test"}}, 46 | {fqn: "organizations/test/teams/test", kind: api.TeamKind, want: &types.ObjectMeta{Organization: "test", Name: "test"}}, 47 | {fqn: "organizations/test/serviceaccounts/test", kind: api.ServiceAccountKind, want: &types.ObjectMeta{Organization: "test", Name: "test"}}, 48 | {fqn: "organizations/test/users/test", kind: api.UserKind, want: &types.ObjectMeta{Organization: "test", Name: "test"}}, 49 | } 50 | for _, tt := range tests { 51 | t.Run(fmt.Sprintf("%v %v", tt.kind, tt.fqn), func(t *testing.T) { 52 | got, err := FromFQN(tt.kind, tt.fqn) 53 | if (err != nil) != tt.wantErr { 54 | t.Errorf("FromFQN() error = %v, wantErr %v", err, tt.wantErr) 55 | return 56 | } 57 | if !reflect.DeepEqual(got, tt.want) { 58 | t.Errorf("FromFQN() = %v, want %v", got, tt.want) 59 | } 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /internal/provider/datasources/organization/organization.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package organization 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/hashicorp/terraform-plugin-framework/datasource" 21 | "github.com/hashicorp/terraform-plugin-framework/datasource/schema" 22 | types "github.com/hashicorp/terraform-plugin-framework/types" 23 | tsbv2 "github.com/tetrateio/api/tsb/v2" 24 | 25 | "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 26 | ) 27 | 28 | // Ensure provider defined types fully satisfy framework interfaces 29 | var _ datasource.DataSource = &OrganizationDataSource{} 30 | 31 | func NewDataSource() datasource.DataSource { 32 | return &OrganizationDataSource{} 33 | } 34 | 35 | // OrganizationDataSource defines the data source implementation. 36 | type OrganizationDataSource struct { 37 | client tsbv2.OrganizationsClient 38 | } 39 | 40 | type organizationDataSourceModel struct { 41 | Id types.String `tfsdk:"id"` 42 | DisplayName types.String `tfsdk:"display_name"` 43 | } 44 | 45 | func (d *OrganizationDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 46 | resp.TypeName = req.ProviderTypeName + "_organization" 47 | } 48 | 49 | // Schema implements datasource.DataSource 50 | func (*OrganizationDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { 51 | resp.Schema = schema.Schema{ 52 | Attributes: map[string]schema.Attribute{ 53 | "id": schema.StringAttribute{ 54 | Required: true, 55 | Description: "Fully-qualified name of the resource.", 56 | }, 57 | "display_name": schema.StringAttribute{ 58 | Description: "User friendly name for the resource.", 59 | Optional: true, 60 | }, 61 | }, 62 | } 63 | } 64 | 65 | func (d *OrganizationDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { 66 | clients := helpers.BuildClientsDatasource(ctx, req, resp) 67 | if resp.Diagnostics.HasError() || clients == nil { 68 | return 69 | } 70 | d.client = clients.Organization 71 | } 72 | -------------------------------------------------------------------------------- /internal/helpers/fqn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package helpers 16 | 17 | import ( 18 | "fmt" 19 | "regexp" 20 | "strings" 21 | 22 | types "github.com/tetrateio/api/tsb/types/v2" 23 | 24 | "github.com/tetrateio/tetrate/pkg/api" 25 | "github.com/tetrateio/tetrate/pkg/fqn" 26 | ) 27 | 28 | func FQN(kind string, m *types.ObjectMeta) string { 29 | return fqn.Tctl{}.FromMeta(api.KindToLatestAPIVersion[kind], kind, m) 30 | } 31 | 32 | func ParentFQN(kind string, m *types.ObjectMeta) string { 33 | return fqn.Tctl{}.ParentFromObjectMeta(&types.Object{ 34 | ApiVersion: api.KindToLatestAPIVersion[kind], 35 | Kind: kind, 36 | Metadata: m, 37 | }) 38 | } 39 | 40 | var apiToRegexp = map[string]*regexp.Regexp{ 41 | api.OrganizationKind: regexp.MustCompile("^organizations/([^/]+)$"), 42 | api.TenantKind: regexp.MustCompile("^organizations/([^/]+)/tenants/([^/]+)$"), 43 | api.TeamKind: regexp.MustCompile("^organizations/([^/]+)/teams/([^/]+)$"), 44 | api.UserKind: regexp.MustCompile("^organizations/([^/]+)/users/([^/]+)$"), 45 | api.ServiceAccountKind: regexp.MustCompile("^organizations/([^/]+)/serviceaccounts/([^/]+)$"), 46 | } 47 | 48 | func FromFQN(kind string, fqn string) (*types.ObjectMeta, error) { 49 | result := &types.ObjectMeta{} 50 | fqn = strings.TrimPrefix(fqn, "/") // trim leading / 51 | 52 | if _, ok := apiToRegexp[kind]; !ok { 53 | return nil, fmt.Errorf("kind '%v', is not yet supported", kind) 54 | } 55 | match := apiToRegexp[kind].FindAllStringSubmatch(fqn, 1) 56 | 57 | err := fmt.Errorf("unable to parse fqn %v into kind %v", fqn, kind) 58 | if len(match) != 1 { 59 | return nil, err 60 | } 61 | 62 | // There should only be one total match and 0 is the whole string so remove that 63 | groups := match[0][1:] 64 | 65 | switch kind { 66 | case api.OrganizationKind: 67 | if len(groups) != 1 { 68 | return nil, err 69 | } 70 | result.Name = groups[0] 71 | case api.TenantKind, api.TeamKind, api.ServiceAccountKind, api.UserKind: 72 | if len(groups) != 2 { 73 | return nil, err 74 | } 75 | result.Organization = groups[0] 76 | result.Name = groups[1] 77 | } 78 | 79 | return result, nil 80 | } 81 | -------------------------------------------------------------------------------- /docs/resources/service_account.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "tsb_service_account Resource - terraform-provider-tsb" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # tsb_service_account (Resource) 10 | 11 | 12 | 13 | ## Example Usage 14 | 15 | ```terraform 16 | resource "tsb_service_account" "example" { 17 | name = "ciautomation" 18 | parent = "organizations/tetrate" 19 | display_name = "CI Automation" 20 | description = "Used by our CI tool to create resources" 21 | key_encoding = "JWT" 22 | } 23 | ``` 24 | 25 | 26 | ## Schema 27 | 28 | ### Required 29 | 30 | - `name` (String) The short name for the resource to be created. 31 | - `parent` (String) The parent ID of the Service Account. 32 | 33 | ### Optional 34 | 35 | - `description` (String) A description of the resource. 36 | - `display_name` (String) User friendly name for the resource. 37 | - `key_encoding` (String) The format in which the generated key pairs will be returned. If not set keys are returned in PEM format. 38 | 39 | ### Read-Only 40 | 41 | - `id` (String) Fully-qualified name of the resource. 42 | - `keys` (Attributes List) Keys associated with the service account. A default key-pair is automatically created when the Service Account is created. Note that TSB does not store the private keys, so it is up to the client to store the returned private keys securely, as they are only returned once after creation. Additional keys can be added (and deleted) by using the corresponding key management APIs. +protoc-gen-terraform:computed (see [below for nested schema](#nestedatt--keys)) 43 | 44 | 45 | ### Nested Schema for `keys` 46 | 47 | Read-Only: 48 | 49 | - `default_token` (String, Sensitive) A default access token that can be used to authenticate to TSB on behalf of the service account. TSB does not store this token and it is only returned when a service account key is created, similar to the private key. It is up to the client to store the token for future use or to use the TSB CLI to generate new tokens as explained in: https://docs.tetrate.io/service-bridge/latest/en-us/howto/service-accounts 50 | - `encoding` (String) Format in which the public and private keys are encoded. By default keys are returned in PEM format. 51 | - `id` (String) Unique identifier for this key-pair. This should be used as the `kid` (key id) when generating JWT tokens that are signed with this key-pair. 52 | - `private_key` (String, Sensitive) The encoded private key associated with the service account. TSB does not store the private key and it is up to the client to store it safely. The encoding format is determined by the `encoding` field. 53 | - `public_key` (String) The encoded public key associated with the service account. The encoding format is determined by the `encoding` field. 54 | 55 | 56 | -------------------------------------------------------------------------------- /generator/resource.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | j "github.com/dave/jennifer/jen" 19 | ) 20 | 21 | func genResource(r resource) *j.File { 22 | f := j.NewFile(r.lowerName) 23 | 24 | f.Comment("// Ensure provider defined types fully satisfy framework interfaces") 25 | f.Var().Id("_").Qual(Resource, "Resource").Op("=").Op("&").Add(r.structId).Values() 26 | f.Var().Id("_").Qual(Resource, "ResourceWithImportState").Op("=").Op("&").Add(r.structId).Values() 27 | 28 | f.Comment("// Constructor") 29 | f.Func().Id("NewResource").Params().Params(j.Qual(Resource, "Resource")).Block(j.Return().Op("&").Add(r.structId).Block()) 30 | 31 | f.Comment("// Resource struct definition") 32 | f.Type().Add(r.structId).Struct(j.Id("client").Qual(r.PkgImportPath, r.Client)) 33 | 34 | f.Comment("// Implement metadata function from Terraform resource interface") 35 | f.Func(). 36 | Parens(j.Id("r").Op("*").Add(r.structId)).Id("Metadata"). 37 | Params(j.Op("_").Qual("context", "Context"), j.Id("req").Qual(Resource, "MetadataRequest"), j.Id("resp").Op("*").Qual(Resource, "MetadataResponse")). 38 | Block( 39 | j.Id("resp").Dot("TypeName").Op("=").Id("req").Dot("ProviderTypeName").Op("+").Lit("_" + r.lowerName), 40 | ) 41 | 42 | f.Comment("// Implement configure function from Terraform resource interface") 43 | f.Func(). 44 | Parens(j.Id("r").Op("*").Add(r.structId)).Id("Configure"). 45 | Params(j.Op("_").Qual("context", "Context"), j.Id("req").Qual(Resource, "ConfigureRequest"), j.Id("resp").Op("*").Qual(Resource, "ConfigureResponse")). 46 | Block( 47 | j.Id("clients").Op(":=").Qual(Helpers, "BuildClientsResource").Call(j.Id("req"), j.Id("resp")), 48 | j.If(j.Id("resp").Dot("Diagnostics").Dot("HasError").Call().Op("||").Id("clients").Op("==").Nil().Block(j.Return())), 49 | j.Id("r").Dot("client").Op("=").Id("clients").Dot(r.Name), 50 | ) 51 | 52 | f.Comment("// Implement schema function from Terraform resource interface") 53 | f.Func(). 54 | Parens(j.Op("*").Add(r.structId)).Id("Schema"). 55 | Params(j.Op("_").Qual("context", "Context"), j.Op("_").Qual(Resource, "SchemaRequest"), j.Id("resp").Op("*").Qual(Resource, "SchemaResponse")). 56 | Block( 57 | j.Id("resp").Dot("Schema").Op("=").Qual(r.PkgImportPath, r.Name+"Schema").Call(), 58 | ) 59 | 60 | return f 61 | } 62 | -------------------------------------------------------------------------------- /internal/provider/validators/address_includes_port.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package validators 16 | 17 | import ( 18 | "context" 19 | "strconv" 20 | "strings" 21 | 22 | "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" 23 | "github.com/hashicorp/terraform-plugin-framework/schema/validator" 24 | ) 25 | 26 | var _ validator.String = addressIncludesPort{} 27 | 28 | // addressIncludesPort validates that the string passed has both a hostname and port. 29 | type addressIncludesPort struct{} 30 | 31 | // ValidateString implements validator.String 32 | func (v addressIncludesPort) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { 33 | // s, ok := req. 34 | // if !ok { 35 | // response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( 36 | // request.AttributePath, 37 | // validator.Description(ctx), 38 | // s.ValueString(), 39 | // )) 40 | // } 41 | 42 | // Check there is thing:thing 43 | split := strings.Split(req.ConfigValue.ValueString(), ":") 44 | if len(split) != 2 { 45 | resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( 46 | req.Path, 47 | v.Description(ctx), 48 | req.ConfigValue.ValueString(), 49 | )) 50 | return 51 | } 52 | 53 | // Check second part is a number 54 | if _, err := strconv.Atoi(split[1]); err != nil { 55 | resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( 56 | req.Path, 57 | v.Description(ctx), 58 | req.ConfigValue.ValueString(), 59 | )) 60 | return 61 | } 62 | } 63 | 64 | // Description describes the validation in plain text formatting. 65 | func (validator addressIncludesPort) Description(_ context.Context) string { 66 | return "address must be of format
:" 67 | } 68 | 69 | // MarkdownDescription describes the validation in Markdown formatting. 70 | func (validator addressIncludesPort) MarkdownDescription(ctx context.Context) string { 71 | return validator.Description(ctx) 72 | } 73 | 74 | // AddressIncludesPort returns an AttributeValidator which ensures that any passed 75 | // attribute value: 76 | // 77 | // - Is a string. 78 | // - Is in the format
:. 79 | // 80 | // Null (unconfigured) and unknown (known after apply) values are skipped. 81 | func AddressIncludesPort() validator.String { 82 | return addressIncludesPort{} 83 | } 84 | -------------------------------------------------------------------------------- /internal/helpers/clients.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package helpers 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "github.com/hashicorp/terraform-plugin-framework/datasource" 22 | "github.com/hashicorp/terraform-plugin-framework/resource" 23 | tsbv2 "github.com/tetrateio/api/tsb/v2" 24 | "google.golang.org/grpc" 25 | ) 26 | 27 | type Clients struct { 28 | Organization tsbv2.OrganizationsClient 29 | Tenant tsbv2.TenantsClient 30 | User tsbv2.TeamsClient 31 | ServiceAccount tsbv2.TeamsClient 32 | Team tsbv2.TeamsClient 33 | Cluster tsbv2.ClustersClient 34 | } 35 | 36 | func NewClients(cc *grpc.ClientConn) *Clients { 37 | return &Clients{ 38 | Organization: tsbv2.NewOrganizationsClient(cc), 39 | Tenant: tsbv2.NewTenantsClient(cc), 40 | Team: tsbv2.NewTeamsClient(cc), 41 | User: tsbv2.NewTeamsClient(cc), 42 | Cluster: tsbv2.NewClustersClient(cc), 43 | } 44 | } 45 | 46 | // Can't use generics for this yet... 47 | // See https://github.com/golang/go/issues/48522 48 | func BuildClientsDatasource(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) *Clients { 49 | // Prevent panic if the provider has not been configured. 50 | if req.ProviderData == nil { 51 | // resp.Diagnostics.AddError("Provider not configured", "Expected req.ProviderData to be populated, but was nil") 52 | return nil 53 | } 54 | 55 | clients, ok := req.ProviderData.(*Clients) 56 | if !ok { 57 | resp.Diagnostics.AddError( 58 | "Unexpected Resource Configure Type", 59 | fmt.Sprintf("Expected *clients, got: %T. Please report this issue to the provider developers.", req.ProviderData), 60 | ) 61 | return nil 62 | } 63 | return clients 64 | } 65 | 66 | // Can't use generics for this yet... 67 | // See https://github.com/golang/go/issues/48522 68 | func BuildClientsResource(req resource.ConfigureRequest, resp *resource.ConfigureResponse) *Clients { 69 | // Prevent panic if the provider has not been configured. 70 | if req.ProviderData == nil { 71 | // resp.Diagnostics.AddError("Provider not configured", "Expected req.ProviderData to be populated, but was nil") 72 | return nil 73 | } 74 | 75 | clients, ok := req.ProviderData.(*Clients) 76 | if !ok { 77 | resp.Diagnostics.AddError( 78 | "Unexpected Resource Configure Type", 79 | fmt.Sprintf("Expected *clients, got: %T. Please report this issue to the provider developers.", req.ProviderData), 80 | ) 81 | return nil 82 | } 83 | return clients 84 | } 85 | -------------------------------------------------------------------------------- /internal/provider/test/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import ( 18 | "bytes" 19 | "os" 20 | "strings" 21 | "testing" 22 | "text/template" 23 | 24 | "github.com/hashicorp/terraform-plugin-framework/providerserver" 25 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 26 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider" 27 | ) 28 | 29 | // AccProtoV6ProviderFactories are used to instantiate a provider during 30 | // acceptance testing. The factory function will be invoked for every Terraform 31 | // CLI command executed to create a provider server to which the CLI can 32 | // reattach. 33 | var ( 34 | AccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ 35 | "tsb": providerserver.NewProtocol6WithError(provider.New("test")()), 36 | } 37 | 38 | AccAddress = os.Getenv("TSB_ADDRESS") 39 | AccUsername = os.Getenv("TSB_USERNAME") 40 | AccPassword = os.Getenv("TSB_PASSWORD") 41 | AccOrganizationName = os.Getenv("TSB_ORGANIZATION") 42 | ) 43 | 44 | func AccPreCheck(t *testing.T) { 45 | // Verify all env vars were set 46 | if AccAddress == "" { 47 | t.Fatal("TSB_ADDRESS must be set for acceptance testing") 48 | } 49 | if AccUsername == "" { 50 | t.Fatal("TSB_USERNAME must be set for acceptance testing") 51 | } 52 | if AccPassword == "" { 53 | t.Fatal("TSB_PASSWORD must be set for acceptance testing") 54 | } 55 | if AccOrganizationName == "" { 56 | t.Fatal("TSB_PASSWORD must be set for acceptance testing") 57 | } 58 | } 59 | 60 | // Pass all non-provider blocks in 61 | // Provider is automatically added to the front 62 | func BuildConfig(t *testing.T, elems ...string) string { 63 | p := providerConfig{ 64 | Address: AccAddress, 65 | BasicAuth: basicAuthConfig{Username: AccUsername, Password: AccPassword}, 66 | } 67 | return strings.Join(append([]string{p.Block(t)}, elems...), "") 68 | } 69 | 70 | type providerConfig struct { 71 | Address string 72 | BasicAuth basicAuthConfig 73 | } 74 | 75 | func (c providerConfig) Block(t *testing.T) string { 76 | tmpl, _ := template.New("provider").Parse(providerTmpl) 77 | w := bytes.NewBuffer(nil) 78 | if err := tmpl.Execute(w, c); err != nil { 79 | t.Fatal("unable to build provider block: %w", err) 80 | } 81 | return w.String() 82 | } 83 | 84 | type basicAuthConfig struct { 85 | Username string 86 | Password string 87 | } 88 | 89 | const providerTmpl = ` 90 | provider "tsb" { 91 | address = "{{.Address}}" 92 | basic_auth = { 93 | username = "{{.BasicAuth.Username}}" 94 | password = "{{.BasicAuth.Password}}" 95 | } 96 | } 97 | ` 98 | -------------------------------------------------------------------------------- /internal/provider/resources/serviceaccount/create.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serviceaccount 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/hashicorp/terraform-plugin-framework/attr" 21 | "github.com/hashicorp/terraform-plugin-framework/resource" 22 | "github.com/hashicorp/terraform-plugin-framework/types" 23 | "github.com/samber/lo" 24 | tsbv2 "github.com/tetrateio/api/tsb/v2" 25 | ) 26 | 27 | func (r *ServiceAccountResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 28 | // Load plan into the model 29 | var model serviceAccountResourceModel 30 | resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) 31 | if resp.Diagnostics.HasError() { 32 | return 33 | } 34 | 35 | serviceAccount, err := r.client.CreateServiceAccount(ctx, &tsbv2.CreateServiceAccountRequest{ 36 | Parent: model.Parent.ValueString(), 37 | Name: model.Name.ValueString(), 38 | KeyEncoding: tsbv2.ServiceAccount_KeyPair_Encoding(tsbv2.ServiceAccount_KeyPair_Encoding_value[model.KeyEncoding.ValueString()]), 39 | ServiceAccount: &tsbv2.ServiceAccount{ 40 | DisplayName: model.DisplayName.ValueString(), 41 | Description: model.Description.ValueString(), 42 | }, 43 | }) 44 | if err != nil { 45 | resp.Diagnostics.AddError("Error creating ServiceAccount", "CreateServiceAccount request failed: "+err.Error()) 46 | return 47 | } 48 | 49 | model.Id = types.StringValue(serviceAccount.Fqn) 50 | 51 | // We have to handle keys differently because they can be null (during a read) 52 | // 53 | keys, diagErr := types.ListValue( 54 | types.ObjectType{ 55 | AttrTypes: map[string]attr.Type{ 56 | "id": types.StringType, 57 | "public_key": types.StringType, 58 | "private_key": types.StringType, 59 | "encoding": types.StringType, 60 | "default_token": types.StringType, 61 | }, 62 | }, 63 | lo.Map(serviceAccount.Keys, func(elem *tsbv2.ServiceAccount_KeyPair, _ int) attr.Value { 64 | return types.ObjectValueMust( 65 | map[string]attr.Type{ 66 | "id": types.StringType, 67 | "public_key": types.StringType, 68 | "private_key": types.StringType, 69 | "encoding": types.StringType, 70 | "default_token": types.StringType, 71 | }, 72 | map[string]attr.Value{ 73 | "id": types.StringValue(elem.Id), 74 | "public_key": types.StringValue(elem.PublicKey), 75 | "private_key": types.StringValue(elem.PrivateKey), 76 | "encoding": types.StringValue(tsbv2.ServiceAccount_KeyPair_Encoding_name[int32(elem.Encoding)]), 77 | "default_token": types.StringValue(elem.DefaultToken), 78 | }, 79 | ) 80 | }), 81 | ) 82 | resp.Diagnostics.Append(diagErr...) 83 | if resp.Diagnostics.HasError() { 84 | return 85 | } 86 | 87 | model.Keys = keys 88 | 89 | resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) 90 | } 91 | -------------------------------------------------------------------------------- /generator/create.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | 20 | j "github.com/dave/jennifer/jen" 21 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 22 | "github.com/samber/lo" 23 | ) 24 | 25 | func genCreate(r resource) *j.File { 26 | f := j.NewFile(r.lowerName) 27 | 28 | f.Func(). 29 | Parens(j.Id("r").Op("*").Add(r.structId)).Id("Create"). 30 | Params(j.Id("ctx").Qual("context", "Context"), j.Id("req").Qual(Resource, "CreateRequest"), j.Id("resp").Op("*").Qual(Resource, "CreateResponse")). 31 | Block( 32 | mergeCode( 33 | r.LoadPlanIntoModel(), 34 | 35 | // Do the actual request 36 | j.Id("request").Op(":=").Op("&").Qual(r.PkgImportPath, "Create"+r.Name+"Request").Values(buildCreateRequest(r)), 37 | j.List(j.Id(r.lowerName), j.Id("err")).Op(":=").Id("r").Dot("client").Dot("Create"+r.Name).Call( 38 | j.Id("ctx"), 39 | j.Id("request"), 40 | ), 41 | 42 | handleError("Error creating "+r.Name, "Create"+r.Name+" request failed"), 43 | 44 | // Set ID in the model and then save it to state 45 | j.Id("model").Dot("Id").Op("=").Qual(Types, "StringValue").Call(j.Id(r.lowerName).Dot("Fqn")), 46 | saveState(), 47 | )..., 48 | ) 49 | 50 | return f 51 | } 52 | 53 | func buildCreateRequest(r resource) j.Dict { 54 | res := j.Dict{ 55 | j.Id("Name"): j.Id("model").Dot("Name").Dot("ValueString").Call(), 56 | j.Id("Parent"): j.Id("model").Dot("Parent").Dot("ValueString").Call(), 57 | j.Id(r.Name): j.Op("&").Qual(r.PkgImportPath, r.Name).Values( 58 | // Filter out the top level or terraform only attributes 59 | buildResource(lo.OmitByKeys(r.Schema.Attributes, []string{"parent", "id", "name"}), []string{}, r), 60 | ), 61 | } 62 | return res 63 | } 64 | 65 | func buildResource(s map[string]schema.Attribute, prefixes []string, r resource) j.Dict { 66 | res := j.Dict{} 67 | for k, v := range s { 68 | fullyQualified := append(prefixes, snakeToCamel(k)) 69 | modelPath := j.Id("model").Add(lo.Map(fullyQualified, func(s string, _ int) j.Code { return j.Dot(s) })...) 70 | 71 | switch t := v.(type) { 72 | // TODO: handle non-string fields 73 | case schema.StringAttribute: 74 | if t.CustomType == nil { 75 | res[j.Id(snakeToCamel(k))] = modelPath.Dot("ValueString").Call() 76 | continue 77 | } 78 | // If it's a string but has a custom type it's an enum. 79 | // This code works if the enum is in the same package as the resource, but we may need to pass 80 | // more information to the struct at schema generation time in the future. 81 | res[j.Id(snakeToCamel(k))] = j.Qual(r.PkgImportPath, snakeToCamel(k)).Call( 82 | j.Qual(r.PkgImportPath, snakeToCamel(k)+"_value").Index(modelPath.Dot("ValueString").Call()), 83 | ) 84 | case schema.ListNestedAttribute: 85 | continue 86 | default: 87 | panic(fmt.Sprintf("%v isn't supported as a resource value", t)) 88 | } 89 | } 90 | return res 91 | } 92 | -------------------------------------------------------------------------------- /generator/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | 20 | j "github.com/dave/jennifer/jen" 21 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 22 | "github.com/samber/lo" 23 | ) 24 | 25 | func genRead(r resource) *j.File { 26 | f := j.NewFile(r.lowerName) 27 | 28 | f.Func(). 29 | Parens(j.Id("r").Op("*").Add(r.structId)).Id("Read"). 30 | Params(j.Id("ctx").Qual("context", "Context"), j.Id("req").Qual(Resource, "ReadRequest"), j.Id("resp").Op("*").Qual(Resource, "ReadResponse")). 31 | Block( 32 | mergeCode( 33 | r.LoadStateIntoModel(), 34 | 35 | // Do the actual request 36 | j.Id("request").Op(":=").Op("&").Qual(r.PkgImportPath, "Get"+r.Name+"Request").Values(j.Dict{ 37 | j.Id("Fqn"): j.Id("model").Dot("Id").Dot("ValueString").Call(), 38 | }), 39 | j.List(j.Id(r.lowerName), j.Id("err")).Op(":=").Id("r").Dot("client").Dot("Get"+r.Name).Call( 40 | j.Id("ctx"), 41 | j.Id("request"), 42 | ), 43 | handleError("Error reading "+r.Name, "Get"+r.Name+"Request failed"), 44 | 45 | // Get meta object from response 46 | j.List(j.Id("meta"), j.Id("err")).Op(":=").Qual(Helpers, "FromFQN").Call( 47 | j.Qual(PkgAPI, r.Name+"Kind"), 48 | j.Id("model").Dot("Id").Dot("ValueString").Call(), 49 | ), 50 | handleError("Error reading"+r.Name, "FQN parsing failed"), 51 | 52 | populateModel(r, lo.OmitByKeys(r.Schema.Attributes, []string{"parent", "id", "name"})), 53 | 54 | saveState(), 55 | )..., 56 | ) 57 | 58 | return f 59 | } 60 | 61 | func populateModel(r resource, s map[string]schema.Attribute) []j.Code { 62 | // Handle common variables manually 63 | res := []j.Code{ 64 | j.Id("model").Dot("Id").Op("=").Qual(Types, "StringValue").Call(j.Id(r.lowerName).Dot("Fqn")), 65 | j.Id("model").Dot("Name").Op("=").Qual(Types, "StringValue").Call(j.Id("meta").Dot("Name")), 66 | j.Id("model").Dot("Parent").Op("=").Qual(Types, "StringValue").Call(j.Qual(Helpers, "ParentFQN").Call(j.Qual(PkgAPI, r.Name+"Kind"), j.Id("meta"))), 67 | } 68 | 69 | // Handle remining recursively 70 | for k, v := range s { 71 | res = append(res, j.Id("model").Dot(snakeToCamel(k)).Op("=").Add(modelField(k, v, r))) 72 | } 73 | return res 74 | } 75 | 76 | func modelField(name string, attr schema.Attribute, r resource) j.Code { 77 | switch t := attr.(type) { 78 | case schema.StringAttribute: 79 | if t.CustomType == nil { 80 | return j.Qual(Types, "StringValue").Call(j.Id(r.lowerName).Dot(snakeToCamel(name))) 81 | } 82 | // If it's a string but has a custom type it's an enum. 83 | // This code works if the enum is in the same package as the resource, but we may need to pass 84 | // more information to the struct at schema generation time in the future. 85 | return j.Qual(Types, "StringValue").Call(j.Qual(r.PkgImportPath, snakeToCamel(name)+"_name").Index(j.Int32().Call(j.Id(r.lowerName).Dot(snakeToCamel(name))))) 86 | case schema.ListNestedAttribute: 87 | return nil 88 | default: 89 | panic(fmt.Sprintf("%v isn't supported as a resource value", t)) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /generator/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "strings" 19 | 20 | j "github.com/dave/jennifer/jen" 21 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 22 | ) 23 | 24 | func NewResource(opts ...resourceOption) *resource { 25 | r := &resource{} 26 | for _, opt := range opts { 27 | opt(r) 28 | } 29 | return r 30 | } 31 | 32 | type resourceOption func(*resource) 33 | 34 | func WithName(name string) resourceOption { 35 | return func(r *resource) { 36 | r.Name = name 37 | r.lowerName = strings.ToLower(name) 38 | r.structId = j.Id(r.Name + "Resource") 39 | r.modelId = j.Id(r.Name + "Model") 40 | } 41 | } 42 | 43 | func WithPkgImportPath(path string) resourceOption { 44 | return func(r *resource) { 45 | r.PkgImportPath = path 46 | } 47 | } 48 | 49 | func WithClient(client string) resourceOption { 50 | return func(r *resource) { 51 | r.Client = client 52 | } 53 | } 54 | 55 | func WithSchema(s schema.Schema) resourceOption { 56 | return func(r *resource) { 57 | r.Schema = s 58 | } 59 | } 60 | 61 | type resource struct { 62 | Name string 63 | PkgImportPath string 64 | Client string 65 | Schema schema.Schema 66 | 67 | structId j.Code 68 | modelId j.Code 69 | lowerName string 70 | } 71 | 72 | func (r resource) LoadPlanIntoModel() j.Statement { 73 | return []j.Code{ 74 | j.Var().Id("model").Add(r.modelId), 75 | j.Id("resp").Dot("Diagnostics").Dot("Append").Call( 76 | j.Id("req").Dot("Plan").Dot("Get").Call(j.Id("ctx"), j.Op("&").Id("model")).Op("..."), 77 | ), 78 | j.If(j.Id("resp").Dot("Diagnostics").Dot("HasError").Call()).Block(j.Return()), 79 | } 80 | } 81 | 82 | func (r resource) LoadStateIntoModel() j.Statement { 83 | return []j.Code{ 84 | j.Var().Id("model").Add(r.modelId), 85 | j.Id("resp").Dot("Diagnostics").Dot("Append").Call( 86 | j.Id("req").Dot("State").Dot("Get").Call(j.Id("ctx"), j.Op("&").Id("model")).Op("..."), 87 | ), 88 | j.If(j.Id("resp").Dot("Diagnostics").Dot("HasError").Call()).Block(j.Return()), 89 | } 90 | } 91 | 92 | func handleError(topLevelMessage, secondLevelMessage string) j.Code { 93 | return j.If(j.Id("err").Op("!=").Nil()).Block( 94 | j.Id("resp").Dot("Diagnostics").Dot("AddError").Call( 95 | j.Lit(topLevelMessage), 96 | j.Lit(secondLevelMessage+": ").Op("+").Id("err").Dot("Error").Call(), 97 | ), 98 | j.Return(), 99 | ) 100 | } 101 | 102 | func saveState() j.Code { 103 | return j.Id("resp").Dot("Diagnostics").Dot("Append").Call(j.Id("resp").Dot("State").Dot("Set").Call(j.Id("ctx"), j.Op("&").Id("model")).Op("...")) 104 | } 105 | 106 | // Gross, but go doesn't support type guards and I don't want to do several levels of append inline 107 | func mergeCode(code ...interface{}) j.Statement { 108 | res := []j.Code{} 109 | for _, c := range code { 110 | switch v := c.(type) { 111 | case j.Code: 112 | res = append(res, v) 113 | case []j.Code: 114 | res = append(res, v...) 115 | case j.Statement: 116 | res = append(res, v...) 117 | } 118 | } 119 | return res 120 | } 121 | -------------------------------------------------------------------------------- /internal/provider/resources/tenant/tenant_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package tenant_test 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "testing" 21 | "text/template" 22 | "time" 23 | 24 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 25 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/test" 26 | ) 27 | 28 | func TestAccTenantResource(t *testing.T) { 29 | name := fmt.Sprintf("tf_tenant_%v", time.Now().Unix()) 30 | parent := fmt.Sprintf("organizations/%v", test.AccOrganizationName) 31 | id := fmt.Sprintf("%v/tenants/%v", parent, name) 32 | original := tenantConfig{ 33 | Name: name, 34 | Parent: parent, 35 | Description: "I am a test Tenant created during Terraform Provider acceptance testing", 36 | DisplayName: "Terraform Provider Test Original", 37 | SecurityDomain: fmt.Sprintf("organizations/%s/securitydomains/yolo", test.AccOrganizationName), 38 | } 39 | updated := original 40 | updated.DisplayName = "Terraform Provider Test Updated" 41 | 42 | resource.Test(t, resource.TestCase{ 43 | PreCheck: func() { test.AccPreCheck(t) }, 44 | ProtoV6ProviderFactories: test.AccProtoV6ProviderFactories, 45 | Steps: []resource.TestStep{ 46 | // Create and Read testing 47 | { 48 | Config: test.BuildConfig(t, original.Block(t)), 49 | Check: resource.ComposeAggregateTestCheckFunc( 50 | resource.TestCheckResourceAttr("tsb_tenant."+name, "id", id), 51 | resource.TestCheckResourceAttr("tsb_tenant."+name, "name", original.Name), 52 | resource.TestCheckResourceAttr("tsb_tenant."+name, "parent", original.Parent), 53 | resource.TestCheckResourceAttr("tsb_tenant."+name, "display_name", original.DisplayName), 54 | resource.TestCheckResourceAttr("tsb_tenant."+name, "description", original.Description), 55 | resource.TestCheckResourceAttr("tsb_tenant."+name, "security_domain", original.SecurityDomain), 56 | ), 57 | }, 58 | // ImportState testing 59 | { 60 | ResourceName: "tsb_tenant." + name, 61 | ImportState: true, 62 | ImportStateVerify: true, 63 | }, 64 | // Update and Read testing 65 | { 66 | Config: test.BuildConfig(t, updated.Block(t)), 67 | Check: resource.ComposeAggregateTestCheckFunc( 68 | resource.TestCheckResourceAttr("tsb_tenant."+name, "display_name", updated.DisplayName)), 69 | }, 70 | // Delete testing automatically occurs in TestCase 71 | }, 72 | }) 73 | } 74 | 75 | type tenantConfig struct { 76 | Name string 77 | Parent string 78 | DisplayName string 79 | Description string 80 | SecurityDomain string 81 | } 82 | 83 | func (c tenantConfig) Block(t *testing.T) string { 84 | tmpl, _ := template.New("tenant").Parse(tenantTmpl) 85 | w := bytes.NewBuffer(nil) 86 | if err := tmpl.Execute(w, c); err != nil { 87 | t.Fatal("unable to build tenant block: %w", err) 88 | } 89 | return w.String() 90 | } 91 | 92 | const tenantTmpl = ` 93 | resource "tsb_tenant" "{{.Name}}" { 94 | name = "{{.Name}}" 95 | parent = "{{.Parent}}" 96 | display_name = "{{.DisplayName}}" 97 | description = "{{.Description}}" 98 | security_domain = "{{.SecurityDomain}}" 99 | } 100 | ` 101 | -------------------------------------------------------------------------------- /internal/provider/resources/user/user_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package user_test 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "testing" 21 | "text/template" 22 | "time" 23 | 24 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 25 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/test" 26 | ) 27 | 28 | func TestAccUserResource(t *testing.T) { 29 | name := fmt.Sprintf("tf_user_%v", time.Now().Unix()) 30 | parent := fmt.Sprintf("organizations/%v", test.AccOrganizationName) 31 | id := fmt.Sprintf("%v/users/%v", parent, name) 32 | original := userConfig{ 33 | Name: name, 34 | Parent: parent, 35 | DisplayName: "Terry Form", 36 | LoginName: "terryfrom", 37 | FirstName: "Terry", 38 | LastName: "Form", 39 | Email: "terry.form@tetrate.io", 40 | } 41 | updated := original 42 | updated.DisplayName = "Terraform Provider Test Updated" 43 | 44 | resource.Test(t, resource.TestCase{ 45 | PreCheck: func() { test.AccPreCheck(t) }, 46 | ProtoV6ProviderFactories: test.AccProtoV6ProviderFactories, 47 | Steps: []resource.TestStep{ 48 | // Create and Read testing 49 | { 50 | Config: test.BuildConfig(t, original.Block(t)), 51 | Check: resource.ComposeAggregateTestCheckFunc( 52 | resource.TestCheckResourceAttr("tsb_user."+name, "id", id), 53 | resource.TestCheckResourceAttr("tsb_user."+name, "name", original.Name), 54 | resource.TestCheckResourceAttr("tsb_user."+name, "parent", original.Parent), 55 | resource.TestCheckResourceAttr("tsb_user."+name, "display_name", original.DisplayName), 56 | resource.TestCheckResourceAttr("tsb_user."+name, "login_name", original.LoginName), 57 | resource.TestCheckResourceAttr("tsb_user."+name, "first_name", original.FirstName), 58 | resource.TestCheckResourceAttr("tsb_user."+name, "last_name", original.LastName), 59 | resource.TestCheckResourceAttr("tsb_user."+name, "email", original.Email), 60 | ), 61 | }, 62 | // ImportState testing 63 | { 64 | ResourceName: "tsb_user." + name, 65 | ImportState: true, 66 | ImportStateVerify: true, 67 | }, 68 | // Update and Read testing 69 | { 70 | Config: test.BuildConfig(t, updated.Block(t)), 71 | Check: resource.ComposeAggregateTestCheckFunc( 72 | resource.TestCheckResourceAttr("tsb_user."+name, "display_name", updated.DisplayName)), 73 | }, 74 | // Delete testing automatically occurs in TestCase 75 | }, 76 | }) 77 | } 78 | 79 | type userConfig struct { 80 | Name string 81 | Parent string 82 | DisplayName string 83 | LoginName string 84 | FirstName string 85 | LastName string 86 | Email string 87 | } 88 | 89 | func (c userConfig) Block(t *testing.T) string { 90 | tmpl, _ := template.New("user").Parse(userTmpl) 91 | w := bytes.NewBuffer(nil) 92 | if err := tmpl.Execute(w, c); err != nil { 93 | t.Fatal("unable to build user block: %w", err) 94 | } 95 | return w.String() 96 | } 97 | 98 | const userTmpl = ` 99 | resource "tsb_user" "{{.Name}}" { 100 | name = "{{.Name}}" 101 | parent = "{{.Parent}}" 102 | display_name = "{{.DisplayName}}" 103 | login_name = "{{.LoginName}}" 104 | first_name = "{{.FirstName}}" 105 | last_name = "{{.LastName}}" 106 | email = "{{.Email}}" 107 | } 108 | ` 109 | -------------------------------------------------------------------------------- /internal/provider/resources/serviceaccount/serviceaccount_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serviceaccount_test 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "testing" 21 | "text/template" 22 | "time" 23 | 24 | "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" 25 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/test" 26 | ) 27 | 28 | func TestAccServiceAccountResource(t *testing.T) { 29 | name := fmt.Sprintf("tf_service_account_%v", time.Now().Unix()) 30 | parent := fmt.Sprintf("organizations/%v", test.AccOrganizationName) 31 | id := fmt.Sprintf("%v/serviceaccounts/%v", parent, name) 32 | original := serviceAccountConfig{ 33 | Name: name, 34 | Parent: parent, 35 | Description: "I am a test ServiceAccount created during Terraform Provider acceptance testing", 36 | DisplayName: "Terraform Provider Test Original", 37 | KeyEncoding: "JWK", 38 | } 39 | updated := original 40 | updated.DisplayName = "Terraform Provider Test Updated" 41 | 42 | resource.Test(t, resource.TestCase{ 43 | PreCheck: func() { test.AccPreCheck(t) }, 44 | ProtoV6ProviderFactories: test.AccProtoV6ProviderFactories, 45 | Steps: []resource.TestStep{ 46 | // Create and Read testing 47 | { 48 | Config: test.BuildConfig(t, original.Block(t)), 49 | Check: resource.ComposeAggregateTestCheckFunc( 50 | resource.TestCheckResourceAttr("tsb_service_account."+name, "id", id), 51 | resource.TestCheckResourceAttr("tsb_service_account."+name, "name", original.Name), 52 | resource.TestCheckResourceAttr("tsb_service_account."+name, "parent", original.Parent), 53 | resource.TestCheckResourceAttr("tsb_service_account."+name, "display_name", original.DisplayName), 54 | resource.TestCheckResourceAttr("tsb_service_account."+name, "description", original.Description), 55 | resource.TestCheckResourceAttr("tsb_service_account."+name, "key_encoding", original.KeyEncoding), 56 | resource.TestCheckResourceAttrSet("tsb_service_account."+name, "keys.0.id"), 57 | resource.TestCheckResourceAttrSet("tsb_service_account."+name, "keys.0.private_key"), 58 | resource.TestCheckResourceAttrSet("tsb_service_account."+name, "keys.0.public_key"), 59 | resource.TestCheckResourceAttr("tsb_service_account."+name, "keys.0.encoding", original.KeyEncoding), 60 | resource.TestCheckResourceAttrSet("tsb_service_account."+name, "keys.0.default_token"), 61 | ), 62 | }, 63 | // ImportState testing 64 | { 65 | ResourceName: "tsb_service_account." + name, 66 | ImportState: true, 67 | ImportStateVerify: true, 68 | ImportStateVerifyIgnore: []string{"keys.0.default_token", "keys.0.encoding", "keys.0.id", "keys.0.private_key", "keys.0.public_key", "keys.#", "keys.0.%", "key_encoding"}, 69 | }, 70 | // Update and Read testing 71 | { 72 | Config: test.BuildConfig(t, updated.Block(t)), 73 | Check: resource.ComposeAggregateTestCheckFunc( 74 | resource.TestCheckResourceAttr("tsb_service_account."+name, "display_name", updated.DisplayName), 75 | resource.TestCheckResourceAttrSet("tsb_service_account."+name, "keys.0.private_key"), 76 | ), 77 | }, 78 | // Delete testing automatically occurs in TestCase 79 | }, 80 | }) 81 | } 82 | 83 | type serviceAccountConfig struct { 84 | Name string 85 | Parent string 86 | Description string 87 | DisplayName string 88 | KeyEncoding string 89 | } 90 | 91 | func (c serviceAccountConfig) Block(t *testing.T) string { 92 | tmpl, _ := template.New("serviceAccount").Parse(serviceAccountTmpl) 93 | w := bytes.NewBuffer(nil) 94 | if err := tmpl.Execute(w, c); err != nil { 95 | t.Fatal("unable to build serviceAccount block: %w", err) 96 | } 97 | return w.String() 98 | } 99 | 100 | const serviceAccountTmpl = ` 101 | resource "tsb_service_account" "{{.Name}}" { 102 | name = "{{.Name}}" 103 | parent = "{{.Parent}}" 104 | display_name = "{{.DisplayName}}" 105 | description = "{{.Description}}" 106 | key_encoding = "{{.KeyEncoding}}" 107 | } 108 | ` 109 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tetratelabs/terraform-provider-tsb 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/dave/jennifer v1.6.0 7 | github.com/hashicorp/terraform-plugin-docs v0.13.0 8 | github.com/hashicorp/terraform-plugin-framework v1.2.0 9 | github.com/hashicorp/terraform-plugin-framework-validators v0.5.0 10 | github.com/hashicorp/terraform-plugin-go v0.14.3 11 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 12 | github.com/samber/lo v1.38.1 13 | github.com/tetrateio/api v0.0.0-20230405190128-8916e4491331 14 | github.com/tetrateio/tetrate v0.0.2-0.20230201205234-5d44a721cb40 15 | google.golang.org/grpc v1.51.0 16 | ) 17 | 18 | require ( 19 | github.com/Masterminds/goutils v1.1.1 // indirect 20 | github.com/Masterminds/semver/v3 v3.1.1 // indirect 21 | github.com/Masterminds/sprig/v3 v3.2.2 // indirect 22 | github.com/agext/levenshtein v1.2.2 // indirect 23 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 24 | github.com/armon/go-radix v1.0.0 // indirect 25 | github.com/bgentry/speakeasy v0.1.0 // indirect 26 | github.com/envoyproxy/protoc-gen-validate v0.6.8 // indirect 27 | github.com/fatih/color v1.13.0 // indirect 28 | github.com/go-logr/logr v1.2.3 // indirect 29 | github.com/gogo/protobuf v1.3.2 // indirect 30 | github.com/golang/protobuf v1.5.2 // indirect 31 | github.com/google/go-cmp v0.5.9 // indirect 32 | github.com/google/gofuzz v1.2.0 // indirect 33 | github.com/google/uuid v1.3.0 // indirect 34 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect 35 | github.com/hashicorp/errwrap v1.1.0 // indirect 36 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect 37 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 38 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect 39 | github.com/hashicorp/go-hclog v1.4.0 // indirect 40 | github.com/hashicorp/go-multierror v1.1.1 // indirect 41 | github.com/hashicorp/go-plugin v1.4.8 // indirect 42 | github.com/hashicorp/go-uuid v1.0.3 // indirect 43 | github.com/hashicorp/go-version v1.6.0 // indirect 44 | github.com/hashicorp/hc-install v0.5.0 // indirect 45 | github.com/hashicorp/hcl/v2 v2.16.2 // indirect 46 | github.com/hashicorp/logutils v1.0.0 // indirect 47 | github.com/hashicorp/terraform-exec v0.18.1 // indirect 48 | github.com/hashicorp/terraform-json v0.16.0 // indirect 49 | github.com/hashicorp/terraform-plugin-log v0.8.0 // indirect 50 | github.com/hashicorp/terraform-registry-address v0.1.0 // indirect 51 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect 52 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect 53 | github.com/huandu/xstrings v1.3.2 // indirect 54 | github.com/imdario/mergo v0.3.13 // indirect 55 | github.com/jinzhu/inflection v1.0.0 // indirect 56 | github.com/json-iterator/go v1.1.12 // indirect 57 | github.com/mattn/go-colorable v0.1.12 // indirect 58 | github.com/mattn/go-isatty v0.0.14 // indirect 59 | github.com/mitchellh/cli v1.1.5 // indirect 60 | github.com/mitchellh/copystructure v1.2.0 // indirect 61 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 62 | github.com/mitchellh/go-wordwrap v1.0.0 // indirect 63 | github.com/mitchellh/mapstructure v1.5.0 // indirect 64 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 65 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 66 | github.com/modern-go/reflect2 v1.0.2 // indirect 67 | github.com/oklog/run v1.1.0 // indirect 68 | github.com/posener/complete v1.2.3 // indirect 69 | github.com/russross/blackfriday v1.6.0 // indirect 70 | github.com/shopspring/decimal v1.3.1 // indirect 71 | github.com/spf13/cast v1.5.0 // indirect 72 | github.com/tetratelabs/multierror v1.1.1 // indirect 73 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect 74 | github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect 75 | github.com/vmihailenco/tagparser v0.1.2 // indirect 76 | github.com/zclconf/go-cty v1.13.1 // indirect 77 | golang.org/x/crypto v0.7.0 // indirect 78 | golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75 // indirect 79 | golang.org/x/mod v0.8.0 // indirect 80 | golang.org/x/net v0.8.0 // indirect 81 | golang.org/x/sys v0.6.0 // indirect 82 | golang.org/x/text v0.8.0 83 | google.golang.org/appengine v1.6.7 // indirect 84 | google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de // indirect 85 | google.golang.org/protobuf v1.28.1 // indirect 86 | gopkg.in/inf.v0 v0.9.1 // indirect 87 | gopkg.in/yaml.v2 v2.4.0 // indirect 88 | istio.io/api v0.0.0-20221005164339-97dc20dc0ff3 // indirect 89 | k8s.io/apimachinery v0.25.2 // indirect 90 | k8s.io/klog/v2 v2.70.1 // indirect 91 | k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect 92 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect 93 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect 94 | sigs.k8s.io/yaml v1.3.0 // indirect 95 | ) 96 | -------------------------------------------------------------------------------- /internal/provider/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package provider 16 | 17 | import ( 18 | "context" 19 | "crypto/tls" 20 | 21 | "github.com/hashicorp/terraform-plugin-framework/datasource" 22 | "github.com/hashicorp/terraform-plugin-framework/provider" 23 | "github.com/hashicorp/terraform-plugin-framework/provider/schema" 24 | "github.com/hashicorp/terraform-plugin-framework/resource" 25 | "github.com/hashicorp/terraform-plugin-framework/schema/validator" 26 | "github.com/hashicorp/terraform-plugin-framework/types" 27 | "google.golang.org/grpc" 28 | "google.golang.org/grpc/credentials" 29 | 30 | "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 31 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/datasources/organization" 32 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/resources/serviceaccount" 33 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/resources/tenant" 34 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/resources/user" 35 | "github.com/tetratelabs/terraform-provider-tsb/internal/provider/validators" 36 | ) 37 | 38 | // Ensure TsbProvider satisfies various provider interfaces. 39 | var _ provider.Provider = &TsbProvider{} 40 | 41 | // TsbProvider defines the provider implementation. 42 | type TsbProvider struct { 43 | // version is set to the provider version on release, "dev" when the 44 | // provider is built and ran locally, and "test" when running acceptance 45 | // testing. 46 | version string 47 | } 48 | 49 | // Schema implements provider.Provider 50 | func (*TsbProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { 51 | resp.Schema = schema.Schema{ 52 | Attributes: map[string]schema.Attribute{ 53 | "address": schema.StringAttribute{ 54 | MarkdownDescription: "The address that the management plane can be reached at. Must include port.", 55 | Required: true, 56 | Validators: []validator.String{validators.AddressIncludesPort()}, 57 | }, 58 | "basic_auth": schema.SingleNestedAttribute{ 59 | Description: "The basic auth credentials to communicate with a TSB management plane.", 60 | Required: true, 61 | Attributes: map[string]schema.Attribute{ 62 | "username": schema.StringAttribute{ 63 | Required: true, 64 | }, 65 | "password": schema.StringAttribute{ 66 | Required: true, 67 | Sensitive: true, 68 | }, 69 | }, 70 | }, 71 | }, 72 | } 73 | } 74 | 75 | // TsbProviderModel describes the provider data model. 76 | type TsbProviderModel struct { 77 | Address types.String `tfsdk:"address"` 78 | BasicAuth helpers.BasicAuth `tfsdk:"basic_auth"` 79 | } 80 | 81 | func (p *TsbProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { 82 | resp.TypeName = "tsb" 83 | resp.Version = p.version 84 | } 85 | 86 | func (p *TsbProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { 87 | var data TsbProviderModel 88 | resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) 89 | if resp.Diagnostics.HasError() { 90 | return 91 | } 92 | 93 | // TODO: support passing in custom ca bundles 94 | tlsConfig := &tls.Config{} 95 | opts := []grpc.DialOption{ 96 | grpc.WithBlock(), 97 | grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), 98 | grpc.WithPerRPCCredentials(data.BasicAuth), 99 | } 100 | 101 | cc, err := grpc.DialContext(ctx, data.Address.ValueString(), opts...) 102 | if err != nil { 103 | panic(err) 104 | } 105 | 106 | clients := helpers.NewClients(cc) 107 | resp.DataSourceData = clients 108 | resp.ResourceData = clients 109 | } 110 | 111 | func (p *TsbProvider) Resources(ctx context.Context) []func() resource.Resource { 112 | return []func() resource.Resource{ 113 | tenant.NewResource, 114 | serviceaccount.NewResource, 115 | user.NewResource, 116 | } 117 | } 118 | 119 | func (p *TsbProvider) DataSources(ctx context.Context) []func() datasource.DataSource { 120 | return []func() datasource.DataSource{ 121 | organization.NewDataSource, 122 | } 123 | } 124 | 125 | func New(version string) func() provider.Provider { 126 | return func() provider.Provider { 127 | return &TsbProvider{ 128 | version: version, 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /internal/provider/resources/serviceaccount/serviceaccount.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Tetrate 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package serviceaccount 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/hashicorp/terraform-plugin-framework/resource" 21 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 22 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" 23 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 24 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" 25 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 26 | "github.com/hashicorp/terraform-plugin-framework/types" 27 | tsbv2 "github.com/tetrateio/api/tsb/v2" 28 | 29 | "github.com/tetratelabs/terraform-provider-tsb/internal/helpers" 30 | ) 31 | 32 | // Ensure provider defined types fully satisfy framework interfaces 33 | var _ resource.Resource = &ServiceAccountResource{} 34 | var _ resource.ResourceWithImportState = &ServiceAccountResource{} 35 | 36 | func NewResource() resource.Resource { 37 | return &ServiceAccountResource{} 38 | } 39 | 40 | // ServiceAccountResource defines the resource implementation. 41 | type ServiceAccountResource struct { 42 | client tsbv2.TeamsClient 43 | } 44 | 45 | type serviceAccountResourceModel struct { 46 | Id types.String `tfsdk:"id"` 47 | Name types.String `tfsdk:"name"` 48 | Parent types.String `tfsdk:"parent"` 49 | Description types.String `tfsdk:"description"` 50 | DisplayName types.String `tfsdk:"display_name"` 51 | KeyEncoding types.String `tfsdk:"key_encoding"` 52 | // types.List is used because keys can be null during read, try to avoid for normal resources. 53 | Keys types.List `tfsdk:"keys"` 54 | } 55 | 56 | func (r *ServiceAccountResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 57 | resp.TypeName = req.ProviderTypeName + "_service_account" 58 | } 59 | 60 | // Schema implements resource.Resource 61 | func (*ServiceAccountResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { 62 | resp.Schema = schema.Schema{ 63 | Attributes: map[string]schema.Attribute{ 64 | "id": schema.StringAttribute{ 65 | Computed: true, 66 | Description: "Fully-qualified name of the resource.", 67 | PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, 68 | }, 69 | "name": schema.StringAttribute{ 70 | Description: "The short name for the resource to be created.", 71 | Required: true, 72 | PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, 73 | }, 74 | "parent": schema.StringAttribute{ 75 | Description: "The parent ID of the Service Account.", 76 | Required: true, 77 | PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, 78 | }, 79 | "display_name": schema.StringAttribute{ 80 | Description: "User friendly name for the resource.", 81 | Optional: true, 82 | }, 83 | "description": schema.StringAttribute{ 84 | Description: "A description of the resource.", 85 | Optional: true, 86 | }, 87 | "key_encoding": schema.StringAttribute{ 88 | Description: "The format in which the generated key pairs will be returned. If not set keys are returned in PEM format.", 89 | Optional: true, 90 | Computed: true, 91 | PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, 92 | Default: stringdefault.StaticString(tsbv2.ServiceAccount_KeyPair_Encoding_name[0]), 93 | }, 94 | "keys": schema.ListNestedAttribute{ 95 | Computed: true, 96 | Description: "Keys associated with the service account. A default key-pair is automatically created when the Service Account is created. Note that TSB does not store the private keys, so it is up to the client to store the returned private keys securely, as they are only returned once after creation. Additional keys can be added (and deleted) by using the corresponding key management APIs. +protoc-gen-terraform:computed", 97 | PlanModifiers: []planmodifier.List{listplanmodifier.UseStateForUnknown()}, 98 | NestedObject: schema.NestedAttributeObject{Attributes: map[string]schema.Attribute{ 99 | "default_token": schema.StringAttribute{ 100 | Description: "A default access token that can be used to authenticate to TSB on behalf of the service account. TSB does not store this token and it is only returned when a service account key is created, similar to the private key. It is up to the client to store the token for future use or to use the TSB CLI to generate new tokens as explained in: https://docs.tetrate.io/service-bridge/latest/en-us/howto/service-accounts", 101 | Computed: true, 102 | Sensitive: true, 103 | }, 104 | "encoding": schema.StringAttribute{ 105 | Description: "Format in which the public and private keys are encoded. By default keys are returned in PEM format.", 106 | Computed: true, 107 | }, 108 | "id": schema.StringAttribute{ 109 | Description: "Unique identifier for this key-pair. This should be used as the `kid` (key id) when generating JWT tokens that are signed with this key-pair.", 110 | Computed: true, 111 | }, 112 | "private_key": schema.StringAttribute{ 113 | Description: "The encoded private key associated with the service account. TSB does not store the private key and it is up to the client to store it safely. The encoding format is determined by the `encoding` field.", 114 | Computed: true, 115 | Sensitive: true, 116 | }, 117 | "public_key": schema.StringAttribute{ 118 | Description: "The encoded public key associated with the service account. The encoding format is determined by the `encoding` field.", 119 | Computed: true, 120 | }, 121 | }}, 122 | }, 123 | }, 124 | } 125 | } 126 | 127 | func (r *ServiceAccountResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { 128 | clients := helpers.BuildClientsResource(req, resp) 129 | if resp.Diagnostics.HasError() || clients == nil { 130 | return 131 | } 132 | r.client = clients.Team 133 | } 134 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 Tetrate 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 41 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 42 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 43 | github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= 44 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 45 | github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= 46 | github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= 47 | github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= 48 | github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= 49 | github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= 50 | github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= 51 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= 52 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= 53 | github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= 54 | github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= 55 | github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= 56 | github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 57 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 58 | github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= 59 | github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= 60 | github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= 61 | github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= 62 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 63 | github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= 64 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 65 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 66 | github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= 67 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 68 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 69 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 70 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 71 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 72 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 73 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 74 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 75 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 76 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 77 | github.com/dave/jennifer v1.6.0 h1:MQ/6emI2xM7wt0tJzJzyUik2Q3Tcn2eE0vtYgh4GPVI= 78 | github.com/dave/jennifer v1.6.0/go.mod h1:AxTG893FiZKqxy3FP1kL80VMshSMuz2G+EgvszgGRnk= 79 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 80 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 81 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 82 | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= 83 | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 84 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 85 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 86 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 87 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 88 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 89 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 90 | github.com/envoyproxy/protoc-gen-validate v0.6.8 h1:B2cR/FAaiMtYDHv5BQpaqtkjGuWQIgr2KQZtHQ7f6i8= 91 | github.com/envoyproxy/protoc-gen-validate v0.6.8/go.mod h1:0ZMblUx0cxNoWRswEEXoj9kHBmqX8pxGweMiyIAfR6A= 92 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 93 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 94 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 95 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 96 | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= 97 | github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 98 | github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= 99 | github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= 100 | github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 101 | github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= 102 | github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 103 | github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= 104 | github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= 105 | github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= 106 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 107 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 108 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 109 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 110 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 111 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 112 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 113 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= 114 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 115 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 116 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 117 | github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= 118 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 119 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 120 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 121 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 122 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 123 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 124 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 125 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 126 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 127 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 128 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 129 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 130 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 131 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 132 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 133 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 134 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 135 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 136 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 137 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 138 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 139 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 140 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 141 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 142 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 143 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 144 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 145 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 146 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 147 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 148 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 149 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 150 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 151 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 152 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 153 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 154 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 155 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 156 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 157 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 158 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 159 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 160 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 161 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 162 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 163 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 164 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 165 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 166 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 167 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 168 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 169 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 170 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 171 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 172 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 173 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 174 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 175 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 176 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 177 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 178 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 179 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 180 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 181 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 182 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 183 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 184 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= 185 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= 186 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 187 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 188 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 189 | github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= 190 | github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= 191 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 192 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 193 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 194 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 195 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= 196 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= 197 | github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= 198 | github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 199 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 200 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 201 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 202 | github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= 203 | github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= 204 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 205 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= 206 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 207 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 208 | github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= 209 | github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 210 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 211 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 212 | github.com/hashicorp/hc-install v0.5.0 h1:D9bl4KayIYKEeJ4vUDe9L5huqxZXczKaykSRcmQ0xY0= 213 | github.com/hashicorp/hc-install v0.5.0/go.mod h1:JyzMfbzfSBSjoDCRPna1vi/24BEDxFaCPfdHtM5SCdo= 214 | github.com/hashicorp/hcl/v2 v2.16.2 h1:mpkHZh/Tv+xet3sy3F9Ld4FyI2tUpWe9x3XtPx9f1a0= 215 | github.com/hashicorp/hcl/v2 v2.16.2/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= 216 | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= 217 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 218 | github.com/hashicorp/terraform-exec v0.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= 219 | github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= 220 | github.com/hashicorp/terraform-json v0.16.0 h1:UKkeWRWb23do5LNAFlh/K3N0ymn1qTOO8c+85Albo3s= 221 | github.com/hashicorp/terraform-json v0.16.0/go.mod h1:v0Ufk9jJnk6tcIZvScHvetlKfiNTC+WS21mnXIlc0B0= 222 | github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smPzZrt1Wlm9koLeKazY= 223 | github.com/hashicorp/terraform-plugin-docs v0.13.0/go.mod h1:W0oCmHAjIlTHBbvtppWHe8fLfZ2BznQbuv8+UD8OucQ= 224 | github.com/hashicorp/terraform-plugin-framework v1.2.0 h1:MZjFFfULnFq8fh04FqrKPcJ/nGpHOvX4buIygT3MSNY= 225 | github.com/hashicorp/terraform-plugin-framework v1.2.0/go.mod h1:nToI62JylqXDq84weLJ/U3umUsBhZAaTmU0HXIVUOcw= 226 | github.com/hashicorp/terraform-plugin-framework-validators v0.5.0 h1:eD79idhnJOBajkUMEbm0c8dOyOb/F49STbUEVojT6F4= 227 | github.com/hashicorp/terraform-plugin-framework-validators v0.5.0/go.mod h1:NfGgclDM3FZqvNVppPKE2aHI1JAyT002ypPRya7ch3I= 228 | github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= 229 | github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= 230 | github.com/hashicorp/terraform-plugin-log v0.8.0 h1:pX2VQ/TGKu+UU1rCay0OlzosNKe4Nz1pepLXj95oyy0= 231 | github.com/hashicorp/terraform-plugin-log v0.8.0/go.mod h1:1myFrhVsBLeylQzYYEV17VVjtG8oYPRFdaZs7xdW2xs= 232 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 h1:G9WAfb8LHeCxu7Ae8nc1agZlQOSCUWsb610iAogBhCs= 233 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1/go.mod h1:xcOSYlRVdPLmDUoqPhO9fiO/YCN/l6MGYeTzGt5jgkQ= 234 | github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= 235 | github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= 236 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= 237 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= 238 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= 239 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 240 | github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 241 | github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= 242 | github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 243 | github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 244 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 245 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 246 | github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 247 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 248 | github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= 249 | github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= 250 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 251 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 252 | github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= 253 | github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= 254 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 255 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 256 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 257 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 258 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 259 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 260 | github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= 261 | github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 262 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 263 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 264 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 265 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 266 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 267 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 268 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 269 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 270 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 271 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 272 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 273 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 274 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 275 | github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= 276 | github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= 277 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 278 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 279 | github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 280 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 281 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 282 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 283 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 284 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 285 | github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= 286 | github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= 287 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 288 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 289 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 290 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 291 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 292 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 293 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 294 | github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= 295 | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 296 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 297 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 298 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 299 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 300 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 301 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 302 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 303 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 304 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 305 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 306 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 307 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 308 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 309 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 310 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 311 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 312 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 313 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 314 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 315 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 316 | github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= 317 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= 318 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 319 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 320 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 321 | github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= 322 | github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= 323 | github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= 324 | github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= 325 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 326 | github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= 327 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 328 | github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= 329 | github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 330 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 331 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 332 | github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= 333 | github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= 334 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 335 | github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= 336 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= 337 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 338 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 339 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 340 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 341 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 342 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 343 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 344 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 345 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 346 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 347 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 348 | github.com/tetrateio/api v0.0.0-20230404164318-a1ab6329ff86 h1:krXw6gS/dZDFC7Znr7RRTPlug4vq0rOoEoBroRBures= 349 | github.com/tetrateio/api v0.0.0-20230404164318-a1ab6329ff86/go.mod h1:aaixS1aeh6hB/GaXYTBWoJAwaLKQtNgkHKMiqAllPN4= 350 | github.com/tetrateio/api v0.0.0-20230405190128-8916e4491331 h1:JiIlxiDX5xa5TgEhMj5rxpr6nVwWNmHRTRp4QlIj9Tk= 351 | github.com/tetrateio/api v0.0.0-20230405190128-8916e4491331/go.mod h1:aaixS1aeh6hB/GaXYTBWoJAwaLKQtNgkHKMiqAllPN4= 352 | github.com/tetrateio/tetrate v0.0.2-0.20230201205234-5d44a721cb40 h1:mK8DI/BiHXE2NaHpKvahBL1a3ey10B7UydRbfpwpH0o= 353 | github.com/tetrateio/tetrate v0.0.2-0.20230201205234-5d44a721cb40/go.mod h1:58JZLAOeIpCAAueeaCFpMV4dL1VjWdsO07m5wcaYHwc= 354 | github.com/tetratelabs/multierror v1.1.1 h1:84JfJvfmnYC5y4877Ag3c4PkCUmQwq6sGI6iRfBATKI= 355 | github.com/tetratelabs/multierror v1.1.1/go.mod h1:hAyQ/XifPOPdb0x3GsowbjsjCPgT9Fsxjety9TxmuGY= 356 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 357 | github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= 358 | github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 359 | github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= 360 | github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= 361 | github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 362 | github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= 363 | github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 364 | github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= 365 | github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= 366 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 367 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 368 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 369 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 370 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 371 | github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= 372 | github.com/zclconf/go-cty v1.13.1 h1:0a6bRwuiSHtAmqCqNOE+c2oHgepv0ctoxU4FUe43kwc= 373 | github.com/zclconf/go-cty v1.13.1/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= 374 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 375 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 376 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 377 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 378 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 379 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 380 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 381 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 382 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 383 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 384 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 385 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 386 | golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 387 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 388 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 389 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 390 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 391 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 392 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 393 | golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= 394 | golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= 395 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 396 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 397 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 398 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 399 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 400 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 401 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 402 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 403 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 404 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 405 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 406 | golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75 h1:x03zeu7B2B11ySp+daztnwM5oBJ/8wGUSqrwcw9L0RA= 407 | golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 408 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 409 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 410 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 411 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 412 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 413 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 414 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 415 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 416 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 417 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 418 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 419 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 420 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 421 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 422 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 423 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 424 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 425 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 426 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 427 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 428 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 429 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 430 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 431 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 432 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 433 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 434 | golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= 435 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 436 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 437 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 438 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 439 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 440 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 441 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 442 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 443 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 444 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 445 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 446 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 447 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 448 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 449 | golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 450 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 451 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 452 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 453 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 454 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 455 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 456 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 457 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 458 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 459 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 460 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 461 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 462 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 463 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 464 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 465 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 466 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 467 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 468 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 469 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 470 | golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= 471 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 472 | golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 473 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= 474 | golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= 475 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 476 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 477 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 478 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 479 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 480 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 481 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 482 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 483 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 484 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 485 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 486 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 487 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 488 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 489 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 490 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 491 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 492 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 493 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 494 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 495 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 496 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 497 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 498 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 499 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 500 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 501 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 502 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 503 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 504 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 505 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 506 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 507 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 508 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 509 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 510 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 511 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 512 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 513 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 514 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 515 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 516 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 517 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 518 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 519 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 520 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 521 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 522 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 523 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 524 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 525 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 526 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 527 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 528 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 529 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 530 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 531 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 532 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 533 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 534 | golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 535 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 536 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 537 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 538 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 539 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 540 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 541 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 542 | golang.org/x/sys v0.0.0-20220908150016-7ac13a9a928d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 543 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 544 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 545 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 546 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 547 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 548 | golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= 549 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 550 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 551 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 552 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 553 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 554 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 555 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 556 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 557 | golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= 558 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 559 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 560 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 561 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 562 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 563 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 564 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 565 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 566 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 567 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 568 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 569 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 570 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 571 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 572 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 573 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 574 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 575 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 576 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 577 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 578 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 579 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 580 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 581 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 582 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 583 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 584 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 585 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 586 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 587 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 588 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 589 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 590 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 591 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 592 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 593 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 594 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 595 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 596 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 597 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 598 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 599 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 600 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 601 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 602 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 603 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 604 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 605 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 606 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 607 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 608 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 609 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 610 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 611 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 612 | golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= 613 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 614 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 615 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 616 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 617 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 618 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 619 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 620 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 621 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 622 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 623 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 624 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 625 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 626 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 627 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 628 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 629 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 630 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 631 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 632 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 633 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 634 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 635 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 636 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 637 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 638 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 639 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 640 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 641 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 642 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 643 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 644 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 645 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 646 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 647 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 648 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 649 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 650 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 651 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 652 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 653 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 654 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 655 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 656 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 657 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 658 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 659 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 660 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 661 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 662 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 663 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 664 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 665 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 666 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 667 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 668 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 669 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 670 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 671 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 672 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 673 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 674 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 675 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 676 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 677 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 678 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 679 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 680 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 681 | google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de h1:5ANeKFmGdtiputJJYeUVg8nTGA/1bEirx4CgzcnPSx8= 682 | google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= 683 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 684 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 685 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 686 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 687 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 688 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 689 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 690 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 691 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 692 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 693 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 694 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 695 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 696 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 697 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 698 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 699 | google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= 700 | google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= 701 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 702 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 703 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 704 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 705 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 706 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 707 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 708 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 709 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 710 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 711 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 712 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 713 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 714 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 715 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 716 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 717 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 718 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 719 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 720 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 721 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 722 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 723 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 724 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 725 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 726 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 727 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 728 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 729 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 730 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 731 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 732 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 733 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 734 | gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 735 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 736 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 737 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 738 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 739 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 740 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 741 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 742 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 743 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 744 | istio.io/api v0.0.0-20221005164339-97dc20dc0ff3 h1:qIJexbt91u51lKTHBh6llz/a342L81U/IC9vGzJqrU8= 745 | istio.io/api v0.0.0-20221005164339-97dc20dc0ff3/go.mod h1:hQkF0Q19MCmfOTre/Sg4KvrwwETq45oaFplnBm2p4j8= 746 | k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= 747 | k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= 748 | k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= 749 | k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= 750 | k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 751 | k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= 752 | k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= 753 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 754 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 755 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 756 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= 757 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= 758 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= 759 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= 760 | sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= 761 | sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= 762 | --------------------------------------------------------------------------------