├── .terraform-version ├── examples └── cloud-init │ ├── multipass_module │ ├── provider.tf │ ├── main.tf │ └── vars.tf │ ├── provider.tf │ ├── main.tf │ ├── user_data.cfg │ ├── variables.tf │ └── README.md ├── terraform-registry-manifest.json ├── docs ├── index.md ├── data-sources │ └── instance.md └── resources │ └── instance.md ├── main.go ├── README.md ├── .gitignore ├── internal └── provider │ ├── provider_test.go │ ├── instance_data_source.go │ ├── utils.go │ ├── provider.go │ └── resource_instance.go ├── terraform-registry-manifest.json.asc ├── LICENSE ├── go.mod ├── .goreleaser.yml └── go.sum /.terraform-version: -------------------------------------------------------------------------------- 1 | 1.1.7 2 | -------------------------------------------------------------------------------- /examples/cloud-init/multipass_module/provider.tf: -------------------------------------------------------------------------------- 1 | ../provider.tf -------------------------------------------------------------------------------- /terraform-registry-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "metadata": { 4 | "protocol_versions": ["6.0"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/cloud-init/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | multipass = { 4 | source = "larstobi/multipass" 5 | version = "1.4.2" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "multipass Provider" 4 | subcategory: "" 5 | description: |- 6 | 7 | --- 8 | 9 | # multipass Provider 10 | 11 | 12 | 13 | 14 | 15 | 16 | ## Schema 17 | -------------------------------------------------------------------------------- /examples/cloud-init/multipass_module/main.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "multipass_instance" "multipass_vm" { 3 | count = var.instance_count 4 | cloudinit_file = var.user_data 5 | name = "${var.name_prefix}-${var.name}-ubuntu-${count.index + 1}" 6 | cpus = var.cpus 7 | memory = var.memory 8 | disk = var.disks 9 | image = var.image_name 10 | } -------------------------------------------------------------------------------- /examples/cloud-init/main.tf: -------------------------------------------------------------------------------- 1 | module "multipass_vm" { 2 | source = "./multipass_module" 3 | 4 | instance_count = var.instance_count 5 | user_data = "${path.module}/user_data.cfg" 6 | name_prefix = "vmtf" 7 | name = var.name 8 | image_name = var.image_name 9 | cpus = var.cpus 10 | memory = var.memory 11 | disks = var.disks 12 | } 13 | -------------------------------------------------------------------------------- /examples/cloud-init/user_data.cfg: -------------------------------------------------------------------------------- 1 | package_update: true 2 | package_upgrade: true 3 | 4 | users: 5 | - name: 'ubuntu' 6 | groups: users,adm,dialout,audio,netdev,video,plugdev,cdrom,games,input,gpio,spi,i2c,render,sudo 7 | shell: /bin/bash 8 | lock_passwd: true 9 | ssh_authorized_keys: 10 | - 'ssh-rsa < add your ssh pubulic key of a connecting machine >' 11 | 12 | ssh_deletekeys: false 13 | packages: 14 | - apache2 15 | - tmux 16 | - vim 17 | - wget 18 | - nmon 19 | -------------------------------------------------------------------------------- /examples/cloud-init/multipass_module/vars.tf: -------------------------------------------------------------------------------- 1 | variable "user_data" { 2 | type = string 3 | } 4 | 5 | variable "name" { 6 | type = string 7 | } 8 | 9 | variable "image_name" { 10 | type = string 11 | } 12 | 13 | variable "cpus" { 14 | type = number 15 | } 16 | 17 | variable "memory" { 18 | type = string 19 | } 20 | 21 | variable "disks" { 22 | type = string 23 | } 24 | 25 | variable "instance_count" { 26 | description = "Number of instances to create" 27 | type = number 28 | default = 1 29 | } 30 | 31 | variable "name_prefix" { 32 | description = "Instance name prefix" 33 | type = string 34 | default = "instance" 35 | } -------------------------------------------------------------------------------- /docs/data-sources/instance.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "multipass_instance Data Source - terraform-provider-multipass" 4 | subcategory: "" 5 | description: |- 6 | Instance data source 7 | --- 8 | 9 | # multipass_instance (Data Source) 10 | 11 | Instance data source 12 | 13 | 14 | 15 | 16 | ## Schema 17 | 18 | ### Required 19 | 20 | - `name` (String) Instance name 21 | 22 | ### Read-Only 23 | 24 | - `image` (String) The image of the instance 25 | - `image_hash` (String) The image_hash of the instance 26 | - `ipv4` (String) The IPv4 address of the instance 27 | - `state` (String) The state of the instance 28 | 29 | 30 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | 8 | "github.com/hashicorp/terraform-plugin-framework/providerserver" 9 | "terraform-provider-multipass/internal/provider" 10 | ) 11 | 12 | var ( 13 | version string = "dev" 14 | ) 15 | 16 | func main() { 17 | var debug bool 18 | 19 | flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") 20 | flag.Parse() 21 | 22 | opts := providerserver.ServeOpts{ 23 | Address: "registry.terraform.io/larstobi/multipass", 24 | Debug: debug, 25 | } 26 | 27 | err := providerserver.Serve(context.Background(), provider.New(version), opts) 28 | 29 | if err != nil { 30 | log.Fatal(err.Error()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Provider for Canonical Multipass 2 | 3 | 4 | The Terraform Multipass provider is a plugin for Terraform that allows for the full lifecycle management of instances using the Multipass virtual machine manager. 5 | 6 | ## Example 7 | 8 | ```hcl 9 | resource "multipass_instance" "test" { 10 | name = "instance-1234" 11 | cpus = 2 12 | image = "jammy" 13 | } 14 | 15 | provider "multipass" {} 16 | 17 | terraform { 18 | required_providers { 19 | multipass = { 20 | source = "larstobi/multipass" 21 | version = "~> 1.0.0" 22 | } 23 | } 24 | } 25 | ``` 26 | 27 | ## Contributing 28 | 29 | I appreciate your help! To contribute, please fork the repository, push your changes and make a Pull Request. 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | 11 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 12 | # .tfvars files are managed as part of configuration and so should be included in 13 | # version control. 14 | # 15 | # example.tfvars 16 | 17 | # Ignore override files as they are usually used to override resources locally and so 18 | # are not checked in 19 | override.tf 20 | override.tf.json 21 | *_override.tf 22 | *_override.tf.json 23 | 24 | # Include override files you do wish to add to version control using negated pattern 25 | # 26 | # !example_override.tf 27 | 28 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 29 | # example: *tfplan* 30 | -------------------------------------------------------------------------------- /internal/provider/provider_test.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/providerserver" 7 | "github.com/hashicorp/terraform-plugin-go/tfprotov6" 8 | ) 9 | 10 | // testAccProtoV6ProviderFactories are used to instantiate a provider during 11 | // acceptance testing. The factory function will be invoked for every Terraform 12 | // CLI command executed to create a provider server to which the CLI can 13 | // reattach. 14 | var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ 15 | "scaffolding": providerserver.NewProtocol6WithError(New("test")()), 16 | } 17 | 18 | func testAccPreCheck(t *testing.T) { 19 | // You can add code here to run prior to any test case execution, for example assertions 20 | // about the appropriate environment variables being set are common to see in a pre-check 21 | // function. 22 | } 23 | -------------------------------------------------------------------------------- /terraform-registry-manifest.json.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNATURE----- 2 | 3 | iQIzBAABCAAdFiEE5EFdn1FmEvjMM7aNeXcHMxvzVJwFAmLajTkACgkQeXcHMxvz 4 | VJw3kA//ZaspFse0zF5kSXjiHEe3oclLNNakryAVj0kNz6KfbNTGbChLxCRDcbYf 5 | L0TLK1QhEZR4oXs2EngRowUIubtWJE8HceWyQqPjqtb52kmlMWrNrb6w+iLU5mY5 6 | ng8m4KCbdx9E1Q2AYynCxuYF9cGh0j4xGhQKxewehnhco2e6o+J4TgokKYa8bvv0 7 | hJY9BVxdo3lx2fIvcaf5tc/zqveiTWKn45NA0NcL98DcCeWLnc8gBx9pu5NveBwH 8 | q4GyVAmvgHNsyG1j4CfKZ6eelxWPI5aM3Y4BGOoqXJdDSV+/nSZOmxRKNVPPbLBa 9 | FgrlcoVDfF3B7yrbrw5KzExhJVEjI+9DsONAnJGYauhrqNnfiluBdyqWmUMuKzOe 10 | yOd4A4BDuJfIWh2EY0WlRSAEK66rQe4Lhh4NpYoIbpB5Hnz2iU8/ZdpuT2hGxS2c 11 | l/heI+zi85AmMSBAc4apNvIqSRFacde7TVFdxV/AUvqUPEUclZV7bsriqCHXUOFl 12 | E9NUnL8iiMhbqCge0t/vYfzb1tQaF+J0Bg5FMAO9J9owxTub9XERR8yTGylXlGN+ 13 | y5HtDDuD2Xdg7tNWRRi7gXZYzV5/5HK8kKnFPJ0yVKhc0gEn5jPbUoRv4AP9JhK5 14 | fomzu/AWgoPzK2cEg9XutlTIQWf2SxnVhPZpQDlAsW2lbRhT+Ok= 15 | =HCSn 16 | -----END PGP SIGNATURE----- 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lars Tobias Skjong-Børsting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/cloud-init/variables.tf: -------------------------------------------------------------------------------- 1 | variable "user_data" { 2 | description = "cloudinit_file that contains bootstrap provision commands" 3 | type = string 4 | default = "./user_data" 5 | } 6 | 7 | variable "name" { 8 | description = "Name of the VM your creating" 9 | type = string 10 | default = "dev" 11 | } 12 | 13 | variable "image_name" { 14 | description = "ubuntu image name default jammy Lts" 15 | type = string 16 | default = "jammy" 17 | } 18 | 19 | variable "cpus" { 20 | description = "virtual cpu count" 21 | type = number 22 | default = 4 23 | } 24 | 25 | variable "memory" { 26 | description = "virtual Vm memory allocation" 27 | type = string 28 | default = "4G" 29 | } 30 | 31 | variable "disks" { 32 | description = "Thin provisioned disk size" 33 | type = string 34 | default = "20G" 35 | } 36 | 37 | variable "instance_count" { 38 | description = "Number of instances to create" 39 | type = number 40 | default = 2 41 | } 42 | 43 | variable "name_prefix" { 44 | description = "Instance name prefix" 45 | type = string 46 | default = "instance" 47 | } 48 | -------------------------------------------------------------------------------- /docs/resources/instance.md: -------------------------------------------------------------------------------- 1 | --- 2 | # generated by https://github.com/hashicorp/terraform-plugin-docs 3 | page_title: "multipass_instance Resource - terraform-provider-multipass" 4 | subcategory: "" 5 | description: |- 6 | Multipass instance resource. 7 | --- 8 | 9 | # multipass_instance (Resource) 10 | 11 | Multipass instance resource. 12 | 13 | 14 | 15 | 16 | ## Schema 17 | 18 | ### Required 19 | 20 | - `name` (String) Name for the instance. If it is 'primary' (the configured primary instance name), the user's home directory is mounted inside the newly launched instance, in 'Home'. 21 | 22 | ### Optional 23 | 24 | - `cloudinit_file` (String) Path to a user-data cloud-init configuration. 25 | - `cpus` (Number) Number of CPUs to allocate. Minimum: 1, default: 1. 26 | - `disk` (String) Disk space to allocate. Positive integers, in KiB, MiB, GiB or TiB suffix. Minimum: 512MiB, default: 5GiB. 27 | - `image` (String) Optional image to launch. If omitted, then the default Ubuntu LTS will be used. can be either ‘release’ or ‘daily‘. If is omitted, ‘release’ will be used. can be a partial image hash or an Ubuntu release version, codename or alias. is a custom image URL that is in http://, https://, or file:// format. 28 | - `memory` (String) Amount of memory to allocate. Positive integers, in KiB, MiB, GiB or TiB suffix. Minimum: 128MiB, default: 1GiB. 29 | 30 | 31 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module terraform-provider-multipass 2 | 3 | go 1.18 4 | 5 | require ( 6 | code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5 7 | github.com/hashicorp/terraform-plugin-framework v0.10.0 8 | github.com/hashicorp/terraform-plugin-go v0.12.0 9 | github.com/hashicorp/terraform-plugin-log v0.7.0 10 | github.com/larstobi/go-multipass v1.2.1 11 | ) 12 | 13 | require ( 14 | github.com/fatih/color v1.13.0 // indirect 15 | github.com/golang/protobuf v1.5.2 // indirect 16 | github.com/google/go-cmp v0.5.8 // indirect 17 | github.com/hashicorp/go-hclog v1.2.1 // indirect 18 | github.com/hashicorp/go-plugin v1.4.4 // indirect 19 | github.com/hashicorp/go-uuid v1.0.3 // indirect 20 | github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c // indirect 21 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect 22 | github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect 23 | github.com/mattn/go-colorable v0.1.12 // indirect 24 | github.com/mattn/go-isatty v0.0.14 // indirect 25 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 26 | github.com/oklog/run v1.1.0 // indirect 27 | github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect 28 | github.com/vmihailenco/tagparser v0.1.2 // indirect 29 | golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect 30 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect 31 | golang.org/x/text v0.3.7 // indirect 32 | google.golang.org/appengine v1.6.7 // indirect 33 | google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 // indirect 34 | google.golang.org/grpc v1.48.0 // indirect 35 | google.golang.org/protobuf v1.28.0 // indirect 36 | ) 37 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Visit https://goreleaser.com for documentation on how to customize this 2 | # behavior. 3 | before: 4 | hooks: 5 | # this is just an example and not a requirement for provider building/publishing 6 | - go mod tidy 7 | builds: 8 | - env: 9 | # goreleaser does not work with CGO, it could also complicate 10 | # usage by users in CI/CD systems like Terraform Cloud where 11 | # they are unable to install libraries. 12 | - CGO_ENABLED=0 13 | mod_timestamp: '{{ .CommitTimestamp }}' 14 | flags: 15 | - -trimpath 16 | ldflags: 17 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' 18 | goos: 19 | - freebsd 20 | - windows 21 | - linux 22 | - darwin 23 | goarch: 24 | - amd64 25 | - '386' 26 | - arm 27 | - arm64 28 | ignore: 29 | - goos: darwin 30 | goarch: '386' 31 | binary: '{{ .ProjectName }}_v{{ .Version }}' 32 | archives: 33 | - format: zip 34 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 35 | checksum: 36 | extra_files: 37 | - glob: 'terraform-registry-manifest.json' 38 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' 39 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 40 | algorithm: sha256 41 | signs: 42 | - artifacts: checksum 43 | args: 44 | # if you are using this in a GitHub action or some other automated pipeline, you 45 | # need to pass the batch flag to indicate its not interactive. 46 | - "--batch" 47 | - "--local-user" 48 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 49 | - "--output" 50 | - "${signature}" 51 | - "--detach-sign" 52 | - "${artifact}" 53 | release: 54 | extra_files: 55 | - glob: 'terraform-registry-manifest.json' 56 | name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json' 57 | # If you want to manually examine the release before its live, uncomment this line: 58 | # draft: true 59 | changelog: 60 | skip: true 61 | -------------------------------------------------------------------------------- /examples/cloud-init/README.md: -------------------------------------------------------------------------------- 1 | # Example of Using the Terraform Provider for Multipass Hypervisor 2 | 3 | **URL:** [Terraform Provider for Multipass Hypervisor](https://registry.terraform.io/providers/larstobi/multipass/1.4.2) 4 | **Author:** Lars Tobias Skjong-Børsting 5 | 6 | Multipass Hypervisor created by Canonical [Ubuntu] 7 | **URL:** [Multipass](https://multipass.run/) 8 | 9 | Terraform example provided by Robert Weaver 10 | 11 | ## What is this Example 12 | 13 | This Terraform module will create 2 example VMs within the Multipass hypervisor and will use cloud-init to install the following packages on each of the two servers upon bootup, as well as ensuring that the server is updated and SSH keys are installed. 14 | 15 | Packages to be installed: 16 | - Apache2 17 | - tmux 18 | - nmon 19 | 20 | ## Terraform Module Structure 21 | 22 | | File/Folder | Description | 23 | |-----------------------------|-------------------------------------------------| 24 | | README.md | This README | 25 | | main.tf | Main calling module | 26 | | multipass_module/ | Main module folder | 27 | | multipass_module/main.tf | Main Terraform module | 28 | | multipass_module/provider.tf | Link to the provider in the main root folder | 29 | | multipass_module/vars.tf | Variables passed to the main Multipass module | 30 | | provider.tf | Main provider for Multipass version 1.4.2 | 31 | | user_data.cfg | Bootstrap installation of packages, SSH keys, and VM upgrade | 32 | | variables.tf | Variables used by the module and default settings | 33 | 34 | 35 | ## Module Configuration 36 | 37 | ```hcl 38 | resource "multipass_instance" "multipass_vm" { 39 | count = var.instance_count 40 | cloudinit_file = "${path.module}/user_data.cfg" 41 | name = var.vm_name 42 | cpus = var.cpus 43 | memory = var.memory 44 | disk = var.disks 45 | image = var.image_name 46 | } ``` 47 | 48 | ## Running the Terraform Plan 49 | 50 | Change directory to the main root directory. 51 | Run terraform init. 52 | Run terraform validate. 53 | Run terraform plan. 54 | Run terraform apply (answer yes to proceed). 55 | -------------------------------------------------------------------------------- /internal/provider/instance_data_source.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/hashicorp/terraform-plugin-framework/diag" 8 | "github.com/hashicorp/terraform-plugin-framework/tfsdk" 9 | "github.com/hashicorp/terraform-plugin-framework/types" 10 | "github.com/larstobi/go-multipass/multipass" 11 | ) 12 | 13 | var _ tfsdk.DataSourceType = instanceDataSourceType{} 14 | var _ tfsdk.DataSource = instanceDataSource{} 15 | 16 | type instanceDataSourceType struct{} 17 | 18 | func (t instanceDataSourceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { 19 | return tfsdk.Schema{ 20 | MarkdownDescription: "Instance data source", 21 | 22 | Attributes: map[string]tfsdk.Attribute{ 23 | "name": { 24 | MarkdownDescription: "Instance name", 25 | Type: types.StringType, 26 | Required: true, 27 | }, 28 | "ipv4": { 29 | MarkdownDescription: "The IPv4 address of the instance", 30 | Type: types.StringType, 31 | Computed: true, 32 | }, 33 | "state": { 34 | MarkdownDescription: "The state of the instance", 35 | Type: types.StringType, 36 | Computed: true, 37 | }, 38 | "image": { 39 | MarkdownDescription: "The image of the instance", 40 | Type: types.StringType, 41 | Computed: true, 42 | }, 43 | "image_hash": { 44 | MarkdownDescription: "The image_hash of the instance", 45 | Type: types.StringType, 46 | Computed: true, 47 | }, 48 | }, 49 | }, nil 50 | } 51 | 52 | func (t instanceDataSourceType) NewDataSource(ctx context.Context, in tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { 53 | provider, diags := convertProviderType(in) 54 | 55 | return instanceDataSource{ 56 | provider: provider, 57 | }, diags 58 | } 59 | 60 | type instanceDataSourceData struct { 61 | Name types.String `tfsdk:"name"` 62 | IPv4 types.String `tfsdk:"ipv4"` 63 | State types.String `tfsdk:"state"` 64 | Image types.String `tfsdk:"image"` 65 | ImageHash types.String `tfsdk:"image_hash"` 66 | } 67 | 68 | type instanceDataSource struct { 69 | provider provider 70 | } 71 | 72 | func (d instanceDataSource) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { 73 | var data instanceDataSourceData 74 | 75 | diags := req.Config.Get(ctx, &data) 76 | resp.Diagnostics.Append(diags...) 77 | 78 | if resp.Diagnostics.HasError() { 79 | return 80 | } 81 | 82 | instance, err := multipass.Info(&multipass.InfoRequest{Name: data.Name.Value}) 83 | if err != nil { 84 | resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read instance, got error: %s", err)) 85 | return 86 | } 87 | 88 | data.IPv4 = types.String{Value: instance.IP} 89 | data.State = types.String{Value: instance.State} 90 | data.Image = types.String{Value: instance.Image} 91 | data.ImageHash = types.String{Value: instance.ImageHash} 92 | 93 | diags = resp.State.Set(ctx, &data) 94 | resp.Diagnostics.Append(diags...) 95 | } 96 | -------------------------------------------------------------------------------- /internal/provider/utils.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "errors" 5 | "math/big" 6 | "strconv" 7 | "strings" 8 | 9 | "code.cloudfoundry.org/bytefmt" 10 | "github.com/hashicorp/terraform-plugin-framework/types" 11 | "github.com/larstobi/go-multipass/multipass" 12 | ) 13 | 14 | func QueryInstance(state Instance) (*Instance, error) { 15 | out, err := multipass.Get(&multipass.GetReq{Name: state.Name.Value}) 16 | if err != nil { 17 | return nil, errors.New("Could not get local value. " + err.Error()) 18 | } 19 | 20 | // Check CPUS 21 | current_cpus := state.CPUS 22 | // If CPUS is not specified, then ignore changes 23 | if !state.CPUS.Null { 24 | cpus := new(big.Float) 25 | cpus.SetString(out.CPUS) 26 | if cpus != state.CPUS.Value { 27 | current_cpus = types.Number{Value: cpus} 28 | } 29 | } 30 | 31 | // Check Memory 32 | current_memory := state.Memory 33 | // If Memory is not specified, then ignore changes 34 | if !state.Memory.Null { 35 | if equal, err := CompareDataSizes(out.Memory, state.Memory.Value); err != nil { 36 | return nil, errors.New("Error comparing memory size: " + err.Error()) 37 | } else { 38 | if !*equal { 39 | current_memory = types.String{Value: RemoveZeroDecimalAndSpaces(out.Memory)} 40 | } 41 | } 42 | } 43 | 44 | // Check Disk 45 | current_disk := state.Disk 46 | // If Disk is not specified, then ignore changes 47 | if !state.Disk.Null { 48 | if equal, err := CompareDataSizes(out.Disk, state.Disk.Value); err != nil { 49 | return nil, errors.New("Error comparing disk size: " + err.Error()) 50 | } else { 51 | if !*equal { 52 | current_disk = types.String{Value: RemoveZeroDecimalAndSpaces(out.Disk)} 53 | } 54 | } 55 | } 56 | 57 | // Generate resource state struct 58 | var result = Instance{ 59 | Name: state.Name, 60 | Image: state.Image, 61 | CPUS: current_cpus, 62 | Memory: current_memory, 63 | Disk: current_disk, 64 | CloudInitFile: state.CloudInitFile, 65 | } 66 | 67 | return &result, nil 68 | } 69 | 70 | func ToMegabytes(input string) (string, error) { 71 | input = RemoveZeroDecimalAndSpaces(input) 72 | input = strings.ReplaceAll(input, "\"", "") 73 | 74 | output, err := bytefmt.ToMegabytes(input) 75 | if err != nil { 76 | return "", err 77 | 78 | } 79 | 80 | result := strconv.FormatUint(output, 10) 81 | return result, nil 82 | } 83 | 84 | // multipass get returns memory and disk values with a .0 decimal 85 | func RemoveZeroDecimalAndSpaces(input string) string { 86 | input = strings.ReplaceAll(input, " ", "") 87 | sep := strings.LastIndexAny(input, "01234567890. ") 88 | var number, suffix string 89 | number = input[:sep+1] 90 | suffix = input[sep+1:] 91 | return strings.TrimSuffix(number, ".0") + suffix 92 | } 93 | 94 | func CompareDataSizes(value1 string, value2 string) (*bool, error) { 95 | result1, result1_err := ToMegabytes(value1) 96 | if result1_err != nil { 97 | return nil, result1_err 98 | } 99 | 100 | result2, result2_err := ToMegabytes(value2) 101 | if result2_err != nil { 102 | return nil, result2_err 103 | } 104 | 105 | equal := result1 == result2 106 | return &equal, nil 107 | } 108 | -------------------------------------------------------------------------------- /internal/provider/provider.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/hashicorp/terraform-plugin-framework/diag" 9 | "github.com/hashicorp/terraform-plugin-framework/tfsdk" 10 | // "github.com/hashicorp/terraform-plugin-framework/types" 11 | ) 12 | 13 | var stderr = os.Stderr 14 | 15 | type provider struct { 16 | configured bool 17 | version string 18 | } 19 | 20 | // type providerData struct { 21 | // Address types.String `tfsdk:"address"` 22 | // } 23 | 24 | func (p *provider) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { 25 | return tfsdk.Schema{ 26 | Attributes: map[string]tfsdk.Attribute{}, 27 | }, nil 28 | } 29 | 30 | type providerData struct{} 31 | 32 | func (p *provider) Configure(ctx context.Context, req tfsdk.ConfigureProviderRequest, resp *tfsdk.ConfigureProviderResponse) { 33 | // Retrieve provider data from configuration 34 | var data providerData 35 | diags := req.Config.Get(ctx, &data) 36 | resp.Diagnostics.Append(diags...) 37 | if resp.Diagnostics.HasError() { 38 | return 39 | } 40 | 41 | p.configured = true 42 | } 43 | 44 | func (p *provider) GetResources(_ context.Context) (map[string]tfsdk.ResourceType, diag.Diagnostics) { 45 | return map[string]tfsdk.ResourceType{ 46 | "multipass_instance": instanceResourceType{}, 47 | }, nil 48 | } 49 | 50 | func (p *provider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { 51 | return map[string]tfsdk.DataSourceType{ 52 | "multipass_instance": instanceDataSourceType{}, 53 | }, nil 54 | 55 | } 56 | 57 | // func (p *provider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) { 58 | // return tfsdk.Schema{ 59 | // Attributes: map[string]tfsdk.Attribute{ 60 | // "address": { 61 | // MarkdownDescription: "Specifies which address to use for the multipassd service. A socket can be specified using unix: or a TCP address can be specified using ", 62 | // Optional: true, 63 | // Type: types.StringType, 64 | // }, 65 | // }, 66 | // }, nil 67 | // } 68 | 69 | func New(version string) func() tfsdk.Provider { 70 | return func() tfsdk.Provider { 71 | return &provider{ 72 | version: version, 73 | } 74 | } 75 | } 76 | 77 | func convertProviderType(in tfsdk.Provider) (provider, diag.Diagnostics) { 78 | var diags diag.Diagnostics 79 | 80 | p, ok := in.(*provider) 81 | 82 | if !ok { 83 | diags.AddError( 84 | "Unexpected Provider Instance Type", 85 | fmt.Sprintf("While creating the data source or resource, an unexpected provider type (%T) was received. This is always a bug in the provider code and should be reported to the provider developers.", p), 86 | ) 87 | return provider{}, diags 88 | } 89 | 90 | if p == nil { 91 | diags.AddError( 92 | "Unexpected Provider Instance Type", 93 | "While creating the data source or resource, an unexpected empty provider instance was received. This is always a bug in the provider code and should be reported to the provider developers.", 94 | ) 95 | return provider{}, diags 96 | } 97 | 98 | return *p, diags 99 | } 100 | -------------------------------------------------------------------------------- /internal/provider/resource_instance.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hashicorp/terraform-plugin-framework/diag" 7 | "github.com/hashicorp/terraform-plugin-framework/path" 8 | "github.com/hashicorp/terraform-plugin-framework/tfsdk" 9 | "github.com/hashicorp/terraform-plugin-framework/types" 10 | "github.com/hashicorp/terraform-plugin-log/tflog" 11 | "github.com/larstobi/go-multipass/multipass" 12 | ) 13 | 14 | var _ tfsdk.ResourceType = instanceResourceType{} 15 | var _ tfsdk.Resource = instanceResource{} 16 | var _ tfsdk.ResourceWithImportState = instanceResource{} 17 | 18 | type instanceResourceType struct{} 19 | 20 | func (r instanceResourceType) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { 21 | return tfsdk.Schema{ 22 | MarkdownDescription: "Multipass instance resource.", 23 | Version: 0, 24 | Attributes: map[string]tfsdk.Attribute{ 25 | "name": { 26 | MarkdownDescription: "Name for the instance. If it is 'primary' " + 27 | "(the configured primary instance name), the user's " + 28 | "home directory is mounted inside the newly launched " + 29 | "instance, in 'Home'.", 30 | Type: types.StringType, 31 | Required: true, 32 | PlanModifiers: []tfsdk.AttributePlanModifier{ 33 | tfsdk.RequiresReplace(), 34 | }, 35 | }, 36 | "image": { 37 | MarkdownDescription: "Optional image to launch. If omitted, then " + 38 | "the default Ubuntu LTS will be used. can be " + 39 | "either ‘release’ or ‘daily‘. If is " + 40 | "omitted, ‘release’ will be used. can be a " + 41 | "partial image hash or an Ubuntu release version, " + 42 | "codename or alias. is a custom image URL " + 43 | "that is in http://, https://, or file:// format.", 44 | Type: types.StringType, 45 | Optional: true, 46 | PlanModifiers: []tfsdk.AttributePlanModifier{ 47 | tfsdk.RequiresReplace(), 48 | }, 49 | }, 50 | "cpus": { 51 | MarkdownDescription: "Number of CPUs to allocate. Minimum: 1, default: 1.", 52 | Type: types.NumberType, 53 | Optional: true, 54 | PlanModifiers: []tfsdk.AttributePlanModifier{ 55 | tfsdk.RequiresReplace(), 56 | }, 57 | }, 58 | "memory": { 59 | MarkdownDescription: "Amount of memory to allocate. Positive integers, " + 60 | "in KiB, MiB, GiB or TiB suffix. Minimum: 128MiB, default: 1GiB.", 61 | Type: types.StringType, 62 | Optional: true, 63 | PlanModifiers: []tfsdk.AttributePlanModifier{ 64 | tfsdk.RequiresReplace(), 65 | }, 66 | }, 67 | "disk": { 68 | MarkdownDescription: "Disk space to allocate. Positive integers, " + 69 | "in KiB, MiB, GiB or TiB suffix. Minimum: 512MiB, default: 5GiB.", 70 | Type: types.StringType, 71 | Optional: true, 72 | PlanModifiers: []tfsdk.AttributePlanModifier{ 73 | tfsdk.RequiresReplace(), 74 | }, 75 | }, 76 | "cloudinit_file": { 77 | MarkdownDescription: "Path to a user-data cloud-init configuration.", 78 | Type: types.StringType, 79 | Optional: true, 80 | PlanModifiers: []tfsdk.AttributePlanModifier{ 81 | tfsdk.RequiresReplace(), 82 | }, 83 | }, 84 | }, 85 | }, nil 86 | } 87 | 88 | func (t instanceResourceType) NewResource(ctx context.Context, in tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) { 89 | provider, diags := convertProviderType(in) 90 | 91 | return instanceResource{ 92 | provider: provider, 93 | }, diags 94 | } 95 | 96 | type Instance struct { 97 | Name types.String `tfsdk:"name"` 98 | Image types.String `tfsdk:"image"` 99 | CPUS types.Number `tfsdk:"cpus"` 100 | Memory types.String `tfsdk:"memory"` 101 | Disk types.String `tfsdk:"disk"` 102 | CloudInitFile types.String `tfsdk:"cloudinit_file"` 103 | } 104 | 105 | type instanceResource struct { 106 | provider provider 107 | } 108 | 109 | func (r instanceResource) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) { 110 | // Retrieve values from plan 111 | var plan Instance 112 | diags := req.Plan.Get(ctx, &plan) 113 | resp.Diagnostics.Append(diags...) 114 | if resp.Diagnostics.HasError() { 115 | return 116 | } 117 | 118 | tflog.Info(ctx, "Multipass instanceResource", map[string]interface{}{ 119 | "name": plan.Name.String(), 120 | }) 121 | 122 | var cpus string 123 | if plan.CPUS.Null { 124 | cpus = "" 125 | } else { 126 | cpus = plan.CPUS.Value.String() 127 | } 128 | 129 | _, err := multipass.LaunchV2(&multipass.LaunchReqV2{ 130 | Name: plan.Name.Value, 131 | Image: plan.Image.Value, 132 | CPUS: cpus, 133 | Memory: plan.Memory.Value, 134 | Disk: plan.Disk.Value, 135 | CloudInitFile: plan.CloudInitFile.Value, 136 | }) 137 | 138 | if err != nil { 139 | resp.Diagnostics.AddError( 140 | "Error from multipass", 141 | "Could not create instance, unexpected error: "+err.Error(), 142 | ) 143 | return 144 | } 145 | 146 | diags = resp.State.Set(ctx, plan) 147 | resp.Diagnostics.Append(diags...) 148 | if resp.Diagnostics.HasError() { 149 | return 150 | } 151 | } 152 | 153 | func (r instanceResource) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) { 154 | // Get current state 155 | var state Instance 156 | diags := req.State.Get(ctx, &state) 157 | resp.Diagnostics.Append(diags...) 158 | if resp.Diagnostics.HasError() { 159 | return 160 | } 161 | 162 | instanceInfo, infoErr := multipass.Info(&multipass.InfoRequest{Name: state.Name.Value}) 163 | if instanceInfo == nil || infoErr != nil { 164 | tflog.Warn(ctx, "Multipass instance not found, removing from state.", map[string]interface{}{ 165 | "name": state.Name.Value, 166 | }) 167 | resp.State.RemoveResource(ctx) 168 | return 169 | } 170 | 171 | result, err := QueryInstance(state) 172 | if err != nil { 173 | resp.Diagnostics.AddError( 174 | "Error from multipass", 175 | "Could not query instance: "+err.Error(), 176 | ) 177 | return 178 | } 179 | 180 | // Set state 181 | diags = resp.State.Set(ctx, result) 182 | resp.Diagnostics.Append(diags...) 183 | if resp.Diagnostics.HasError() { 184 | return 185 | } 186 | } 187 | 188 | func (r instanceResource) Update(ctx context.Context, req tfsdk.UpdateResourceRequest, resp *tfsdk.UpdateResourceResponse) { 189 | // multipass stop instance 190 | // multipass set local.instance.cpus etc 191 | // multipass start instance 192 | } 193 | 194 | func (r instanceResource) Delete(ctx context.Context, req tfsdk.DeleteResourceRequest, resp *tfsdk.DeleteResourceResponse) { 195 | var state Instance 196 | diags := req.State.Get(ctx, &state) 197 | resp.Diagnostics.Append(diags...) 198 | if resp.Diagnostics.HasError() { 199 | return 200 | } 201 | 202 | // Delete Instance by calling API 203 | err := multipass.Delete(&multipass.DeleteRequest{Name: state.Name.Value}) 204 | if err != nil { 205 | resp.Diagnostics.AddError( 206 | "Error from multipass", 207 | "Could not delete instance "+state.Name.Value+": "+err.Error(), 208 | ) 209 | return 210 | } 211 | 212 | // Remove resource from state 213 | resp.State.RemoveResource(ctx) 214 | 215 | } 216 | 217 | // Import resource 218 | func (r instanceResource) ImportState(ctx context.Context, req tfsdk.ImportResourceStateRequest, resp *tfsdk.ImportResourceStateResponse) { 219 | // Save the import identifier in the id attribute 220 | tfsdk.ResourceImportStatePassthroughID(ctx, path.Root("name"), req, resp) 221 | } 222 | -------------------------------------------------------------------------------- /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 | code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5 h1:tM5+dn2C9xZw1RzgI6WTQW1rGqdUimKB3RFbyu4h6Hc= 4 | code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5/go.mod h1:v4VVB6oBMz/c9fRY6vZrwr5xKRWOH5NPDjQZlPk0Gbs= 5 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 6 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 7 | github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= 8 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 9 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 10 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 11 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 12 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 13 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 14 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 15 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 16 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 17 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 19 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 21 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 22 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 23 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 24 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 25 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 26 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 27 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 28 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 29 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 30 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 31 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 32 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 33 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 34 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 35 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 36 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 37 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 38 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 39 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 40 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 41 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 42 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 43 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 44 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 45 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 46 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 47 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 48 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 49 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 50 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 51 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 52 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 53 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 54 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 55 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 56 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 57 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 58 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 59 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 60 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 61 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 62 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 63 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 64 | github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= 65 | github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 66 | github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ= 67 | github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= 68 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= 69 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 70 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 71 | github.com/hashicorp/terraform-plugin-framework v0.10.0 h1:LGYcnvNdVaZA1ZHe53BHLVjaaGs7HTiq6+9Js29stL4= 72 | github.com/hashicorp/terraform-plugin-framework v0.10.0/go.mod h1:CK7Opzukfu/2CPJs+HzUdfHrFlp+ZIQeSxjF0x8k464= 73 | github.com/hashicorp/terraform-plugin-go v0.12.0 h1:6wW9mT1dSs0Xq4LR6HXj1heQ5ovr5GxXNJwkErZzpJw= 74 | github.com/hashicorp/terraform-plugin-go v0.12.0/go.mod h1:kwhmaWHNDvT1B3QiSJdAtrB/D4RaKSY/v3r2BuoWK4M= 75 | github.com/hashicorp/terraform-plugin-log v0.7.0 h1:SDxJUyT8TwN4l5b5/VkiTIaQgY6R+Y2BQ0sRZftGKQs= 76 | github.com/hashicorp/terraform-plugin-log v0.7.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= 77 | github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c h1:D8aRO6+mTqHfLsK/BC3j5OAoogv1WLRWzY1AaTo3rBg= 78 | github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI= 79 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= 80 | github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= 81 | github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= 82 | github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= 83 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 84 | github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= 85 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 86 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 87 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 88 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 89 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 90 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 91 | github.com/larstobi/go-multipass v1.2.1 h1:9OJKnGBlUvCPkCrS6MOzfRavGRDNMYRDAHHQPggsh0Y= 92 | github.com/larstobi/go-multipass v1.2.1/go.mod h1:RkIg1w+XE1w+BVRVIZCPjtUWyT+p9R3Bdyn+MUkGiNg= 93 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 94 | github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 95 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 96 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 97 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 98 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 99 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 100 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 101 | github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758= 102 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 103 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 104 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 105 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 106 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 107 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 108 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 109 | github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= 110 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 111 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 112 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 113 | github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= 114 | github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 115 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 116 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 117 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 118 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 119 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 120 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 121 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 122 | github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= 123 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 124 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 125 | github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= 126 | github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= 127 | github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 128 | github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= 129 | github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 130 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 131 | github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= 132 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 133 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 134 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 135 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 136 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 137 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 138 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 139 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 140 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 141 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 142 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 143 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 144 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 145 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 146 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 147 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 148 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 149 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 150 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 151 | golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 152 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 153 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 154 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 155 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 156 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 157 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 158 | golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= 159 | golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 160 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 161 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 162 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 163 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 164 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 165 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 166 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 167 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 168 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 169 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 170 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 171 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 172 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 173 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 174 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 175 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 176 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 177 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 178 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 179 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 180 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 181 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 182 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 183 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 184 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 185 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 186 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= 187 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 188 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 189 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 190 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 191 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 192 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 193 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 194 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 195 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 196 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 197 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 198 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 199 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 200 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 201 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 202 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 203 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 204 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 205 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 206 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 207 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 208 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 209 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 210 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 211 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 212 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 213 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 214 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 215 | google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9 h1:1aEQRgZ4Gks2SRAkLzIPpIszRazwVfjSFe1cKc+e0Jg= 216 | google.golang.org/genproto v0.0.0-20220715211116-798f69b842b9/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= 217 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 218 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 219 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 220 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 221 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 222 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 223 | google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= 224 | google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 225 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 226 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 227 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 228 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 229 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 230 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 231 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 232 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 233 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 234 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 235 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 236 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 237 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 238 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 239 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 240 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 241 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 242 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 243 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 244 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 245 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 246 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 247 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 248 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 249 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 250 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 251 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 252 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 253 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 254 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 255 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 256 | --------------------------------------------------------------------------------