diff --git a/.github_changelog_generator b/.github_changelog_generator deleted file mode 100644 index 519bb291..00000000 --- a/.github_changelog_generator +++ /dev/null @@ -1,6 +0,0 @@ -since_tag=v0.4.4 -exclude-labels=duplicate,question,invalid,wontfix,dependencies -pr-wo-labels=false -bugs-label=BUG FIXES: -breaking-label=BREAKING CHANGES: -enhancement-label=ENHANCEMENTS: diff --git a/.go-version b/.go-version deleted file mode 100644 index 84cc5294..00000000 --- a/.go-version +++ /dev/null @@ -1 +0,0 @@ -1.18.0 diff --git a/go.mod b/go.mod index f5479a00..a37a4c65 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,11 @@ module github.com/bpg/terraform-provider-proxmox go 1.20 +replace github.com/bpg/proxmox-api => ../proxmox-api + require ( - github.com/google/go-querystring v1.1.0 + github.com/bpg/proxmox-api v0.0.0-20230715175825-fd46d7fc2f0c + github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.3.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-framework v1.3.2 @@ -12,10 +15,10 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.11.1 github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0 - github.com/pkg/sftp v1.13.5 - github.com/skeema/knownhosts v1.1.1 + github.com/pkg/sftp v1.13.5 // indirect + github.com/skeema/knownhosts v1.1.1 // indirect github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.11.0 + golang.org/x/crypto v0.11.0 // indirect golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 ) @@ -45,7 +48,7 @@ require ( github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/kr/fs v0.1.0 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -55,6 +58,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.6.1 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 73f28ad1..8eb85709 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/bpg/proxmox-api v0.0.0-20230715175825-fd46d7fc2f0c h1:cPvk1HoTGJBesKwcs8bYa+pPA5Kudgt9NYFCYdtelBc= +github.com/bpg/proxmox-api v0.0.0-20230715175825-fd46d7fc2f0c/go.mod h1:oWrrYJWZZqBOqHrAqaUxZpf0w6RQR2l1nxDy/QKkLHo= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= @@ -94,7 +96,6 @@ github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= diff --git a/go.work b/go.work new file mode 100644 index 00000000..fb2ecc5b --- /dev/null +++ b/go.work @@ -0,0 +1,7 @@ +go 1.20 + +use ( + . + tools + ../proxmox-api +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 00000000..34beef47 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,8 @@ +github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= +github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= +github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= diff --git a/internal/network/resource_linux_bridge.go b/internal/network/resource_linux_bridge.go index 84b00534..1b334aad 100644 --- a/internal/network/resource_linux_bridge.go +++ b/internal/network/resource_linux_bridge.go @@ -24,9 +24,12 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - pvetypes "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" + "github.com/bpg/proxmox-api" + "github.com/bpg/proxmox-api/nodes" + + pvetypes "github.com/bpg/proxmox-api/types" + + nettypes "github.com/bpg/terraform-provider-proxmox/internal/network/types" ) var ( @@ -40,10 +43,10 @@ type linuxBridgeResourceModel struct { ID types.String `tfsdk:"id"` NodeName types.String `tfsdk:"node_name"` Name types.String `tfsdk:"name"` - Address pvetypes.IPCIDRValue `tfsdk:"address"` - Gateway pvetypes.IPAddrValue `tfsdk:"gateway"` - Address6 pvetypes.IPCIDRValue `tfsdk:"address6"` - Gateway6 pvetypes.IPAddrValue `tfsdk:"gateway6"` + Address nettypes.IPCIDRValue `tfsdk:"address"` + Gateway nettypes.IPAddrValue `tfsdk:"gateway"` + Address6 nettypes.IPCIDRValue `tfsdk:"address6"` + Gateway6 nettypes.IPAddrValue `tfsdk:"gateway6"` Autostart types.Bool `tfsdk:"autostart"` MTU types.Int64 `tfsdk:"mtu"` Comment types.String `tfsdk:"comment"` @@ -95,10 +98,10 @@ func (m *linuxBridgeResourceModel) importFromNetworkInterfaceList( ctx context.Context, iface *nodes.NetworkInterfaceListResponseData, ) error { - m.Address = pvetypes.NewIPCIDRPointerValue(iface.CIDR) - m.Gateway = pvetypes.NewIPAddrPointerValue(iface.Gateway) - m.Address6 = pvetypes.NewIPCIDRPointerValue(iface.CIDR6) - m.Gateway6 = pvetypes.NewIPAddrPointerValue(iface.Gateway6) + m.Address = nettypes.NewIPCIDRPointerValue(iface.CIDR) + m.Gateway = nettypes.NewIPAddrPointerValue(iface.Gateway) + m.Address6 = nettypes.NewIPCIDRPointerValue(iface.CIDR6) + m.Gateway6 = nettypes.NewIPAddrPointerValue(iface.Gateway6) m.Autostart = types.BoolPointerValue(iface.Autostart.PointerBool()) if iface.MTU != nil { @@ -189,22 +192,22 @@ func (r *linuxBridgeResource) Schema( }, "address": schema.StringAttribute{ Description: "The interface IPv4/CIDR address.", - CustomType: pvetypes.IPCIDRType{}, + CustomType: nettypes.IPCIDRType{}, Optional: true, }, "gateway": schema.StringAttribute{ Description: "Default gateway address.", - CustomType: pvetypes.IPAddrType{}, + CustomType: nettypes.IPAddrType{}, Optional: true, }, "address6": schema.StringAttribute{ Description: "The interface IPv6/CIDR address.", - CustomType: pvetypes.IPCIDRType{}, + CustomType: nettypes.IPCIDRType{}, Optional: true, }, "gateway6": schema.StringAttribute{ Description: "Default IPv6 gateway address.", - CustomType: pvetypes.IPAddrType{}, + CustomType: nettypes.IPAddrType{}, Optional: true, }, "autostart": schema.BoolAttribute{ diff --git a/internal/network/resource_linux_vlan.go b/internal/network/resource_linux_vlan.go index e53fd911..61817c75 100644 --- a/internal/network/resource_linux_vlan.go +++ b/internal/network/resource_linux_vlan.go @@ -22,9 +22,12 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - pvetypes "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" + "github.com/bpg/proxmox-api" + "github.com/bpg/proxmox-api/nodes" + + pvetypes "github.com/bpg/proxmox-api/types" + + nettypes "github.com/bpg/terraform-provider-proxmox/internal/network/types" ) var ( @@ -38,10 +41,10 @@ type linuxVLANResourceModel struct { ID types.String `tfsdk:"id"` NodeName types.String `tfsdk:"node_name"` Name types.String `tfsdk:"name"` - Address pvetypes.IPCIDRValue `tfsdk:"address"` - Gateway pvetypes.IPAddrValue `tfsdk:"gateway"` - Address6 pvetypes.IPCIDRValue `tfsdk:"address6"` - Gateway6 pvetypes.IPAddrValue `tfsdk:"gateway6"` + Address nettypes.IPCIDRValue `tfsdk:"address"` + Gateway nettypes.IPAddrValue `tfsdk:"gateway"` + Address6 nettypes.IPCIDRValue `tfsdk:"address6"` + Gateway6 nettypes.IPAddrValue `tfsdk:"gateway6"` Autostart types.Bool `tfsdk:"autostart"` MTU types.Int64 `tfsdk:"mtu"` Comment types.String `tfsdk:"comment"` @@ -80,10 +83,10 @@ func (m *linuxVLANResourceModel) exportToNetworkInterfaceCreateUpdateBody() *nod } func (m *linuxVLANResourceModel) importFromNetworkInterfaceList(iface *nodes.NetworkInterfaceListResponseData) { - m.Address = pvetypes.NewIPCIDRPointerValue(iface.CIDR) - m.Gateway = pvetypes.NewIPAddrPointerValue(iface.Gateway) - m.Address6 = pvetypes.NewIPCIDRPointerValue(iface.CIDR6) - m.Gateway6 = pvetypes.NewIPAddrPointerValue(iface.Gateway6) + m.Address = nettypes.NewIPCIDRPointerValue(iface.CIDR) + m.Gateway = nettypes.NewIPAddrPointerValue(iface.Gateway) + m.Address6 = nettypes.NewIPCIDRPointerValue(iface.CIDR6) + m.Gateway6 = nettypes.NewIPAddrPointerValue(iface.Gateway6) m.Autostart = types.BoolPointerValue(iface.Autostart.PointerBool()) if iface.MTU != nil { @@ -166,22 +169,22 @@ func (r *linuxVLANResource) Schema( }, "address": schema.StringAttribute{ Description: "The interface IPv4/CIDR address.", - CustomType: pvetypes.IPCIDRType{}, + CustomType: nettypes.IPCIDRType{}, Optional: true, }, "gateway": schema.StringAttribute{ Description: "Default gateway address.", - CustomType: pvetypes.IPAddrType{}, + CustomType: nettypes.IPAddrType{}, Optional: true, }, "address6": schema.StringAttribute{ Description: "The interface IPv6/CIDR address.", - CustomType: pvetypes.IPCIDRType{}, + CustomType: nettypes.IPCIDRType{}, Optional: true, }, "gateway6": schema.StringAttribute{ Description: "Default IPv6 gateway address.", - CustomType: pvetypes.IPAddrType{}, + CustomType: nettypes.IPAddrType{}, Optional: true, }, "autostart": schema.BoolAttribute{ diff --git a/internal/types/ip_addr.go b/internal/network/types/ip_addr.go similarity index 100% rename from internal/types/ip_addr.go rename to internal/network/types/ip_addr.go diff --git a/internal/types/ip_addr_test.go b/internal/network/types/ip_addr_test.go similarity index 100% rename from internal/types/ip_addr_test.go rename to internal/network/types/ip_addr_test.go diff --git a/internal/types/ip_addr_value.go b/internal/network/types/ip_addr_value.go similarity index 100% rename from internal/types/ip_addr_value.go rename to internal/network/types/ip_addr_value.go diff --git a/internal/types/ip_cidr.go b/internal/network/types/ip_cidr.go similarity index 100% rename from internal/types/ip_cidr.go rename to internal/network/types/ip_cidr.go diff --git a/internal/types/ip_cidr_test.go b/internal/network/types/ip_cidr_test.go similarity index 100% rename from internal/types/ip_cidr_test.go rename to internal/network/types/ip_cidr_test.go diff --git a/internal/types/ip_cidr_value.go b/internal/network/types/ip_cidr_value.go similarity index 100% rename from internal/types/ip_cidr_value.go rename to internal/network/types/ip_cidr_value.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1aba6dae..7a01c0c3 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -23,11 +23,12 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/bpg/proxmox-api" + "github.com/bpg/proxmox-api/nodes" + "github.com/bpg/proxmox-api/rest" + "github.com/bpg/proxmox-api/ssh" + "github.com/bpg/terraform-provider-proxmox/internal/network" - "github.com/bpg/terraform-provider-proxmox/proxmox" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" - "github.com/bpg/terraform-provider-proxmox/proxmox/ssh" "github.com/bpg/terraform-provider-proxmox/utils" ) @@ -255,7 +256,7 @@ func (p *proxmoxProvider) Configure( // Create the Proxmox VE API client - creds, err := api.NewCredentials(username, password, "", apiToken) + creds, err := rest.NewCredentials(username, password, "", apiToken) if err != nil { resp.Diagnostics.AddError( "Unable to create Proxmox VE API credentials", @@ -263,7 +264,7 @@ func (p *proxmoxProvider) Configure( ) } - conn, err := api.NewConnection( + conn, err := rest.NewConnection( endpoint, insecure, ) @@ -278,7 +279,7 @@ func (p *proxmoxProvider) Configure( return } - apiClient, err := api.NewClient(creds, conn) + restClient, err := rest.NewClient(creds, conn) if err != nil { resp.Diagnostics.AddError( "Unable to create Proxmox VE API client", @@ -325,7 +326,7 @@ func (p *proxmoxProvider) Configure( sshClient, err := ssh.NewClient( sshUsername, sshPassword, sshAgent, sshAgentSocket, &apiResolverWithOverrides{ - ar: apiResolver{c: apiClient}, + ar: apiResolver{c: restClient}, overrides: nodeOverrides, }, ) @@ -340,7 +341,7 @@ func (p *proxmoxProvider) Configure( return } - client := proxmox.NewClient(apiClient, sshClient) + client := proxmox.NewClient(restClient, sshClient) resp.ResourceData = client resp.DataSourceData = client @@ -358,7 +359,7 @@ func (p *proxmoxProvider) DataSources(_ context.Context) []func() datasource.Dat } type apiResolver struct { - c api.Client + c rest.Client } func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (string, error) { diff --git a/internal/types/common_types.go b/internal/types/common_types.go deleted file mode 100644 index 0ec037cb..00000000 --- a/internal/types/common_types.go +++ /dev/null @@ -1,185 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package types - -import ( - "bytes" - "encoding/json" - "strconv" - "strings" - "time" -) - -// CustomBool allows a JSON boolean value to also be an integer. -type CustomBool bool - -// CustomCommaSeparatedList allows a JSON string to also be a string array. -type CustomCommaSeparatedList []string - -// CustomInt allows a JSON integer value to also be a string. -type CustomInt int - -// CustomLineBreakSeparatedList allows a multiline JSON string to also be a string array. -type CustomLineBreakSeparatedList []string - -// CustomPrivileges allows a JSON object of privileges to also be a string array. -type CustomPrivileges []string - -// CustomTimestamp allows a JSON boolean value to also be a unix timestamp. -type CustomTimestamp time.Time - -// MarshalJSON converts a boolean to a JSON value. -func (r CustomBool) MarshalJSON() ([]byte, error) { - buffer := new(bytes.Buffer) - - if r { - buffer.WriteString("1") - } else { - buffer.WriteString("0") - } - - return buffer.Bytes(), nil -} - -// UnmarshalJSON converts a JSON value to a boolean. -func (r *CustomBool) UnmarshalJSON(b []byte) error { - s := string(b) - *r = s == "1" || s == "true" - - return nil -} - -// Pointer returns a pointers. -func (r CustomBool) Pointer() *CustomBool { - return &r -} - -// PointerBool returns a pointer to a boolean. -func (r *CustomBool) PointerBool() *bool { - return (*bool)(r) -} - -// MarshalJSON converts a boolean to a JSON value. -func (r *CustomCommaSeparatedList) MarshalJSON() ([]byte, error) { - s := strings.Join(*r, ",") - - return json.Marshal(s) -} - -// UnmarshalJSON converts a JSON value to a boolean. -func (r *CustomCommaSeparatedList) UnmarshalJSON(b []byte) error { - var s string - - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - - *r = strings.Split(s, ",") - - return nil -} - -// UnmarshalJSON converts a JSON value to an integer. -func (r *CustomInt) UnmarshalJSON(b []byte) error { - s := string(b) - - if strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") { - s = s[1 : len(s)-1] - } - - i, err := strconv.ParseInt(s, 10, 32) - if err != nil { - return err - } - - *r = CustomInt(i) - - return nil -} - -// MarshalJSON converts a boolean to a JSON value. -func (r *CustomLineBreakSeparatedList) MarshalJSON() ([]byte, error) { - s := strings.Join(*r, "\n") - - return json.Marshal(s) -} - -// UnmarshalJSON converts a JSON value to a boolean. -func (r *CustomLineBreakSeparatedList) UnmarshalJSON(b []byte) error { - var s string - - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - - *r = strings.Split(s, "\n") - - return nil -} - -// MarshalJSON converts a boolean to a JSON value. -func (r *CustomPrivileges) MarshalJSON() ([]byte, error) { - privileges := map[string]CustomBool{} - - for _, v := range *r { - privileges[v] = true - } - - return json.Marshal(privileges) -} - -// UnmarshalJSON converts a JSON value to a boolean. -func (r *CustomPrivileges) UnmarshalJSON(b []byte) error { - var privileges interface{} - - err := json.Unmarshal(b, &privileges) - if err != nil { - return err - } - - switch s := privileges.(type) { - case string: - if s != "" { - *r = strings.Split(s, ",") - } else { - *r = CustomPrivileges{} - } - default: - *r = CustomPrivileges{} - - for k, v := range privileges.(map[string]interface{}) { - if v.(float64) >= 1 { - *r = append(*r, k) - } - } - } - - return nil -} - -// MarshalJSON converts a timestamp to a JSON value. -func (r CustomTimestamp) MarshalJSON() ([]byte, error) { - timestamp := time.Time(r) - buffer := bytes.NewBufferString(strconv.FormatInt(timestamp.Unix(), 10)) - - return buffer.Bytes(), nil -} - -// UnmarshalJSON converts a JSON value to a timestamp. -func (r *CustomTimestamp) UnmarshalJSON(b []byte) error { - s := string(b) - i, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return err - } - - *r = CustomTimestamp(time.Unix(i, 0).UTC()) - - return nil -} diff --git a/internal/types/disk_size.go b/internal/types/disk_size.go deleted file mode 100644 index d65eb3e3..00000000 --- a/internal/types/disk_size.go +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package types - -import ( - "encoding/json" - "fmt" - "math" - "regexp" - "strconv" - "strings" -) - -// Regex used to identify size strings. Case-insensitive. Covers megabytes, gigabytes and terabytes. -var sizeRegex = regexp.MustCompile(`(?i)^(\d+(\.\d+)?)(k|kb|kib|m|mb|mib|g|gb|gib|t|tb|tib)?$`) - -// DiskSize allows a JSON integer value to also be a string. This is mapped to `` data type in Proxmox API. -// Represents a disk size in bytes. -type DiskSize int64 - -// String returns the string representation of the disk size. -func (r DiskSize) String() string { - return FormatDiskSize(r) -} - -// InMegabytes returns the disk size in megabytes. -func (r DiskSize) InMegabytes() int { - return int(int64(r) / 1024 / 1024) -} - -// InGigabytes returns the disk size in gigabytes. -func (r DiskSize) InGigabytes() int { - return int(int64(r) / 1024 / 1024 / 1024) -} - -// DiskSizeFromGigabytes creates a DiskSize from gigabytes. -func DiskSizeFromGigabytes(size int) DiskSize { - return DiskSize(size * 1024 * 1024 * 1024) -} - -// MarshalJSON marshals a disk size into a Proxmox API `` string. -func (r DiskSize) MarshalJSON() ([]byte, error) { - bytes, err := json.Marshal(FormatDiskSize(r)) - if err != nil { - return nil, fmt.Errorf("cannot marshal disk size: %w", err) - } - - return bytes, nil -} - -// UnmarshalJSON unmarshals a disk size from a Proxmox API `` string. -func (r *DiskSize) UnmarshalJSON(b []byte) error { - s := string(b) - - size, err := ParseDiskSize(s) - if err != nil { - return err - } - - *r = size - - return nil -} - -// ParseDiskSize parses a disk size string into a number of bytes. -func ParseDiskSize(size string) (DiskSize, error) { - matches := sizeRegex.FindStringSubmatch(size) - if len(matches) > 0 { - fsize, err := strconv.ParseFloat(matches[1], 64) - if err != nil { - return -1, fmt.Errorf("cannot parse disk size \"%s\": %w", size, err) - } - - switch strings.ToLower(matches[3]) { - case "k", "kb", "kib": - fsize *= 1024 - case "m", "mb", "mib": - fsize = fsize * 1024 * 1024 - case "g", "gb", "gib": - fsize = fsize * 1024 * 1024 * 1024 - case "t", "tb", "tib": - fsize = fsize * 1024 * 1024 * 1024 * 1024 - } - - return DiskSize(math.Ceil(fsize)), nil - } - - return -1, fmt.Errorf("cannot parse disk size \"%s\"", size) -} - -// FormatDiskSize turns a number of bytes into a disk size string. -func FormatDiskSize(size DiskSize) string { - if size < 0 { - return "" - } - - if size < 1024 { - return fmt.Sprintf("%d", size) - } - - round := func(f float64) string { - return strconv.FormatFloat(math.Ceil(f*100)/100, 'f', -1, 64) - } - - if size < 1024*1024 { - return round(float64(size)/1024) + "K" - } - - if size < 1024*1024*1024 { - return round(float64(size)/1024/1024) + "M" - } - - if size < 1024*1024*1024*1024 { - return round(float64(size)/1024/1024/1024) + "G" - } - - return round(float64(size)/1024/1024/1024/1024) + "T" -} diff --git a/internal/types/disk_size_test.go b/internal/types/disk_size_test.go deleted file mode 100644 index 243417ff..00000000 --- a/internal/types/disk_size_test.go +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package types - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestParseDiskSize(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - size string - want int64 - wantErr bool - }{ - {"parse TB", "2TB", 2199023255552, false}, - {"parse T", "2T", 2199023255552, false}, - {"parse fraction T", "2.2T", 2418925581108, false}, - {"parse GB", "2GB", 2147483648, false}, - {"parse G", "2G", 2147483648, false}, - {"parse M", "2048M", 2147483648, false}, - {"parse MB", "2048MB", 2147483648, false}, - {"parse MiB", "2048MiB", 2147483648, false}, - {"parse K", "1K", 1024, false}, - {"parse KB", "2KB", 2048, false}, - {"parse KiB", "4KiB", 4096, false}, - {"parse no units as bytes", "12345", 12345, false}, - {"error on bad format string", "20l8G", -1, true}, - {"error on unknown unit string", "2048W", -1, true}, - {"error on arbitrary string", "something", -1, true}, - } - for _, test := range tests { - tt := test - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - got, err := ParseDiskSize(tt.size) - if (err != nil) != tt.wantErr { - t.Errorf("parseDiskSize() error = %v, wantErr %v", err, tt.wantErr) - return - } - if int64(got) != tt.want { - t.Errorf("parseDiskSize() got = %v, want %v", got, tt.want) - } - }) - } -} - -func TestFormatDiskSize(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - size int64 - want string - }{ - {"handle 0 size", 0, "0"}, - {"handle bytes", 1001, "1001"}, - {"handle kilobytes", 1234, "1.21K"}, - {"handle megabytes", 2097152, "2M"}, - {"handle gigabytes", 2147483648, "2G"}, - {"handle terabytes", 2199023255552, "2T"}, - } - for _, test := range tests { - tt := test - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - if got := FormatDiskSize(DiskSize(tt.size)); got != tt.want { - t.Errorf("formatDiskSize() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestToFromGigabytes(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - size int - want string - }{ - {"handle 0 size", 0, "0"}, - {"handle 99 GB", 99, "99G"}, - {"handle 100 GB", 100, "100G"}, - {"handle 101 GB", 101, "101G"}, - {"handle 1023 GB", 1023, "1023G"}, - {"handle 1024 GB", 1024, "1T"}, - {"handle 1025 GB", 1025, "1.01T"}, - } - for _, test := range tests { - tt := test - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - ds := DiskSizeFromGigabytes(tt.size) - gb := ds.InGigabytes() - assert.Equal(t, tt.size, gb) - if got := ds.String(); got != tt.want { - t.Errorf("DiskSize.String() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/types/helpers.go b/internal/types/helpers.go deleted file mode 100644 index 68349517..00000000 --- a/internal/types/helpers.go +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package types - -// StrPtr returns a pointer to a string. -func StrPtr(s string) *string { - return &s -} - -// BoolPtr returns a pointer to a bool. -func BoolPtr(s bool) *CustomBool { - customBool := CustomBool(s) - return &customBool -} diff --git a/proxmox/access/acl.go b/proxmox/access/acl.go deleted file mode 100644 index 24df34f0..00000000 --- a/proxmox/access/acl.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -import ( - "context" - "fmt" - "net/http" - "sort" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -func (c *Client) aclPath() string { - return c.ExpandPath("acl") -} - -// GetACL retrieves the access control list. -func (c *Client) GetACL(ctx context.Context) ([]*ACLGetResponseData, error) { - resBody := &ACLGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.aclPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get access control list: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].Path < resBody.Data[j].Path - }) - - return resBody.Data, nil -} - -// UpdateACL updates the access control list. -func (c *Client) UpdateACL(ctx context.Context, d *ACLUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.aclPath(), d, nil) - if err != nil { - return fmt.Errorf("failed to update access control list: %w", err) - } - - return nil -} diff --git a/proxmox/access/acl_types.go b/proxmox/access/acl_types.go deleted file mode 100644 index 851c8606..00000000 --- a/proxmox/access/acl_types.go +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// ACLGetResponseBody contains the body from an access control list response. -type ACLGetResponseBody struct { - Data []*ACLGetResponseData `json:"data,omitempty"` -} - -// ACLGetResponseData contains the data from an access control list response. -type ACLGetResponseData struct { - Path string `json:"path"` - Propagate *types.CustomBool `json:"propagate,omitempty"` - RoleID string `json:"roleid"` - Type string `json:"type"` - UserOrGroupID string `json:"ugid"` -} - -// ACLUpdateRequestBody contains the data for an access control list update request. -type ACLUpdateRequestBody struct { - Delete *types.CustomBool `json:"delete,omitempty" url:"delete,omitempty,int"` - Groups []string `json:"groups,omitempty" url:"groups,omitempty,comma"` - Path string `json:"path" url:"path"` - Propagate *types.CustomBool `json:"propagate,omitempty" url:"propagate,omitempty,int"` - Roles []string `json:"roles" url:"roles,comma"` - Users []string `json:"users,omitempty" url:"users,omitempty,comma"` -} diff --git a/proxmox/access/client.go b/proxmox/access/client.go deleted file mode 100644 index e625e1f5..00000000 --- a/proxmox/access/client.go +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -import ( - "fmt" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Client is an interface for performing requests against the Proxmox 'access' API. -type Client struct { - api.Client -} - -// ExpandPath expands a path relative to the client's base path. -func (c *Client) ExpandPath(path string) string { - return fmt.Sprintf("access/%s", path) -} diff --git a/proxmox/access/groups.go b/proxmox/access/groups.go deleted file mode 100644 index 7b91b837..00000000 --- a/proxmox/access/groups.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -import ( - "context" - "fmt" - "net/http" - "net/url" - "sort" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -func (c *Client) groupsPath() string { - return c.ExpandPath("groups") -} - -func (c *Client) groupPath(id string) string { - return fmt.Sprintf("%s/%s", c.groupsPath(), url.PathEscape(id)) -} - -// CreateGroup creates an access group. -func (c *Client) CreateGroup(ctx context.Context, d *GroupCreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.groupsPath(), d, nil) - if err != nil { - return fmt.Errorf("failed to create access group: %w", err) - } - - return nil -} - -// DeleteGroup deletes an access group. -func (c *Client) DeleteGroup(ctx context.Context, id string) error { - err := c.DoRequest(ctx, http.MethodDelete, c.groupPath(id), nil, nil) - if err != nil { - return fmt.Errorf("failed to delete access group: %w", err) - } - - return nil -} - -// GetGroup retrieves an access group. -func (c *Client) GetGroup(ctx context.Context, id string) (*GroupGetResponseData, error) { - resBody := &GroupGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.groupPath(id), nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get access group: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Strings(resBody.Data.Members) - - return resBody.Data, nil -} - -// ListGroups retrieves a list of access groups. -func (c *Client) ListGroups(ctx context.Context) ([]*GroupListResponseData, error) { - resBody := &GroupListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.groupsPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to list access groups: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].ID < resBody.Data[j].ID - }) - - return resBody.Data, nil -} - -// UpdateGroup updates an access group. -func (c *Client) UpdateGroup(ctx context.Context, id string, d *GroupUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.groupPath(id), d, nil) - if err != nil { - return fmt.Errorf("failed to update access group: %w", err) - } - - return nil -} diff --git a/proxmox/access/groups_types.go b/proxmox/access/groups_types.go deleted file mode 100644 index 98d5e87d..00000000 --- a/proxmox/access/groups_types.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -// GroupCreateRequestBody contains the data for an access group create request. -type GroupCreateRequestBody struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - ID string `json:"groupid" url:"groupid"` -} - -// GroupGetResponseBody contains the body from an access group get response. -type GroupGetResponseBody struct { - Data *GroupGetResponseData `json:"data,omitempty"` -} - -// GroupGetResponseData contains the data from an access group get response. -type GroupGetResponseData struct { - Comment *string `json:"comment,omitempty"` - Members []string `json:"members"` -} - -// GroupListResponseBody contains the body from an access group list response. -type GroupListResponseBody struct { - Data []*GroupListResponseData `json:"data,omitempty"` -} - -// GroupListResponseData contains the data from an access group list response. -type GroupListResponseData struct { - Comment *string `json:"comment,omitempty"` - ID string `json:"groupid"` -} - -// GroupUpdateRequestBody contains the data for an access group update request. -type GroupUpdateRequestBody struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` -} diff --git a/proxmox/access/roles.go b/proxmox/access/roles.go deleted file mode 100644 index 009ded95..00000000 --- a/proxmox/access/roles.go +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -import ( - "context" - "fmt" - "net/http" - "net/url" - "sort" - - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -func (c *Client) rolesPath() string { - return c.ExpandPath("roles") -} - -func (c *Client) rolePath(id string) string { - return fmt.Sprintf("%s/%s", c.rolesPath(), url.PathEscape(id)) -} - -// CreateRole creates an access role. -func (c *Client) CreateRole(ctx context.Context, d *RoleCreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.rolesPath(), d, nil) - if err != nil { - return fmt.Errorf("error creating role: %w", err) - } - - return nil -} - -// DeleteRole deletes an access role. -func (c *Client) DeleteRole(ctx context.Context, id string) error { - err := c.DoRequest(ctx, http.MethodDelete, c.rolePath(id), nil, nil) - if err != nil { - return fmt.Errorf("error deleting role: %w", err) - } - - return nil -} - -// GetRole retrieves an access role. -func (c *Client) GetRole(ctx context.Context, id string) (*types.CustomPrivileges, error) { - resBody := &RoleGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.rolePath(id), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error getting role: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Strings(*resBody.Data) - - return resBody.Data, nil -} - -// ListRoles retrieves a list of access roles. -func (c *Client) ListRoles(ctx context.Context) ([]*RoleListResponseData, error) { - resBody := &RoleListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.rolesPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error listing roles: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].ID < resBody.Data[j].ID - }) - - for i := range resBody.Data { - if resBody.Data[i].Privileges != nil { - sort.Strings(*resBody.Data[i].Privileges) - } - } - - return resBody.Data, nil -} - -// UpdateRole updates an access role. -func (c *Client) UpdateRole(ctx context.Context, id string, d *RoleUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.rolePath(id), d, nil) - if err != nil { - return fmt.Errorf("error updating role: %w", err) - } - - return nil -} diff --git a/proxmox/access/roles_types.go b/proxmox/access/roles_types.go deleted file mode 100644 index e9096b92..00000000 --- a/proxmox/access/roles_types.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// RoleCreateRequestBody contains the data for an access group create request. -type RoleCreateRequestBody struct { - ID string `json:"roleid" url:"roleid"` - Privileges types.CustomPrivileges `json:"privs" url:"privs,comma"` -} - -// RoleGetResponseBody contains the body from an access group get response. -type RoleGetResponseBody struct { - Data *types.CustomPrivileges `json:"data,omitempty"` -} - -// RoleListResponseBody contains the body from an access group list response. -type RoleListResponseBody struct { - Data []*RoleListResponseData `json:"data,omitempty"` -} - -// RoleListResponseData contains the data from an access group list response. -type RoleListResponseData struct { - ID string `json:"roleid"` - Privileges *types.CustomPrivileges `json:"privs,omitempty"` - Special *types.CustomBool `json:"special,omitempty"` -} - -// RoleUpdateRequestBody contains the data for an access group update request. -type RoleUpdateRequestBody struct { - Privileges types.CustomPrivileges `json:"privs" url:"privs,comma"` -} diff --git a/proxmox/access/users.go b/proxmox/access/users.go deleted file mode 100644 index a1f3dfec..00000000 --- a/proxmox/access/users.go +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -import ( - "context" - "fmt" - "net/http" - "net/url" - "sort" - "time" - - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -func (c *Client) usersPath() string { - return c.ExpandPath("users") -} - -func (c *Client) userPath(id string) string { - return fmt.Sprintf("%s/%s", c.usersPath(), url.PathEscape(id)) -} - -// ChangeUserPassword changes a user's password. -func (c *Client) ChangeUserPassword(ctx context.Context, id, password string) error { - d := UserChangePasswordRequestBody{ - ID: id, - Password: password, - } - - err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("password"), d, nil) - if err != nil { - return fmt.Errorf("error changing user password: %w", err) - } - - return nil -} - -// CreateUser creates a user. -func (c *Client) CreateUser(ctx context.Context, d *UserCreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.usersPath(), d, nil) - if err != nil { - return fmt.Errorf("error creating user: %w", err) - } - - return nil -} - -// DeleteUser deletes an user. -func (c *Client) DeleteUser(ctx context.Context, id string) error { - err := c.DoRequest(ctx, http.MethodDelete, c.userPath(id), nil, nil) - if err != nil { - return fmt.Errorf("error deleting user: %w", err) - } - - return nil -} - -// GetUser retrieves a user. -func (c *Client) GetUser(ctx context.Context, id string) (*UserGetResponseData, error) { - resBody := &UserGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.userPath(id), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving user: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - if resBody.Data.ExpirationDate != nil { - expirationDate := types.CustomTimestamp(time.Time(*resBody.Data.ExpirationDate).UTC()) - resBody.Data.ExpirationDate = &expirationDate - } - - if resBody.Data.Groups != nil { - sort.Strings(*resBody.Data.Groups) - } - - return resBody.Data, nil -} - -// ListUsers retrieves a list of users. -func (c *Client) ListUsers(ctx context.Context) ([]*UserListResponseData, error) { - resBody := &UserListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.usersPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error listing users: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].ID < resBody.Data[j].ID - }) - - for i := range resBody.Data { - if resBody.Data[i].ExpirationDate != nil { - expirationDate := types.CustomTimestamp(time.Time(*resBody.Data[i].ExpirationDate).UTC()) - resBody.Data[i].ExpirationDate = &expirationDate - } - - if resBody.Data[i].Groups != nil { - sort.Strings(*resBody.Data[i].Groups) - } - } - - return resBody.Data, nil -} - -// UpdateUser updates a user. -func (c *Client) UpdateUser(ctx context.Context, id string, d *UserUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.userPath(id), d, nil) - if err != nil { - return fmt.Errorf("error updating user: %w", err) - } - - return nil -} diff --git a/proxmox/access/users_types.go b/proxmox/access/users_types.go deleted file mode 100644 index 90ac5083..00000000 --- a/proxmox/access/users_types.go +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package access - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// UserChangePasswordRequestBody contains the data for a user password change request. -type UserChangePasswordRequestBody struct { - ID string `json:"userid" url:"userid"` - Password string `json:"password" url:"password"` -} - -// UserCreateRequestBody contains the data for a user create request. -type UserCreateRequestBody struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Email *string `json:"email,omitempty" url:"email,omitempty"` - Enabled *types.CustomBool `json:"enable,omitempty" url:"enable,omitempty,int"` - ExpirationDate *types.CustomTimestamp `json:"expire,omitempty" url:"expire,omitempty,unix"` - FirstName *string `json:"firstname,omitempty" url:"firstname,omitempty"` - Groups []string `json:"groups,omitempty" url:"groups,omitempty,comma"` - ID string `json:"userid" url:"userid"` - Keys *string `json:"keys,omitempty" url:"keys,omitempty"` - LastName *string `json:"lastname,omitempty" url:"lastname,omitempty"` - Password string `json:"password" url:"password"` -} - -// UserGetResponseBody contains the body from a user get response. -type UserGetResponseBody struct { - Data *UserGetResponseData `json:"data,omitempty"` -} - -// UserGetResponseData contains the data from an user get response. -type UserGetResponseData struct { - Comment *string `json:"comment,omitempty"` - Email *string `json:"email,omitempty"` - Enabled *types.CustomBool `json:"enable,omitempty"` - ExpirationDate *types.CustomTimestamp `json:"expire,omitempty"` - FirstName *string `json:"firstname,omitempty"` - Groups *[]string `json:"groups,omitempty"` - Keys *string `json:"keys,omitempty"` - LastName *string `json:"lastname,omitempty"` -} - -// UserListResponseBody contains the body from a user list response. -type UserListResponseBody struct { - Data []*UserListResponseData `json:"data,omitempty"` -} - -// UserListResponseData contains the data from an user list response. -type UserListResponseData struct { - Comment *string `json:"comment,omitempty"` - Email *string `json:"email,omitempty"` - Enabled *types.CustomBool `json:"enable,omitempty"` - ExpirationDate *types.CustomTimestamp `json:"expire,omitempty"` - FirstName *string `json:"firstname,omitempty"` - Groups *[]string `json:"groups,omitempty"` - ID string `json:"userid"` - Keys *string `json:"keys,omitempty"` - LastName *string `json:"lastname,omitempty"` -} - -// UserUpdateRequestBody contains the data for an user update request. -type UserUpdateRequestBody struct { - Append *types.CustomBool `json:"append,omitempty" url:"append,omitempty"` - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Email *string `json:"email,omitempty" url:"email,omitempty"` - Enabled *types.CustomBool `json:"enable,omitempty" url:"enable,omitempty,int"` - ExpirationDate *types.CustomTimestamp `json:"expire,omitempty" url:"expire,omitempty,unix"` - FirstName *string `json:"firstname,omitempty" url:"firstname,omitempty"` - Groups []string `json:"groups,omitempty" url:"groups,omitempty,comma"` - Keys *string `json:"keys,omitempty" url:"keys,omitempty"` - LastName *string `json:"lastname,omitempty" url:"lastname,omitempty"` -} diff --git a/proxmox/api/authenticator.go b/proxmox/api/authenticator.go deleted file mode 100644 index 3586bc3a..00000000 --- a/proxmox/api/authenticator.go +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package api - -import ( - "context" - "net/http" -) - -// Authenticator is an interface for adding authentication data to a request. -// The authenticator is also aware of the authentication context, e.g. if it -// is configured to use the root user. -type Authenticator interface { - // IsRoot returns true if the authenticator is configured to use the root - IsRoot() bool - - // IsRootTicket returns true if the authenticator is configured to use the root directly using a login ticket. - // (root using token is weaker, cannot change VM arch) - IsRootTicket() bool - - // AuthenticateRequest adds authentication data to a new request. - AuthenticateRequest(ctx context.Context, req *http.Request) error -} diff --git a/proxmox/api/client.go b/proxmox/api/client.go deleted file mode 100644 index 8b268c4f..00000000 --- a/proxmox/api/client.go +++ /dev/null @@ -1,318 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package api - -import ( - "bytes" - "context" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "time" - - "github.com/google/go-querystring/query" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging" - - "github.com/bpg/terraform-provider-proxmox/utils" -) - -// ErrNoDataObjectInResponse is returned when the server does not include a data object in the response. -var ErrNoDataObjectInResponse = errors.New("the server did not include a data object in the response") - -const ( - // the large timeout is to allow for large file uploads. - httpClientTimeout = 5 * time.Minute - basePathJSONAPI = "api2/json" -) - -// Client is an interface for performing requests against the Proxmox API. -type Client interface { - // DoRequest performs a request against the Proxmox API. - DoRequest( - ctx context.Context, - method, path string, - requestBody, responseBody interface{}, - ) error - - // ExpandPath expands a path relative to the client's base path. - // For example, if the client is configured for a VM and the - // path is "firewall/options", the returned path will be - // "/nodes//qemu//firewall/options". - ExpandPath(path string) string - - // IsRoot returns true if the client is configured with the root user. - IsRoot() bool - - // IsRootTicket returns true if the authenticator is configured to use the root directly using a login ticket. - // (root using token is weaker, cannot change VM arch) - IsRootTicket() bool -} - -// Connection represents a connection to the Proxmox Virtual Environment API. -type Connection struct { - endpoint string - httpClient *http.Client -} - -// NewConnection creates and initializes a Connection instance. -func NewConnection(endpoint string, insecure bool) (*Connection, error) { - u, err := url.ParseRequestURI(endpoint) - if err != nil { - return nil, errors.New( - "you must specify a valid endpoint for the Proxmox Virtual Environment API (valid: https://host:port/)", - ) - } - - if u.Scheme != "https" { - return nil, errors.New( - "you must specify a secure endpoint for the Proxmox Virtual Environment API (valid: https://host:port/)", - ) - } - - var transport http.RoundTripper = &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: insecure, //nolint:gosec - }, - } - - if logging.IsDebugOrHigher() { - transport = logging.NewLoggingHTTPTransport(transport) - } - - return &Connection{ - endpoint: strings.TrimRight(u.String(), "/"), - httpClient: &http.Client{ - Transport: transport, - Timeout: httpClientTimeout, - }, - }, nil -} - -// VirtualEnvironmentClient implements an API client for the Proxmox Virtual Environment API. -type client struct { - conn *Connection - auth Authenticator -} - -// NewClient creates and initializes a VirtualEnvironmentClient instance. -func NewClient(creds *Credentials, conn *Connection) (Client, error) { - if creds == nil { - return nil, errors.New("credentials must not be nil") - } - - if conn == nil { - return nil, errors.New("connection must not be nil") - } - - var auth Authenticator - - var err error - - if creds.APIToken != nil { - auth, err = NewTokenAuthenticator(*creds.APIToken) - } else { - auth, err = NewTicketAuthenticator(conn, creds) - } - - if err != nil { - return nil, err - } - - return &client{ - conn: conn, - auth: auth, - }, nil -} - -// DoRequest performs a HTTP request against a JSON API endpoint. -func (c *client) DoRequest( - ctx context.Context, - method, path string, - requestBody, responseBody interface{}, -) error { - var reqBodyReader io.Reader - - var reqContentLength *int64 - - modifiedPath := path - reqBodyType := "" - - //nolint:nestif - if requestBody != nil { - multipartData, multipart := requestBody.(*MultiPartData) - pipedBodyReader, pipedBody := requestBody.(*io.PipeReader) - - switch { - case multipart: - reqBodyReader = multipartData.Reader - reqBodyType = fmt.Sprintf("multipart/form-data; boundary=%s", multipartData.Boundary) - reqContentLength = multipartData.Size - case pipedBody: - reqBodyReader = pipedBodyReader - default: - v, err := query.Values(requestBody) - if err != nil { - return fmt.Errorf("failed to encode HTTP %s request (path: %s) - Reason: %w", - method, - modifiedPath, - err, - ) - } - - encodedValues := v.Encode() - if encodedValues != "" { - if method == http.MethodDelete || method == http.MethodGet || method == http.MethodHead { - if !strings.Contains(modifiedPath, "?") { - modifiedPath = fmt.Sprintf("%s?%s", modifiedPath, encodedValues) - } else { - modifiedPath = fmt.Sprintf("%s&%s", modifiedPath, encodedValues) - } - } else { - reqBodyReader = bytes.NewBufferString(encodedValues) - reqBodyType = "application/x-www-form-urlencoded" - } - } - } - } else { - reqBodyReader = new(bytes.Buffer) - } - - req, err := http.NewRequestWithContext( - ctx, - method, - fmt.Sprintf("%s/%s/%s", c.conn.endpoint, basePathJSONAPI, modifiedPath), - reqBodyReader, - ) - if err != nil { - return fmt.Errorf( - "failed to create HTTP %s request (path: %s) - Reason: %w", - method, - modifiedPath, - err, - ) - } - - req.Header.Add("Accept", "application/json") - - if reqContentLength != nil { - req.ContentLength = *reqContentLength - } - - if reqBodyType != "" { - req.Header.Add("Content-Type", reqBodyType) - } - - err = c.auth.AuthenticateRequest(ctx, req) - if err != nil { - return fmt.Errorf("failed to authenticate HTTP %s request (path: %s) - Reason: %w", - method, - modifiedPath, - err, - ) - } - - //nolint:bodyclose - res, err := c.conn.httpClient.Do(req) - if err != nil { - return fmt.Errorf("failed to perform HTTP %s request (path: %s) - Reason: %w", - method, - modifiedPath, - err, - ) - } - - defer utils.CloseOrLogError(ctx)(res.Body) - - err = validateResponseCode(res) - if err != nil { - return err - } - - //nolint:nestif - if responseBody != nil { - err = json.NewDecoder(res.Body).Decode(responseBody) - if err != nil { - return fmt.Errorf( - "failed to decode HTTP %s response (path: %s) - Reason: %w", - method, - modifiedPath, - err, - ) - } - } else { - data, err := io.ReadAll(res.Body) - if err != nil { - return fmt.Errorf( - "failed to read HTTP %s response body (path: %s) - Reason: %w", - method, - modifiedPath, - err, - ) - } - if len(data) > 0 { - dr := dataResponse{} - - if err2 := json.NewDecoder(bytes.NewReader(data)).Decode(&dr); err2 == nil { - if dr.Data == nil { - return nil - } - } - tflog.Warn(ctx, "unhandled HTTP response body", map[string]interface{}{ - "data": dr.Data, - }) - } - } - - return nil -} - -type dataResponse struct { - Data interface{} `json:"data"` -} - -// ExpandPath expands the given path to an absolute path. -func (c *client) ExpandPath(path string) string { - return path -} - -func (c *client) IsRoot() bool { - return c.auth.IsRoot() -} - -func (c *client) IsRootTicket() bool { - return c.auth.IsRootTicket() -} - -// validateResponseCode ensures that a response is valid. -func validateResponseCode(res *http.Response) error { - if res.StatusCode < 200 || res.StatusCode >= 300 { - status := strings.TrimPrefix(res.Status, fmt.Sprintf("%d ", res.StatusCode)) - - errRes := &ErrorResponseBody{} - err := json.NewDecoder(res.Body).Decode(errRes) - - if err == nil && errRes.Errors != nil { - var errList []string - - for k, v := range *errRes.Errors { - errList = append(errList, fmt.Sprintf("%s: %s", k, strings.TrimRight(v, "\n\r"))) - } - - status = fmt.Sprintf("%s (%s)", status, strings.Join(errList, " - ")) - } - - return fmt.Errorf("received an HTTP %d response - Reason: %s", res.StatusCode, status) - } - - return nil -} diff --git a/proxmox/api/client_types.go b/proxmox/api/client_types.go deleted file mode 100644 index bca396d2..00000000 --- a/proxmox/api/client_types.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package api - -import ( - "io" - "os" -) - -// MultiPartData enables multipart uploads in DoRequest. -type MultiPartData struct { - Boundary string - Reader io.Reader - Size *int64 -} - -// ErrorResponseBody contains the body of an error response. -type ErrorResponseBody struct { - Data *string `json:"data"` - Errors *map[string]string `json:"errors"` -} - -// FileUploadRequest is a request for uploading a file. -type FileUploadRequest struct { - ContentType string - FileName string - File *os.File -} diff --git a/proxmox/api/credentials.go b/proxmox/api/credentials.go deleted file mode 100644 index fab5e822..00000000 --- a/proxmox/api/credentials.go +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package api - -import ( - "errors" - "strings" -) - -const rootUsername = "root@pam" - -// Credentials is a struct that holds the credentials for the Proxmox Virtual -// Environment API. -type Credentials struct { - Username string - Password string - OTP *string - APIToken *string -} - -// NewCredentials creates a new Credentials struct. -func NewCredentials(username, password, otp, apiToken string) (*Credentials, error) { - if apiToken != "" { - return &Credentials{ - APIToken: &apiToken, - }, nil - } - - if password == "" { - return nil, errors.New( - "you must specify a password for the Proxmox Virtual Environment API", - ) - } - - if username == "" { - return nil, errors.New( - "you must specify a username for the Proxmox Virtual Environment API", - ) - } - - if !strings.Contains(username, "@") { - return nil, errors.New( - "make sure the username for the Proxmox Virtual Environment API ends in '@pve or @pam'", - ) - } - - c := &Credentials{ - Username: username, - Password: password, - } - - if otp != "" { - c.OTP = &otp - } - - return c, nil -} diff --git a/proxmox/api/ticket_auth.go b/proxmox/api/ticket_auth.go deleted file mode 100644 index 4d6c692a..00000000 --- a/proxmox/api/ticket_auth.go +++ /dev/null @@ -1,137 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package api - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - "net/url" - - "github.com/hashicorp/terraform-plugin-log/tflog" - - "github.com/bpg/terraform-provider-proxmox/utils" -) - -type ticketAuthenticator struct { - conn *Connection - authRequest string - authData *AuthenticationResponseData -} - -// NewTicketAuthenticator returns a new ticket authenticator. -func NewTicketAuthenticator(conn *Connection, creds *Credentials) (Authenticator, error) { - authRequest := fmt.Sprintf( - "username=%s&password=%s", - url.QueryEscape(creds.Username), - url.QueryEscape(creds.Password), - ) - - // OTP is optional, and probably doesn't make much sense for most provider users. - if creds.OTP != nil { - authRequest = fmt.Sprintf("%s&otp=%s", authRequest, url.QueryEscape(*creds.OTP)) - } - - return &ticketAuthenticator{ - conn: conn, - authRequest: authRequest, - }, nil -} - -func (t *ticketAuthenticator) authenticate(ctx context.Context) (*AuthenticationResponseData, error) { - if t.authData != nil { - return t.authData, nil - } - - req, err := http.NewRequestWithContext( - ctx, - http.MethodPost, - fmt.Sprintf("%s/%s/access/ticket", t.conn.endpoint, basePathJSONAPI), - bytes.NewBufferString(t.authRequest), - ) - if err != nil { - return nil, fmt.Errorf("failed to create authentication request: %w", err) - } - - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - - tflog.Debug(ctx, "Sending authentication request", map[string]interface{}{ - "path": req.URL.Path, - }) - - //nolint:bodyclose - res, err := t.conn.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to retrieve authentication response: %w", err) - } - - defer utils.CloseOrLogError(ctx)(res.Body) - - err = validateResponseCode(res) - if err != nil { - return nil, fmt.Errorf("failed to authenticate: %w", err) - } - - resBody := AuthenticationResponseBody{} - - err = json.NewDecoder(res.Body).Decode(&resBody) - if err != nil { - return nil, fmt.Errorf("failed to decode authentication response, %w", err) - } - - if resBody.Data == nil { - return nil, errors.New("the server did not include a data object in the authentication response") - } - - if resBody.Data.CSRFPreventionToken == nil { - return nil, errors.New( - "the server did not include a CSRF prevention token in the authentication response", - ) - } - - if resBody.Data.Ticket == nil { - return nil, errors.New("the server did not include a ticket in the authentication response") - } - - if resBody.Data.Username == "" { - return nil, errors.New("the server did not include the username in the authentication response") - } - - t.authData = resBody.Data - - return resBody.Data, nil -} - -func (t *ticketAuthenticator) IsRoot() bool { - return t.authData != nil && t.authData.Username == rootUsername -} - -func (t *ticketAuthenticator) IsRootTicket() bool { - return t.IsRoot() -} - -// AuthenticateRequest adds authentication data to a new request. -func (t *ticketAuthenticator) AuthenticateRequest(ctx context.Context, req *http.Request) error { - a, err := t.authenticate(ctx) - if err != nil { - return fmt.Errorf("failed to authenticate: %w", err) - } - - req.AddCookie(&http.Cookie{ - Name: "PVEAuthCookie", - Value: *a.Ticket, - }) - - if req.Method != http.MethodGet { - req.Header.Add("CSRFPreventionToken", *a.CSRFPreventionToken) - } - - return nil -} diff --git a/proxmox/api/ticket_auth_types.go b/proxmox/api/ticket_auth_types.go deleted file mode 100644 index 97fcd38b..00000000 --- a/proxmox/api/ticket_auth_types.go +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package api - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// AuthenticationResponseBody contains the body from an authentication response. -type AuthenticationResponseBody struct { - Data *AuthenticationResponseData `json:"data,omitempty"` -} - -// AuthenticationResponseCapabilities contains the supported capabilities for a session. -type AuthenticationResponseCapabilities struct { - Access *types.CustomPrivileges `json:"access,omitempty"` - Datacenter *types.CustomPrivileges `json:"dc,omitempty"` - Nodes *types.CustomPrivileges `json:"nodes,omitempty"` - Storage *types.CustomPrivileges `json:"storage,omitempty"` - VMs *types.CustomPrivileges `json:"vms,omitempty"` -} - -// AuthenticationResponseData contains the data from an authentication response. -type AuthenticationResponseData struct { - ClusterName *string `json:"clustername,omitempty"` - CSRFPreventionToken *string `json:"CSRFPreventionToken,omitempty"` - Capabilities *AuthenticationResponseCapabilities `json:"cap,omitempty"` - Ticket *string `json:"ticket,omitempty"` - Username string `json:"username"` -} diff --git a/proxmox/api/token_auth.go b/proxmox/api/token_auth.go deleted file mode 100644 index beccbd8b..00000000 --- a/proxmox/api/token_auth.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package api - -import ( - "context" - "net/http" - "strings" -) - -type tokenAuthenticator struct { - username string - token string -} - -// NewTokenAuthenticator creates a new authenticator that uses a PVE API Token -// for authentication. -func NewTokenAuthenticator(token string) (Authenticator, error) { - return &tokenAuthenticator{ - username: strings.Split(token, "!")[0], - token: token, - }, nil -} - -func (t *tokenAuthenticator) IsRoot() bool { - return t.username == rootUsername -} - -func (t *tokenAuthenticator) IsRootTicket() bool { - // Logged using a token, therefore not a ticket login - return false -} - -func (t *tokenAuthenticator) AuthenticateRequest(_ context.Context, req *http.Request) error { - req.Header.Set("Authorization", "PVEAPIToken="+t.token) - return nil -} diff --git a/proxmox/client.go b/proxmox/client.go deleted file mode 100644 index fe437144..00000000 --- a/proxmox/client.go +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package proxmox - -import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/access" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/cluster" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" - "github.com/bpg/terraform-provider-proxmox/proxmox/pools" - "github.com/bpg/terraform-provider-proxmox/proxmox/ssh" - "github.com/bpg/terraform-provider-proxmox/proxmox/storage" - "github.com/bpg/terraform-provider-proxmox/proxmox/version" -) - -// Client defines a client interface for the Proxmox Virtual Environment API. -type Client interface { - // Access returns a client for managing access control. - Access() *access.Client - - // Cluster returns a client for managing the cluster. - Cluster() *cluster.Client - - // Node returns a client for managing resources on a specific node. - Node(nodeName string) *nodes.Client - - // Pool returns a client for managing resource pools. - Pool() *pools.Client - - // Storage returns a client for managing storage. - Storage() *storage.Client - - // Version returns a client for getting the version of the Proxmox Virtual Environment API. - Version() *version.Client - - // API returns a lower-lever REST API client. - API() api.Client - - // SSH returns a lower-lever SSH client. - SSH() ssh.Client -} - -type client struct { - a api.Client - s ssh.Client -} - -// NewClient creates a new API client. -func NewClient(a api.Client, s ssh.Client) Client { - return &client{a: a, s: s} -} - -// Access returns a client for managing access control. -func (c *client) Access() *access.Client { - return &access.Client{Client: c.a} -} - -// Cluster returns a client for managing the cluster. -func (c *client) Cluster() *cluster.Client { - return &cluster.Client{Client: c.a} -} - -// Node returns a client for managing resources on a specific node. -func (c *client) Node(nodeName string) *nodes.Client { - return &nodes.Client{Client: c.a, NodeName: nodeName} -} - -// Pool returns a client for managing resource pools. -func (c *client) Pool() *pools.Client { - return &pools.Client{Client: c.a} -} - -// Storage returns a client for managing storage. -func (c *client) Storage() *storage.Client { - return &storage.Client{Client: c.a} -} - -// Version returns a client for getting the version of the Proxmox Virtual Environment API. -func (c *client) Version() *version.Client { - return &version.Client{Client: c.a} -} - -// API returns a lower-lever REST API client. -func (c *client) API() api.Client { - return c.a -} - -// SSH returns a lower-lever SSH client.s. -func (c *client) SSH() ssh.Client { - return c.s -} diff --git a/proxmox/cluster/client.go b/proxmox/cluster/client.go deleted file mode 100644 index 7b2278ae..00000000 --- a/proxmox/cluster/client.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package cluster - -import ( - "fmt" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - clusterfirewall "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/firewall" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" -) - -// Client is an interface for accessing the Proxmox cluster API. -type Client struct { - api.Client -} - -// ExpandPath expands a relative path to a full cluster API path. -func (c *Client) ExpandPath(path string) string { - return fmt.Sprintf("cluster/%s", path) -} - -// Firewall returns a client for managing the cluster firewall. -func (c *Client) Firewall() clusterfirewall.API { - return &clusterfirewall.Client{ - Client: firewall.Client{Client: c}, - } -} diff --git a/proxmox/cluster/cluster.go b/proxmox/cluster/cluster.go deleted file mode 100644 index 13b69db7..00000000 --- a/proxmox/cluster/cluster.go +++ /dev/null @@ -1,143 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package cluster - -import ( - "context" - "errors" - "fmt" - "net/http" - "sync" - - "github.com/hashicorp/terraform-plugin-log/tflog" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -const ( - getVMIDStep = 1 -) - -// ErrVMDoesNotExist is returned when the VM identifier cannot be found on any cluster node. -var ErrVMDoesNotExist = errors.New("unable to find VM identifier on any cluster node") - -var ( - //nolint:gochecknoglobals - getVMIDCounter = -1 - //nolint:gochecknoglobals - getVMIDCounterMutex = &sync.Mutex{} -) - -// GetNextID retrieves the next free VM identifier for the cluster. -func (c *Client) GetNextID(ctx context.Context, vmID *int) (*int, error) { - reqBody := &NextIDRequestBody{ - VMID: vmID, - } - - resBody := &NextIDResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, "cluster/nextid", reqBody, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving next VM ID: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return (*int)(resBody.Data), nil -} - -// GetVMID retrieves the next available VM identifier. -func (c *Client) GetVMID(ctx context.Context) (*int, error) { - getVMIDCounterMutex.Lock() - defer getVMIDCounterMutex.Unlock() - - if getVMIDCounter < 0 { - nextVMID, err := c.GetNextID(ctx, nil) - if err != nil { - return nil, err - } - - if nextVMID == nil { - return nil, errors.New("unable to retrieve the next available VM identifier") - } - - getVMIDCounter = *nextVMID + getVMIDStep - - tflog.Debug(ctx, "next VM identifier", map[string]interface{}{ - "id": *nextVMID, - }) - - return nextVMID, nil - } - - vmID := getVMIDCounter - - for vmID <= 2147483637 { - _, err := c.GetNextID(ctx, &vmID) - if err != nil { - vmID += getVMIDStep - - continue - } - - getVMIDCounter = vmID + getVMIDStep - - tflog.Debug(ctx, "next VM identifier", map[string]interface{}{ - "id": vmID, - }) - - return &vmID, nil - } - - return nil, errors.New("unable to determine the next available VM identifier") -} - -// GetClusterResources retrieves current resources for cluster. -func (c *Client) GetClusterResources(ctx context.Context, resourceType string) ([]*ResourcesListResponseData, error) { - reqBody := &ResourcesListRequestBody{ - Type: resourceType, - } - resBody := &ResourcesListBody{} - - err := c.DoRequest(ctx, http.MethodGet, "cluster/resources", reqBody, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get resources list of type (\"%s\") for cluster: %w", resourceType, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// GetClusterResourcesVM retrieves current VM resources for cluster. -func (c *Client) GetClusterResourcesVM(ctx context.Context) ([]*ResourcesListResponseData, error) { - return c.GetClusterResources(ctx, "vm") -} - -// GetVMNodeName gets node for specified vmID. -func (c *Client) GetVMNodeName(ctx context.Context, vmID int) (*string, error) { - allClusterVM, err := c.GetClusterResourcesVM(ctx) - if err != nil { - return nil, err - } - - if allClusterVM == nil { - return nil, api.ErrNoDataObjectInResponse - } - - for _, v := range allClusterVM { - if v.VMID == vmID { - return &v.NodeName, nil - } - } - - return nil, ErrVMDoesNotExist -} diff --git a/proxmox/cluster/cluster_types.go b/proxmox/cluster/cluster_types.go deleted file mode 100644 index 23e49d7d..00000000 --- a/proxmox/cluster/cluster_types.go +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package cluster - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// NextIDRequestBody contains the data for a cluster next id request. -type NextIDRequestBody struct { - VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"` -} - -// NextIDResponseBody contains the body from a cluster next id response. -type NextIDResponseBody struct { - Data *types.CustomInt `json:"data,omitempty"` -} - -// ResourcesListBody contains the body from a cluste resource list response. -type ResourcesListBody struct { - Data []*ResourcesListResponseData `json:"data,omitempty"` -} - -// ResourcesListRequestBody contains the body params to cluster resource list request. -type ResourcesListRequestBody struct { - Type string `json:"type" url:"type"` -} - -// ResourcesListResponseData contains the data from a cluster resource list body response. -type ResourcesListResponseData struct { - Type string `json:"type"` - ID string `json:"id"` - CgroupMode int `json:"cgroup-mode,omitempty"` - Content int `json:"content,omitempty"` - CPU float64 `json:"cpu,omitempty"` - Disk int `json:"disk,omitempty"` - HaState string `json:"hastate,omitempty"` - Level string `json:"level,omitempty"` - MaxCPU float64 `json:"maxcpu,omitempty"` - MaxDisk int `json:"maxdisk,omitempty"` - MaxMem int `json:"maxmem,omitempty"` - Mem int `json:"mem,omitempty"` - Name string `json:"name,omitempty"` - NodeName string `json:"node,omitempty"` - PluginType string `json:"plugintype,omitempty"` - PoolName string `json:"poolname,omitempty"` - Status string `json:"status,omitempty"` - Storage string `json:"storage,omitempty"` - Uptime int `json:"uptime,omitempty"` - VMID int `json:"vmid,omitempty"` -} diff --git a/proxmox/cluster/firewall/client.go b/proxmox/cluster/firewall/client.go deleted file mode 100644 index a5b67f2b..00000000 --- a/proxmox/cluster/firewall/client.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "fmt" - - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" -) - -// API is an interface for managing cluster firewall. -type API interface { - firewall.API - SecurityGroup - Options - SecurityGroup(group string) firewall.Rule -} - -// Client is an interface for accessing the Proxmox cluster firewall API. -type Client struct { - firewall.Client -} - -type groupClient struct { - firewall.Client - Group string -} - -// SecurityGroup returns a client for managing a specific security group. -func (c *Client) SecurityGroup(group string) firewall.Rule { - // My head really hurts when I'm looking at this code - // I'm not sure if this is the best way to do the required - // interface composition and method "overrides", but it works. - return &Client{ - Client: firewall.Client{ - Client: &groupClient{ - Client: c.Client, - Group: group, - }, - }, - } -} - -func (c *groupClient) ExpandPath(_ string) string { - return fmt.Sprintf("cluster/firewall/groups/%s", c.Group) -} diff --git a/proxmox/cluster/firewall/options.go b/proxmox/cluster/firewall/options.go deleted file mode 100644 index 63440234..00000000 --- a/proxmox/cluster/firewall/options.go +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "context" - "fmt" - "net/http" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Options is an interface for managing global firewall options. -type Options interface { - SetGlobalOptions(ctx context.Context, d *OptionsPutRequestBody) error - GetGlobalOptions(ctx context.Context) (*OptionsGetResponseData, error) -} - -// SetGlobalOptions sets the global firewall options. -func (c *Client) SetGlobalOptions(ctx context.Context, d *OptionsPutRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, "cluster/firewall/options", d, nil) - if err != nil { - return fmt.Errorf("error setting optionss: %w", err) - } - - return nil -} - -// GetGlobalOptions retrieves the global firewall options. -func (c *Client) GetGlobalOptions(ctx context.Context) (*OptionsGetResponseData, error) { - resBody := &OptionsGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, "cluster/firewall/options", nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving options: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} diff --git a/proxmox/cluster/firewall/options_types.go b/proxmox/cluster/firewall/options_types.go deleted file mode 100644 index f618bf31..00000000 --- a/proxmox/cluster/firewall/options_types.go +++ /dev/null @@ -1,109 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "encoding/json" - "fmt" - "net/url" - "strconv" - "strings" - - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// OptionsPutRequestBody is the request body for the PUT /cluster/firewall/options API call. -type OptionsPutRequestBody struct { - EBTables *types.CustomBool `json:"ebtables,omitempty" url:"ebtables,omitempty,int"` - Enable *types.CustomBool `json:"enable,omitempty" url:"enable,omitempty,int"` - LogRateLimit *CustomLogRateLimit `json:"log_ratelimit,omitempty" url:"log_ratelimit,omitempty"` - PolicyIn *string `json:"policy_in,omitempty" url:"policy_in,omitempty"` - PolicyOut *string `json:"policy_out,omitempty" url:"policy_out,omitempty"` -} - -// CustomLogRateLimit is a custom type for the log_ratelimit field of the firewall optionss. -type CustomLogRateLimit struct { - Enable types.CustomBool `json:"enable,omitempty" url:"enable,omitempty,int"` - Burst *int `json:"burst,omitempty" url:"burst,omitempty,int"` - Rate *string `json:"rate,omitempty" url:"rate,omitempty"` -} - -// OptionsGetResponseBody is the response body for the GET /cluster/firewall/options API call. -type OptionsGetResponseBody struct { - Data *OptionsGetResponseData `json:"data,omitempty"` -} - -// OptionsGetResponseData is the data field of the response body for the GET /cluster/firewall/options API call. -type OptionsGetResponseData struct { - EBTables *types.CustomBool `json:"ebtables" url:"ebtables, int"` - Enable *types.CustomBool `json:"enable" url:"enable,int"` - LogRateLimit *CustomLogRateLimit `json:"log_ratelimit" url:"log_ratelimit"` - PolicyIn *string `json:"policy_in" url:"policy_in"` - PolicyOut *string `json:"policy_out" url:"policy_out"` -} - -// EncodeValues converts a CustomWatchdogDevice struct to a URL vlaue. -func (r *CustomLogRateLimit) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.Enable { - values = append(values, "enable=1") - } else { - values = append(values, "enable=0") - } - - if r.Burst != nil { - values = append(values, fmt.Sprintf("burst=%d", *r.Burst)) - } - - if r.Rate != nil { - values = append(values, fmt.Sprintf("rate=%s", *r.Rate)) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// UnmarshalJSON unmarshals a CustomLogRateLimit struct from JSON. -func (r *CustomLogRateLimit) UnmarshalJSON(b []byte) error { - var s string - - err := json.Unmarshal(b, &s) - if err != nil { - return fmt.Errorf("error unmarshaling json: %w", err) - } - - if s == "" { - return nil - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 1 { - r.Enable = v[0] == "1" - } else if len(v) == 2 { - switch v[0] { - case "enable": - r.Enable = v[1] == "1" - case "burst": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("error converting burst to int: %w", err) - } - r.Burst = &iv - case "rate": - r.Rate = &v[1] - } - } - } - - return nil -} diff --git a/proxmox/cluster/firewall/security_groups.go b/proxmox/cluster/firewall/security_groups.go deleted file mode 100644 index 438363b4..00000000 --- a/proxmox/cluster/firewall/security_groups.go +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "context" - "fmt" - "net/http" - "net/url" - "sort" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// SecurityGroup is an interface for the Proxmox security group API. -type SecurityGroup interface { - CreateGroup(ctx context.Context, d *GroupCreateRequestBody) error - ListGroups(ctx context.Context) ([]*GroupListResponseData, error) - UpdateGroup(ctx context.Context, d *GroupUpdateRequestBody) error - DeleteGroup(ctx context.Context, group string) error -} - -func (c *Client) securityGroupsPath() string { - return "cluster/firewall/groups" -} - -// CreateGroup create new security group. -func (c *Client) CreateGroup(ctx context.Context, d *GroupCreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.securityGroupsPath(), d, nil) - if err != nil { - return fmt.Errorf("error creating security group: %w", err) - } - - return nil -} - -// ListGroups retrieve list of security groups. -func (c *Client) ListGroups(ctx context.Context) ([]*GroupListResponseData, error) { - resBody := &GroupListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.securityGroupsPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving security groups: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].Group < resBody.Data[j].Group - }) - - return resBody.Data, nil -} - -// UpdateGroup update security group. -func (c *Client) UpdateGroup(ctx context.Context, d *GroupUpdateRequestBody) error { - err := c.DoRequest( - ctx, - http.MethodPost, - c.securityGroupsPath(), - d, - nil, - ) - if err != nil { - return fmt.Errorf("error updating security group: %w", err) - } - - return nil -} - -// DeleteGroup delete security group. -func (c *Client) DeleteGroup(ctx context.Context, group string) error { - err := c.DoRequest( - ctx, - http.MethodDelete, - fmt.Sprintf("%s/%s", c.securityGroupsPath(), url.PathEscape(group)), - nil, - nil, - ) - if err != nil { - return fmt.Errorf("error deleting security group '%s': %w", group, err) - } - - return nil -} diff --git a/proxmox/cluster/firewall/security_groups_types.go b/proxmox/cluster/firewall/security_groups_types.go deleted file mode 100644 index a84fd090..00000000 --- a/proxmox/cluster/firewall/security_groups_types.go +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -// GroupCreateRequestBody contains the data for a security group create request. -type GroupCreateRequestBody struct { - Group string `json:"group" url:"group"` - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Digest *string `json:"digest,omitempty" url:"digest,omitempty"` -} - -// GroupListResponseData contains the data from a group list response. -type GroupListResponseData struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Group string `json:"group" url:"group"` - Digest string `json:"digest" url:"digest"` -} - -// GroupListResponseBody contains the data from a group get response. -type GroupListResponseBody struct { - Data []*GroupListResponseData `json:"data,omitempty"` -} - -// GroupUpdateRequestBody contains the data for a group update request. -type GroupUpdateRequestBody struct { - Group string `json:"group" url:"group"` - - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - ReName *string `json:"rename,omitempty" url:"rename,omitempty"` - Digest *string `json:"digest,omitempty" url:"digest,omitempty"` -} diff --git a/proxmox/firewall/aliases.go b/proxmox/firewall/aliases.go deleted file mode 100644 index afd14512..00000000 --- a/proxmox/firewall/aliases.go +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "context" - "fmt" - "net/http" - "net/url" - "sort" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Alias is an interface for managing firewall aliases. -type Alias interface { - CreateAlias(ctx context.Context, d *AliasCreateRequestBody) error - DeleteAlias(ctx context.Context, name string) error - GetAlias(ctx context.Context, name string) (*AliasGetResponseData, error) - ListAliases(ctx context.Context) ([]*AliasGetResponseData, error) - UpdateAlias(ctx context.Context, name string, d *AliasUpdateRequestBody) error -} - -func (c *Client) aliasesPath() string { - return c.ExpandPath("firewall/aliases") -} - -func (c *Client) aliasPath(name string) string { - return fmt.Sprintf("%s/%s", c.aliasesPath(), url.PathEscape(name)) -} - -// CreateAlias create an alias. -func (c *Client) CreateAlias(ctx context.Context, d *AliasCreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.aliasesPath(), d, nil) - if err != nil { - return fmt.Errorf("error creating alias: %w", err) - } - - return nil -} - -// DeleteAlias delete an alias. -func (c *Client) DeleteAlias(ctx context.Context, name string) error { - err := c.DoRequest(ctx, http.MethodDelete, c.aliasPath(name), nil, nil) - if err != nil { - return fmt.Errorf("error deleting alias '%s': %w", name, err) - } - - return nil -} - -// GetAlias retrieves an alias. -func (c *Client) GetAlias(ctx context.Context, name string) (*AliasGetResponseData, error) { - resBody := &AliasGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.aliasPath(name), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving alias '%s': %w", name, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// ListAliases retrieves a list of aliases. -func (c *Client) ListAliases(ctx context.Context) ([]*AliasGetResponseData, error) { - resBody := &AliasListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.aliasesPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving aliases: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].Name < resBody.Data[j].Name - }) - - return resBody.Data, nil -} - -// UpdateAlias updates an alias. -func (c *Client) UpdateAlias(ctx context.Context, name string, d *AliasUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.aliasPath(name), d, nil) - if err != nil { - return fmt.Errorf("error updating alias '%s': %w", name, err) - } - - return nil -} diff --git a/proxmox/firewall/aliases_types.go b/proxmox/firewall/aliases_types.go deleted file mode 100644 index 4a1927d7..00000000 --- a/proxmox/firewall/aliases_types.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -// AliasCreateRequestBody contains the data for an alias create request. -type AliasCreateRequestBody struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Name string `json:"name" url:"name"` - CIDR string `json:"cidr" url:"cidr"` -} - -// AliasGetResponseBody contains the body from an alias get response. -type AliasGetResponseBody struct { - Data *AliasGetResponseData `json:"data,omitempty"` -} - -// AliasGetResponseData contains the data from an alias get response. -type AliasGetResponseData struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Name string `json:"name" url:"name"` - CIDR string `json:"cidr" url:"cidr"` - Digest *string `json:"digest" url:"digest"` - IPVersion int `json:"ipversion" url:"ipversion"` -} - -// AliasListResponseBody contains the data from an alias get response. -type AliasListResponseBody struct { - Data []*AliasGetResponseData `json:"data,omitempty"` -} - -// AliasUpdateRequestBody contains the data for an alias update request. -type AliasUpdateRequestBody struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - ReName string `json:"rename" url:"rename"` - CIDR string `json:"cidr" url:"cidr"` -} diff --git a/proxmox/firewall/client.go b/proxmox/firewall/client.go deleted file mode 100644 index a7332611..00000000 --- a/proxmox/firewall/client.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// API is an interface for the Proxmox firewall API. -type API interface { - Alias - IPSet - Rule - Options -} - -// Client is an interface for accessing the Proxmox firewall API. -type Client struct { - api.Client -} diff --git a/proxmox/firewall/ipset.go b/proxmox/firewall/ipset.go deleted file mode 100644 index a79f6a5b..00000000 --- a/proxmox/firewall/ipset.go +++ /dev/null @@ -1,146 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -/** -* Reference: https://pve.proxmox.com/pve-docs/api-viewer/#/cluster/firewall/ipset - */ - -package firewall - -import ( - "context" - "fmt" - "net/http" - "net/url" - "sort" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// IPSet is an interface for managing IP sets. -type IPSet interface { - CreateIPSet(ctx context.Context, d *IPSetCreateRequestBody) error - AddCIDRToIPSet(ctx context.Context, id string, d IPSetGetResponseData) error - UpdateIPSet(ctx context.Context, d *IPSetUpdateRequestBody) error - DeleteIPSet(ctx context.Context, id string) error - DeleteIPSetContent(ctx context.Context, id string, cidr string) error - GetIPSetContent(ctx context.Context, id string) ([]*IPSetGetResponseData, error) - ListIPSets(ctx context.Context) ([]*IPSetListResponseData, error) -} - -func (c *Client) ipsetPath() string { - return c.ExpandPath("firewall/ipset") -} - -// CreateIPSet create an IPSet. -func (c *Client) CreateIPSet(ctx context.Context, d *IPSetCreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.ipsetPath(), d, nil) - if err != nil { - return fmt.Errorf("error creating IPSet: %w", err) - } - - return nil -} - -// AddCIDRToIPSet adds IP or Network to IPSet. -func (c *Client) AddCIDRToIPSet(ctx context.Context, id string, d IPSetGetResponseData) error { - err := c.DoRequest( - ctx, - http.MethodPost, - fmt.Sprintf("%s/%s", c.ipsetPath(), url.PathEscape(id)), - &d, - nil, - ) - if err != nil { - return fmt.Errorf("error adding CIDR to IPSet: %w", err) - } - - return nil -} - -// UpdateIPSet updates an IPSet. -func (c *Client) UpdateIPSet(ctx context.Context, d *IPSetUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.ipsetPath(), d, nil) - if err != nil { - return fmt.Errorf("error updating IPSet: %w", err) - } - - return nil -} - -// DeleteIPSet delete an IPSet. -func (c *Client) DeleteIPSet(ctx context.Context, id string) error { - err := c.DoRequest( - ctx, - http.MethodDelete, - fmt.Sprintf("%s/%s", c.ipsetPath(), url.PathEscape(id)), - nil, - nil, - ) - if err != nil { - return fmt.Errorf("error deleting IPSet %s: %w", id, err) - } - - return nil -} - -// DeleteIPSetContent remove IP or Network from IPSet. -func (c *Client) DeleteIPSetContent(ctx context.Context, id string, cidr string) error { - err := c.DoRequest( - ctx, - http.MethodDelete, - fmt.Sprintf("%s/%s/%s", c.ipsetPath(), url.PathEscape(id), url.PathEscape(cidr)), - nil, - nil, - ) - if err != nil { - return fmt.Errorf("error deleting IPSet content %s: %w", id, err) - } - - return nil -} - -// GetIPSetContent retrieve a list of IPSet content. -func (c *Client) GetIPSetContent(ctx context.Context, id string) ([]*IPSetGetResponseData, error) { - resBody := &IPSetGetResponseBody{} - - err := c.DoRequest( - ctx, - http.MethodGet, - fmt.Sprintf("%s/%s", c.ipsetPath(), url.PathEscape(id)), - nil, - resBody, - ) - if err != nil { - return nil, fmt.Errorf("error getting IPSet content: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// ListIPSets retrieves list of IPSets. -func (c *Client) ListIPSets(ctx context.Context) ([]*IPSetListResponseData, error) { - resBody := &IPSetListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ipsetPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error getting IPSet list: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].Name < resBody.Data[j].Name - }) - - return resBody.Data, nil -} diff --git a/proxmox/firewall/ipset_types.go b/proxmox/firewall/ipset_types.go deleted file mode 100644 index 87d31e25..00000000 --- a/proxmox/firewall/ipset_types.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// IPSetListResponseBody contains the data from an IPSet get response. -type IPSetListResponseBody struct { - Data []*IPSetListResponseData `json:"data,omitempty"` -} - -// IPSetCreateRequestBody contains the data for an IPSet create request. -type IPSetCreateRequestBody struct { - Comment string `json:"comment,omitempty" url:"comment,omitempty"` - Name string `json:"name" url:"name"` -} - -// IPSetGetResponseBody contains the body from an IPSet get response. -type IPSetGetResponseBody struct { - Data []*IPSetGetResponseData `json:"data,omitempty"` -} - -// IPSetGetResponseData contains the data from an IPSet get response. -type IPSetGetResponseData struct { - CIDR string `json:"cidr" url:"cidr"` - NoMatch *types.CustomBool `json:"nomatch,omitempty" url:"nomatch,omitempty,int"` - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` -} - -// IPSetUpdateRequestBody contains the data for an IPSet update request. -type IPSetUpdateRequestBody struct { - ReName string `json:"rename,omitempty" url:"rename,omitempty"` - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Name string `json:"name" url:"name"` -} - -// IPSetListResponseData contains list of IPSets from. -type IPSetListResponseData struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Name string `json:"name" url:"name"` -} - -// IPSetContent is an array of IPSetGetResponseData. -type IPSetContent []IPSetGetResponseData diff --git a/proxmox/firewall/options.go b/proxmox/firewall/options.go deleted file mode 100644 index 16a7a19b..00000000 --- a/proxmox/firewall/options.go +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "context" - "fmt" - "net/http" - "strconv" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Options is an interface for the Proxmox firewall options API. -type Options interface { - GetOptionsID() string - SetOptions(ctx context.Context, d *OptionsPutRequestBody) error - GetOptions(ctx context.Context) (*OptionsGetResponseData, error) -} - -func (c *Client) optionsPath() string { - return c.ExpandPath("firewall/options") -} - -// GetOptionsID returns the ID of the options object. -func (c *Client) GetOptionsID() string { - return "options-" + strconv.Itoa(schema.HashString(c.optionsPath())) -} - -// SetOptions sets the options object. -func (c *Client) SetOptions(ctx context.Context, d *OptionsPutRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.optionsPath(), d, nil) - if err != nil { - return fmt.Errorf("error setting optionss: %w", err) - } - - return nil -} - -// GetOptions retrieves the options object. -func (c *Client) GetOptions(ctx context.Context) (*OptionsGetResponseData, error) { - resBody := &OptionsGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.optionsPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving options: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} diff --git a/proxmox/firewall/options_types.go b/proxmox/firewall/options_types.go deleted file mode 100644 index 3f924ab0..00000000 --- a/proxmox/firewall/options_types.go +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// OptionsPutRequestBody is the request body for the PUT /cluster/firewall/options API call. -type OptionsPutRequestBody struct { - DHCP *types.CustomBool `json:"dhcp,omitempty" url:"dhcp,omitempty,int"` - Enable *types.CustomBool `json:"enable,omitempty" url:"enable,omitempty,int"` - IPFilter *types.CustomBool `json:"ipfilter,omitempty" url:"ipfilter,omitempty,int"` - LogLevelIN *string `json:"log_level_in,omitempty" url:"log_level_in,omitempty"` - LogLevelOUT *string `json:"log_level_out,omitempty" url:"log_level_out,omitempty"` - MACFilter *types.CustomBool `json:"macfilter,omitempty" url:"macfilter,omitempty,int"` - NDP *types.CustomBool `json:"ndp,omitempty" url:"ndp,omitempty,int"` - PolicyIn *string `json:"policy_in,omitempty" url:"policy_in,omitempty"` - PolicyOut *string `json:"policy_out,omitempty" url:"policy_out,omitempty"` - RAdv *types.CustomBool `json:"radv,omitempty" url:"radv,omitempty,int"` -} - -// OptionsGetResponseBody is the response body for the GET /cluster/firewall/options API call. -type OptionsGetResponseBody struct { - Data *OptionsGetResponseData `json:"data,omitempty"` -} - -// OptionsGetResponseData is the data field of the response body for the GET /cluster/firewall/options API call. -type OptionsGetResponseData struct { - DHCP *types.CustomBool `json:"dhcp" url:"dhcp,int"` - Enable *types.CustomBool `json:"enable" url:"enable,int"` - IPFilter *types.CustomBool `json:"ipfilter" url:"ipfilter,int"` - LogLevelIN *string `json:"log_level_in" url:"log_level_in"` - LogLevelOUT *string `json:"log_level_out" url:"log_level_out"` - MACFilter *types.CustomBool `json:"macfilter" url:"macfilter,int"` - NDP *types.CustomBool `json:"ndp" url:"ndp,int"` - PolicyIn *string `json:"policy_in" url:"policy_in"` - PolicyOut *string `json:"policy_out" url:"policy_out"` - RAdv *types.CustomBool `json:"radv" url:"radv,int"` -} diff --git a/proxmox/firewall/rules.go b/proxmox/firewall/rules.go deleted file mode 100644 index 74f3b985..00000000 --- a/proxmox/firewall/rules.go +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "context" - "fmt" - "net/http" - "strconv" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Rule is an interface for the Proxmox firewall rule API. -type Rule interface { - GetRulesID() string - CreateRule(ctx context.Context, d *RuleCreateRequestBody) error - GetRule(ctx context.Context, pos int) (*RuleGetResponseData, error) - ListRules(ctx context.Context) ([]*RuleListResponseData, error) - UpdateRule(ctx context.Context, pos int, d *RuleUpdateRequestBody) error - DeleteRule(ctx context.Context, pos int) error -} - -// BaseRule is the base struct for firewall rules. -type BaseRule struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - Dest *string `json:"dest,omitempty" url:"dest,omitempty"` - Digest *string `json:"digest,omitempty" url:"digest,omitempty"` - DPort *string `json:"dport,omitempty" url:"dport,omitempty"` - Enable *types.CustomBool `json:"enable,omitempty" url:"enable,omitempty,int"` - ICMPType *string `json:"icmp-type,omitempty" url:"icmp-type,omitempty"` - IFace *string `json:"iface,omitempty" url:"iface,omitempty"` - Log *string `json:"log,omitempty" url:"log,omitempty"` - Macro *string `json:"macro,omitempty" url:"macro,omitempty"` - Proto *string `json:"proto,omitempty" url:"proto,omitempty"` - Source *string `json:"source,omitempty" url:"source,omitempty"` - SPort *string `json:"sport,omitempty" url:"sport,omitempty"` -} - -func (c *Client) rulesPath() string { - return c.ExpandPath("firewall/rules") -} - -func (c *Client) rulePath(pos int) string { - return fmt.Sprintf("%s/%d", c.rulesPath(), pos) -} - -// GetRulesID returns the ID of the rules object. -func (c *Client) GetRulesID() string { - return "rule-" + strconv.Itoa(schema.HashString(c.rulesPath())) -} - -// CreateRule creates a firewall rule. -func (c *Client) CreateRule(ctx context.Context, d *RuleCreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.rulesPath(), d, nil) - if err != nil { - return fmt.Errorf("error creating firewall rule: %w", err) - } - - return nil -} - -// GetRule retrieves a firewall rule. -func (c *Client) GetRule(ctx context.Context, pos int) (*RuleGetResponseData, error) { - resBody := &RuleGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.rulePath(pos), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving firewall rule %d: %w", pos, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// ListRules retrieves a list of firewall rules. -func (c *Client) ListRules(ctx context.Context) ([]*RuleListResponseData, error) { - resBody := &RuleListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.rulesPath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving firewall rules: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// UpdateRule updates a firewall rule. -func (c *Client) UpdateRule(ctx context.Context, pos int, d *RuleUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.rulePath(pos), d, nil) - if err != nil { - return fmt.Errorf("error updating firewall rule %d: %w", pos, err) - } - - return nil -} - -// DeleteRule deletes a firewall rule. -func (c *Client) DeleteRule(ctx context.Context, pos int) error { - err := c.DoRequest(ctx, http.MethodDelete, c.rulePath(pos), nil, nil) - if err != nil { - return fmt.Errorf("error deleting firewall rule %d: %w", pos, err) - } - - return nil -} diff --git a/proxmox/firewall/rules_types.go b/proxmox/firewall/rules_types.go deleted file mode 100644 index 8bf212e6..00000000 --- a/proxmox/firewall/rules_types.go +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -// RuleCreateRequestBody contains the data for a firewall rule create request. -type RuleCreateRequestBody struct { - BaseRule - - Action string `json:"action" url:"action"` - Type string `json:"type" url:"type"` - - Group *string `json:"group,omitempty" url:"group,omitempty"` -} - -// RuleGetResponseBody contains the body from a firewall rule get response. -type RuleGetResponseBody struct { - Data *RuleGetResponseData `json:"data,omitempty"` -} - -// RuleGetResponseData contains the data from a firewall rule get response. -type RuleGetResponseData struct { - BaseRule - - // NOTE: This is `int` in the PVE API docs, but it's actually a string in the response. - Pos string `json:"pos" url:"pos"` - Action string `json:"action" url:"action"` - Type string `json:"type" url:"type"` -} - -// RuleListResponseBody contains the data from a firewall rule get response. -type RuleListResponseBody struct { - Data []*RuleListResponseData `json:"data,omitempty"` -} - -// RuleListResponseData contains the data from a firewall rule get response. -type RuleListResponseData struct { - Pos int `json:"pos" url:"pos"` -} - -// RuleUpdateRequestBody contains the data for a firewall rule update request. -type RuleUpdateRequestBody struct { - BaseRule - - Pos *int `json:"pos,omitempty" url:"pos,omitempty"` - Action *string `json:"action,omitempty" url:"action,omitempty"` - Type *string `json:"type,omitempty" url:"type,omitempty"` - - Group *string `json:"group,omitempty" url:"group,omitempty"` -} diff --git a/proxmox/nodes/certificate.go b/proxmox/nodes/certificate.go deleted file mode 100644 index 3e2c8dc0..00000000 --- a/proxmox/nodes/certificate.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "context" - "fmt" - "net/http" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// DeleteCertificate deletes the custom certificate for a node. -func (c *Client) DeleteCertificate(ctx context.Context, d *CertificateDeleteRequestBody) error { - err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath("certificates/custom"), d, nil) - if err != nil { - return fmt.Errorf("error deleting certificate: %w", err) - } - - return nil -} - -// ListCertificates retrieves the list of certificates for a node. -func (c *Client) ListCertificates(ctx context.Context) (*[]CertificateListResponseData, error) { - resBody := &CertificateListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("certificates/info"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving certificate list: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// UpdateCertificate updates the custom certificate for a node. -func (c *Client) UpdateCertificate(ctx context.Context, d *CertificateUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("certificates/custom"), d, nil) - if err != nil { - return fmt.Errorf("error updating certificate: %w", err) - } - - return nil -} diff --git a/proxmox/nodes/certificate_types.go b/proxmox/nodes/certificate_types.go deleted file mode 100644 index f81f82e3..00000000 --- a/proxmox/nodes/certificate_types.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// CertificateDeleteRequestBody contains the data for a custom certificate delete request. -type CertificateDeleteRequestBody struct { - Restart *types.CustomBool `json:"restart,omitempty" url:"restart,omitempty,int"` -} - -// CertificateListResponseBody contains the body from a certificate list response. -type CertificateListResponseBody struct { - Data *[]CertificateListResponseData `json:"data,omitempty"` -} - -// CertificateListResponseData contains the data from a certificate list response. -type CertificateListResponseData struct { - Certificates *string `json:"pem,omitempty"` - FileName *string `json:"filename,omitempty"` - Fingerprint *string `json:"fingerprint,omitempty"` - Issuer *string `json:"issuer,omitempty"` - NotAfter *types.CustomTimestamp `json:"notafter,omitempty"` - NotBefore *types.CustomTimestamp `json:"notbefore,omitempty"` - PublicKeyBits *int `json:"public-key-bits,omitempty"` - PublicKeyType *string `json:"public-key-type,omitempty"` - Subject *string `json:"subject,omitempty"` - SubjectAlternativeNames *[]string `json:"san,omitempty"` -} - -// CertificateUpdateRequestBody contains the body for a custom certificate update request. -type CertificateUpdateRequestBody struct { - Certificates string `json:"certificates" url:"certificates"` - Force *types.CustomBool `json:"force,omitempty" url:"force,omitempty,int"` - PrivateKey *string `json:"key,omitempty" url:"key,omitempty"` - Restart *types.CustomBool `json:"restart,omitempty" url:"restart,omitempty,int"` -} diff --git a/proxmox/nodes/client.go b/proxmox/nodes/client.go deleted file mode 100644 index 6b8d6529..00000000 --- a/proxmox/nodes/client.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "fmt" - "net/url" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/tasks" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" -) - -// Client is an interface for accessing the Proxmox node API. -type Client struct { - api.Client - NodeName string -} - -// ExpandPath expands a relative path to a full node API path. -func (c *Client) ExpandPath(path string) string { - return fmt.Sprintf("nodes/%s/%s", url.PathEscape(c.NodeName), path) -} - -// Container returns a client for managing a specific container. -func (c *Client) Container(vmID int) *containers.Client { - return &containers.Client{ - Client: c, - VMID: vmID, - } -} - -// VM returns a client for managing a specific VM. -func (c *Client) VM(vmID int) *vms.Client { - return &vms.Client{ - Client: c, - VMID: vmID, - } -} - -// Tasks returns a client for managing VM tasks. -func (c *Client) Tasks() *tasks.Client { - return &tasks.Client{ - Client: c, - } -} diff --git a/proxmox/nodes/containers/client.go b/proxmox/nodes/containers/client.go deleted file mode 100644 index 4f474996..00000000 --- a/proxmox/nodes/containers/client.go +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package containers - -import ( - "fmt" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" - containerfirewall "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers/firewall" -) - -// Client is an interface for accessing the Proxmox container API. -type Client struct { - api.Client - VMID int -} - -func (c *Client) basePath() string { - return c.Client.ExpandPath("lxc") -} - -// ExpandPath expands a relative path to a full container API path. -func (c *Client) ExpandPath(path string) string { - ep := fmt.Sprintf("%s/%d", c.basePath(), c.VMID) - if path != "" { - ep = fmt.Sprintf("%s/%s", ep, path) - } - - return ep -} - -// Firewall returns a client for managing the container firewall. -func (c *Client) Firewall() firewall.API { - return &containerfirewall.Client{ - Client: firewall.Client{Client: c}, - } -} diff --git a/proxmox/nodes/containers/containers.go b/proxmox/nodes/containers/containers.go deleted file mode 100644 index 386e6489..00000000 --- a/proxmox/nodes/containers/containers.go +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package containers - -import ( - "context" - "fmt" - "net/http" - "strings" - "time" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// CloneContainer clones a container. -func (c *Client) CloneContainer(ctx context.Context, d *CloneRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("/clone"), d, nil) - if err != nil { - return fmt.Errorf("error cloning container: %w", err) - } - - return nil -} - -// CreateContainer creates a container. -func (c *Client) CreateContainer(ctx context.Context, d *CreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.basePath(), d, nil) - if err != nil { - return fmt.Errorf("error creating container: %w", err) - } - - return nil -} - -// DeleteContainer deletes a container. -func (c *Client) DeleteContainer(ctx context.Context) error { - err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath(""), nil, nil) - if err != nil { - return fmt.Errorf("error deleting container: %w", err) - } - - return nil -} - -// GetContainer retrieves a container. -func (c *Client) GetContainer(ctx context.Context) (*GetResponseData, error) { - resBody := &GetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("config"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving container: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// GetContainerStatus retrieves the status for a container. -func (c *Client) GetContainerStatus(ctx context.Context) (*GetStatusResponseData, error) { - resBody := &GetStatusResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("status/current"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving container status: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// RebootContainer reboots a container. -func (c *Client) RebootContainer(ctx context.Context, d *RebootRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/reboot"), d, nil) - if err != nil { - return fmt.Errorf("error rebooting container: %w", err) - } - - return nil -} - -// ShutdownContainer shuts down a container. -func (c *Client) ShutdownContainer(ctx context.Context, d *ShutdownRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/shutdown"), d, nil) - if err != nil { - return fmt.Errorf("error shutting down container: %w", err) - } - - return nil -} - -// StartContainer starts a container. -func (c *Client) StartContainer(ctx context.Context) error { - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/start"), nil, nil) - if err != nil { - return fmt.Errorf("error starting container: %w", err) - } - - return nil -} - -// StopContainer stops a container immediately. -func (c *Client) StopContainer(ctx context.Context) error { - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/stop"), nil, nil) - if err != nil { - return fmt.Errorf("error stopping container: %w", err) - } - - return nil -} - -// UpdateContainer updates a container. -func (c *Client) UpdateContainer(ctx context.Context, d *UpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("config"), d, nil) - if err != nil { - return fmt.Errorf("error updating container: %w", err) - } - - return nil -} - -// WaitForContainerState waits for a container to reach a specific state. -func (c *Client) WaitForContainerState(ctx context.Context, state string, timeout int, delay int) error { - state = strings.ToLower(state) - - timeDelay := int64(delay) - timeMax := float64(timeout) - timeStart := time.Now() - timeElapsed := timeStart.Sub(timeStart) - - for timeElapsed.Seconds() < timeMax { - if int64(timeElapsed.Seconds())%timeDelay == 0 { - data, err := c.GetContainerStatus(ctx) - if err != nil { - return fmt.Errorf("error retrieving container status: %w", err) - } - - if data.Status == state { - return nil - } - - time.Sleep(1 * time.Second) - } - - time.Sleep(200 * time.Millisecond) - - timeElapsed = time.Since(timeStart) - - if ctx.Err() != nil { - return fmt.Errorf("context error: %w", ctx.Err()) - } - } - - return fmt.Errorf( - "timeout while waiting for container \"%d\" to enter the state \"%s\"", - c.VMID, - state, - ) -} - -// WaitForContainerLock waits for a container lock to be released. -func (c *Client) WaitForContainerLock(ctx context.Context, timeout int, delay int, ignoreErrorResponse bool) error { - timeDelay := int64(delay) - timeMax := float64(timeout) - timeStart := time.Now() - timeElapsed := timeStart.Sub(timeStart) - - for timeElapsed.Seconds() < timeMax { - if int64(timeElapsed.Seconds())%timeDelay == 0 { - data, err := c.GetContainerStatus(ctx) - - if err != nil { - if !ignoreErrorResponse { - return fmt.Errorf("error retrieving container status: %w", err) - } - } else if data.Lock == nil || *data.Lock == "" { - return nil - } - - time.Sleep(1 * time.Second) - } - - time.Sleep(200 * time.Millisecond) - - timeElapsed = time.Since(timeStart) - - if ctx.Err() != nil { - return fmt.Errorf("context error: %w", ctx.Err()) - } - } - - return fmt.Errorf("timeout while waiting for container \"%d\" to become unlocked", c.VMID) -} diff --git a/proxmox/nodes/containers/containers_types.go b/proxmox/nodes/containers/containers_types.go deleted file mode 100644 index 1ad3a3f3..00000000 --- a/proxmox/nodes/containers/containers_types.go +++ /dev/null @@ -1,811 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package containers - -import ( - "encoding/json" - "fmt" - "net/url" - "strconv" - "strings" - - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// CloneRequestBody contains the data for an container clone request. -type CloneRequestBody struct { - BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - Description *string `json:"description,omitempty" url:"description,omitempty"` - FullCopy *types.CustomBool `json:"full,omitempty" url:"full,omitempty,int"` - Hostname *string `json:"hostname,omitempty" url:"hostname,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - SnapshotName *string `json:"snapname,omitempty" url:"snapname,omitempty"` - TargetNodeName *string `json:"target,omitempty" url:"target,omitempty"` - TargetStorage *string `json:"storage,omitempty" url:"storage,omitempty"` - VMIDNew int `json:"newid" url:"newid"` -} - -// CreateRequestBody contains the data for a user create request. -type CreateRequestBody struct { - BandwidthLimit *float64 `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - ConsoleEnabled *types.CustomBool `json:"console,omitempty" url:"console,omitempty,int"` - ConsoleMode *string `json:"cmode,omitempty" url:"cmode,omitempty"` - CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` - CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` - CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"` - CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"` - DatastoreID *string `json:"storage,omitempty" url:"storage,omitempty"` - DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"` - Delete []string `json:"delete,omitempty" url:"delete,omitempty"` - Description *string `json:"description,omitempty" url:"description,omitempty"` - DNSDomain *string `json:"searchdomain,omitempty" url:"searchdomain,omitempty"` - DNSServer *string `json:"nameserver,omitempty" url:"nameserver,omitempty"` - Features *CustomFeatures `json:"features,omitempty" url:"features,omitempty"` - Force *types.CustomBool `json:"force,omitempty" url:"force,omitempty,int"` - HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"` - Hostname *string `json:"hostname,omitempty" url:"hostname,omitempty"` - IgnoreUnpackErrors *types.CustomBool `json:"ignore-unpack-errors,omitempty" url:"force,omitempty,int"` - Lock *string `json:"lock,omitempty" url:"lock,omitempty,int"` - MountPoints CustomMountPointArray `json:"mp,omitempty" url:"mp,omitempty,numbered"` - NetworkInterfaces CustomNetworkInterfaceArray `json:"net,omitempty" url:"net,omitempty,numbered"` - OSTemplateFileVolume *string `json:"ostemplate,omitempty" url:"ostemplate,omitempty"` - OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"` - Password *string `json:"password,omitempty" url:"password,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - Protection *types.CustomBool `json:"protection,omitempty" url:"protection,omitempty,int"` - Restore *types.CustomBool `json:"restore,omitempty" url:"restore,omitempty,int"` - RootFS *CustomRootFS `json:"rootfs,omitempty" url:"rootfs,omitempty"` - SSHKeys *CustomSSHKeys `json:"ssh-public-keys,omitempty" url:"ssh-public-keys,omitempty"` - Start *types.CustomBool `json:"start,omitempty" url:"start,omitempty,int"` - StartOnBoot *types.CustomBool `json:"onboot,omitempty" url:"onboot,omitempty,int"` - StartupBehavior *CustomStartupBehavior `json:"startup,omitempty" url:"startup,omitempty"` - Swap *int `json:"swap,omitempty" url:"swap,omitempty"` - Tags *string `json:"tags,omitempty" url:"tags,omitempty"` - Template *types.CustomBool `json:"template,omitempty" url:"template,omitempty,int"` - TTY *int `json:"tty,omitempty" url:"tty,omitempty"` - Unique *types.CustomBool `json:"unique,omitempty" url:"unique,omitempty,int"` - Unprivileged *types.CustomBool `json:"unprivileged,omitempty" url:"unprivileged,omitempty,int"` - VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"` -} - -// CustomFeatures contains the values for the "features" property. -type CustomFeatures struct { - FUSE *types.CustomBool `json:"fuse,omitempty" url:"fuse,omitempty,int"` - KeyControl *types.CustomBool `json:"keyctl,omitempty" url:"keyctl,omitempty,int"` - MountTypes *[]string `json:"mount,omitempty" url:"mount,omitempty"` - Nesting *types.CustomBool `json:"nesting,omitempty" url:"nesting,omitempty,int"` -} - -// CustomMountPoint contains the values for the "mp[n]" properties. -type CustomMountPoint struct { - ACL *types.CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"` - Backup *types.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` - DiskSize *string `json:"size,omitempty" url:"size,omitempty"` - Enabled bool `json:"-" url:"-"` - MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"` - MountPoint string `json:"mp" url:"mp"` - Quota *types.CustomBool `json:"quota,omitempty" url:"quota,omitempty,int"` - ReadOnly *types.CustomBool `json:"ro,omitempty" url:"ro,omitempty,int"` - Replicate *types.CustomBool `json:"replicate,omitempty" url:"replicate,omitempty,int"` - Shared *types.CustomBool `json:"shared,omitempty" url:"shared,omitempty,int"` - Volume string `json:"volume" url:"volume"` -} - -// CustomMountPointArray is an array of CustomMountPoint. -type CustomMountPointArray []CustomMountPoint - -// CustomNetworkInterface contains the values for the "net[n]" properties. -type CustomNetworkInterface struct { - Bridge *string `json:"bridge,omitempty" url:"bridge,omitempty"` - Enabled bool `json:"-" url:"-"` - Firewall *types.CustomBool `json:"firewall,omitempty" url:"firewall,omitempty,int"` - IPv4Address *string `json:"ip,omitempty" url:"ip,omitempty"` - IPv4Gateway *string `json:"gw,omitempty" url:"gw,omitempty"` - IPv6Address *string `json:"ip6,omitempty" url:"ip6,omitempty"` - IPv6Gateway *string `json:"gw6,omitempty" url:"gw6,omitempty"` - MACAddress *string `json:"hwaddr,omitempty" url:"hwaddr,omitempty"` - MTU *int `json:"mtu,omitempty" url:"mtu,omitempty"` - Name string `json:"name" url:"name"` - RateLimit *float64 `json:"rate,omitempty" url:"rate,omitempty"` - Tag *int `json:"tag,omitempty" url:"tag,omitempty"` - Trunks *[]int `json:"trunks,omitempty" url:"trunks,omitempty"` - Type *string `json:"type,omitempty" url:"type,omitempty"` -} - -// CustomNetworkInterfaceArray is an array of CustomNetworkInterface. -type CustomNetworkInterfaceArray []CustomNetworkInterface - -// CustomRootFS contains the values for the "rootfs" property. -type CustomRootFS struct { - ACL *types.CustomBool `json:"acl,omitempty" url:"acl,omitempty,int"` - Size *types.DiskSize `json:"size,omitempty" url:"size,omitempty"` - MountOptions *[]string `json:"mountoptions,omitempty" url:"mountoptions,omitempty"` - Quota *types.CustomBool `json:"quota,omitempty" url:"quota,omitempty,int"` - ReadOnly *types.CustomBool `json:"ro,omitempty" url:"ro,omitempty,int"` - Replicate *types.CustomBool `json:"replicate,omitempty" url:"replicate,omitempty,int"` - Shared *types.CustomBool `json:"shared,omitempty" url:"shared,omitempty,int"` - Volume string `json:"volume" url:"volume"` -} - -// CustomSSHKeys contains the values for the "ssh-public-keys" property. -type CustomSSHKeys []string - -// CustomStartupBehavior contains the values for the "startup" property. -type CustomStartupBehavior struct { - Down *int `json:"down,omitempty" url:"down,omitempty"` - Order *int `json:"order,omitempty" url:"order,omitempty"` - Up *int `json:"up,omitempty" url:"up,omitempty"` -} - -// GetResponseBody contains the body from a user get response. -type GetResponseBody struct { - Data *GetResponseData `json:"data,omitempty"` -} - -// GetResponseData contains the data from a user get response. -type GetResponseData struct { - ConsoleEnabled *types.CustomBool `json:"console,omitempty"` - ConsoleMode *string `json:"cmode,omitempty"` - CPUArchitecture *string `json:"arch,omitempty"` - CPUCores *int `json:"cores,omitempty"` - CPULimit *int `json:"cpulimit,omitempty"` - CPUUnits *int `json:"cpuunits,omitempty"` - DedicatedMemory *int `json:"memory,omitempty"` - Description *string `json:"description,omitempty"` - Digest string `json:"digest"` - DNSDomain *string `json:"searchdomain,omitempty"` - DNSServer *string `json:"nameserver,omitempty"` - Features *CustomFeatures `json:"features,omitempty"` - HookScript *string `json:"hookscript,omitempty"` - Hostname *string `json:"hostname,omitempty"` - Lock *types.CustomBool `json:"lock,omitempty"` - LXCConfiguration *[][2]string `json:"lxc,omitempty"` - MountPoint0 CustomMountPoint `json:"mp0,omitempty"` - MountPoint1 CustomMountPoint `json:"mp1,omitempty"` - MountPoint2 CustomMountPoint `json:"mp2,omitempty"` - MountPoint3 CustomMountPoint `json:"mp3,omitempty"` - NetworkInterface0 *CustomNetworkInterface `json:"net0,omitempty"` - NetworkInterface1 *CustomNetworkInterface `json:"net1,omitempty"` - NetworkInterface2 *CustomNetworkInterface `json:"net2,omitempty"` - NetworkInterface3 *CustomNetworkInterface `json:"net3,omitempty"` - NetworkInterface4 *CustomNetworkInterface `json:"net4,omitempty"` - NetworkInterface5 *CustomNetworkInterface `json:"net5,omitempty"` - NetworkInterface6 *CustomNetworkInterface `json:"net6,omitempty"` - NetworkInterface7 *CustomNetworkInterface `json:"net7,omitempty"` - OSType *string `json:"ostype,omitempty"` - Protection *types.CustomBool `json:"protection,omitempty"` - RootFS *CustomRootFS `json:"rootfs,omitempty"` - StartOnBoot *types.CustomBool `json:"onboot,omitempty"` - StartupBehavior *CustomStartupBehavior `json:"startup,omitempty"` - Swap *int `json:"swap,omitempty"` - Tags *string `json:"tags,omitempty"` - Template *types.CustomBool `json:"template,omitempty"` - TTY *int `json:"tty,omitempty"` - Unprivileged *types.CustomBool `json:"unprivileged,omitempty"` -} - -// GetStatusResponseBody contains the body from a container get status response. -type GetStatusResponseBody struct { - Data *GetStatusResponseData `json:"data,omitempty"` -} - -// GetStatusResponseData contains the data from a container get status response. -type GetStatusResponseData struct { - CPUCount *float64 `json:"cpus,omitempty"` - Lock *string `json:"lock,omitempty"` - MemoryAllocation *int `json:"maxmem,omitempty"` - Name *string `json:"name,omitempty"` - RootDiskSize *interface{} `json:"maxdisk,omitempty"` - Status string `json:"status,omitempty"` - SwapAllocation *int `json:"maxswap,omitempty"` - Tags *string `json:"tags,omitempty"` - Uptime *int `json:"uptime,omitempty"` - VMID *int `json:"vmid,omitempty"` -} - -// RebootRequestBody contains the body for a container reboot request. -type RebootRequestBody struct { - Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"` -} - -// ShutdownRequestBody contains the body for a container shutdown request. -type ShutdownRequestBody struct { - ForceStop *types.CustomBool `json:"forceStop,omitempty" url:"forceStop,omitempty,int"` - Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"` -} - -// UpdateRequestBody contains the data for an user update request. -type UpdateRequestBody CreateRequestBody - -// EncodeValues converts a ContainerCustomFeatures struct to a URL value. -func (r *CustomFeatures) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.FUSE != nil { - if *r.FUSE { - values = append(values, "fuse=1") - } else { - values = append(values, "fuse=0") - } - } - - if r.KeyControl != nil { - if *r.KeyControl { - values = append(values, "keyctl=1") - } else { - values = append(values, "keyctl=0") - } - } - - if r.MountTypes != nil { - if len(*r.MountTypes) > 0 { - values = append(values, fmt.Sprintf("mount=%s", strings.Join(*r.MountTypes, ";"))) - } - } - - if r.Nesting != nil { - if *r.Nesting { - values = append(values, "nesting=1") - } else { - values = append(values, "nesting=0") - } - } - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// EncodeValues converts a CustomMountPoint struct to a URL value. -func (r *CustomMountPoint) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.ACL != nil { - if *r.ACL { - values = append(values, "acl=%d") - } else { - values = append(values, "acl=0") - } - } - - if r.Backup != nil { - if *r.Backup { - values = append(values, "backup=1") - } else { - values = append(values, "backup=0") - } - } - - if r.DiskSize != nil { - values = append(values, fmt.Sprintf("size=%s", *r.DiskSize)) - } - - if r.MountOptions != nil { - if len(*r.MountOptions) > 0 { - values = append(values, fmt.Sprintf("mount=%s", strings.Join(*r.MountOptions, ";"))) - } - } - - values = append(values, fmt.Sprintf("mp=%s", r.MountPoint)) - - if r.Quota != nil { - if *r.Quota { - values = append(values, "quota=1") - } else { - values = append(values, "quota=0") - } - } - - if r.ReadOnly != nil { - if *r.ReadOnly { - values = append(values, "ro=1") - } else { - values = append(values, "ro=0") - } - } - - if r.Replicate != nil { - if *r.ReadOnly { - values = append(values, "replicate=1") - } else { - values = append(values, "replicate=0") - } - } - - if r.Shared != nil { - if *r.Shared { - values = append(values, "shared=1") - } else { - values = append(values, "shared=0") - } - } - - values = append(values, fmt.Sprintf("volume=%s", r.Volume)) - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// EncodeValues converts a CustomMountPointArray array to multiple URL values. -func (r CustomMountPointArray) EncodeValues( - key string, - v *url.Values, -) error { - for i, d := range r { - if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil { - return fmt.Errorf("failed to encode CustomMountPointArray: %w", err) - } - } - - return nil -} - -// EncodeValues converts a CustomNetworkInterface struct to a URL value. -func (r *CustomNetworkInterface) EncodeValues( - key string, - v *url.Values, -) error { - var values []string - - if r.Bridge != nil { - values = append(values, fmt.Sprintf("bridge=%s", *r.Bridge)) - } - - if r.Firewall != nil { - if *r.Firewall { - values = append(values, "firewall=1") - } else { - values = append(values, "firewall=0") - } - } - - if r.IPv4Address != nil { - values = append(values, fmt.Sprintf("ip=%s", *r.IPv4Address)) - } - - if r.IPv4Gateway != nil { - values = append(values, fmt.Sprintf("gw=%s", *r.IPv4Gateway)) - } - - if r.IPv6Address != nil { - values = append(values, fmt.Sprintf("ip6=%s", *r.IPv6Address)) - } - - if r.IPv6Gateway != nil { - values = append(values, fmt.Sprintf("gw6=%s", *r.IPv6Gateway)) - } - - if r.MACAddress != nil { - values = append(values, fmt.Sprintf("hwaddr=%s", *r.MACAddress)) - } - - if r.MTU != nil { - values = append(values, fmt.Sprintf("mtu=%d", *r.MTU)) - } - - values = append(values, fmt.Sprintf("name=%s", r.Name)) - - if r.RateLimit != nil { - values = append(values, fmt.Sprintf("rate=%.2f", *r.RateLimit)) - } - - if r.Tag != nil { - values = append(values, fmt.Sprintf("tag=%d", *r.Tag)) - } - - if r.Trunks != nil && len(*r.Trunks) > 0 { - sTrunks := make([]string, len(*r.Trunks)) - - for i, v := range *r.Trunks { - sTrunks[i] = strconv.Itoa(v) - } - - values = append(values, fmt.Sprintf("trunks=%s", strings.Join(sTrunks, ";"))) - } - - if r.Type != nil { - values = append(values, fmt.Sprintf("type=%s", *r.Type)) - } - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// EncodeValues converts a CustomNetworkInterfaceArray array to multiple URL values. -func (r CustomNetworkInterfaceArray) EncodeValues( - key string, - v *url.Values, -) error { - for i, d := range r { - if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil { - return fmt.Errorf("failed to encode CustomNetworkInterfaceArray: %w", err) - } - } - - return nil -} - -// EncodeValues converts a CustomRootFS struct to a URL value. -func (r *CustomRootFS) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.ACL != nil { - if *r.ACL { - values = append(values, "acl=%d") - } else { - values = append(values, "acl=0") - } - } - - if r.Size != nil { - values = append(values, fmt.Sprintf("size=%s", *r.Size)) - } - - if r.MountOptions != nil { - if len(*r.MountOptions) > 0 { - values = append(values, fmt.Sprintf("mount=%s", strings.Join(*r.MountOptions, ";"))) - } - } - - if r.Quota != nil { - if *r.Quota { - values = append(values, "quota=1") - } else { - values = append(values, "quota=0") - } - } - - if r.ReadOnly != nil { - if *r.ReadOnly { - values = append(values, "ro=1") - } else { - values = append(values, "ro=0") - } - } - - if r.Replicate != nil { - if *r.ReadOnly { - values = append(values, "replicate=1") - } else { - values = append(values, "replicate=0") - } - } - - if r.Shared != nil { - if *r.Shared { - values = append(values, "shared=1") - } else { - values = append(values, "shared=0") - } - } - - values = append(values, fmt.Sprintf("volume=%s", r.Volume)) - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// EncodeValues converts a CustomSSHKeys array to a URL value. -func (r CustomSSHKeys) EncodeValues(key string, v *url.Values) error { - v.Add(key, strings.Join(r, "\n")) - - return nil -} - -// EncodeValues converts a CustomStartupBehavior struct to a URL value. -func (r *CustomStartupBehavior) EncodeValues( - key string, - v *url.Values, -) error { - var values []string - - if r.Down != nil { - values = append(values, fmt.Sprintf("down=%d", *r.Down)) - } - - if r.Order != nil { - values = append(values, fmt.Sprintf("order=%d", *r.Order)) - } - - if r.Up != nil { - values = append(values, fmt.Sprintf("up=%d", *r.Up)) - } - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// UnmarshalJSON converts a ContainerCustomFeatures string to an object. -func (r *CustomFeatures) UnmarshalJSON(b []byte) error { - var s string - - err := json.Unmarshal(b, &s) - if err != nil { - return fmt.Errorf("unable to unmarshal ContainerCustomFeatures: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 2 { - switch v[0] { - case "fuse": - bv := types.CustomBool(v[1] == "1") - r.FUSE = &bv - case "keyctl": - bv := types.CustomBool(v[1] == "1") - r.KeyControl = &bv - case "mount": - if v[1] != "" { - a := strings.Split(v[1], ";") - r.MountTypes = &a - } else { - var a []string - r.MountTypes = &a - } - case "nesting": - bv := types.CustomBool(v[1] == "1") - r.Nesting = &bv - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomMountPoint string to an object. -func (r *CustomMountPoint) UnmarshalJSON(b []byte) error { - var s string - - err := json.Unmarshal(b, &s) - if err != nil { - return fmt.Errorf("unable to unmarshal CustomMountPoint: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 1 { - r.Volume = v[0] - } else if len(v) == 2 { - switch v[0] { - case "acl": - bv := types.CustomBool(v[1] == "1") - r.ACL = &bv - case "backup": - bv := types.CustomBool(v[1] == "1") - r.Backup = &bv - case "mountoptions": - if v[1] != "" { - a := strings.Split(v[1], ";") - r.MountOptions = &a - } else { - var a []string - r.MountOptions = &a - } - case "mp": - r.MountPoint = v[1] - case "quota": - bv := types.CustomBool(v[1] == "1") - r.Quota = &bv - case "ro": - bv := types.CustomBool(v[1] == "1") - r.ReadOnly = &bv - case "replicate": - bv := types.CustomBool(v[1] == "1") - r.Replicate = &bv - case "shared": - bv := types.CustomBool(v[1] == "1") - r.Shared = &bv - case "size": - r.DiskSize = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomNetworkInterface string to an object. -func (r *CustomNetworkInterface) UnmarshalJSON(b []byte) error { - var s string - - er := json.Unmarshal(b, &s) - if er != nil { - return fmt.Errorf("unable to unmarshal CustomNetworkInterface: %w", er) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - //nolint:nestif - if len(v) == 1 { - r.Name = v[0] - } else if len(v) == 2 { - switch v[0] { - case "bridge": - r.Bridge = &v[1] - case "firewall": - bv := types.CustomBool(v[1] == "1") - r.Firewall = &bv - case "gw": - r.IPv4Gateway = &v[1] - case "gw6": - r.IPv6Gateway = &v[1] - case "ip": - r.IPv4Address = &v[1] - case "ip6": - r.IPv6Address = &v[1] - case "hwaddr": - r.MACAddress = &v[1] - case "mtu": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("unable to unmarshal 'mtu': %w", err) - } - - r.MTU = &iv - case "name": - r.Name = v[1] - case "rate": - fv, err := strconv.ParseFloat(v[1], 64) - if err != nil { - return fmt.Errorf("unable to unmarshal 'rate': %w", err) - } - - r.RateLimit = &fv - case "tag": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("unable to unmarshal 'tag': %w", err) - } - - r.Tag = &iv - case "trunks": - var err error - if v[1] != "" { - trunks := strings.Split(v[1], ";") - a := make([]int, len(trunks)) - - for ti, tv := range trunks { - a[ti], err = strconv.Atoi(tv) - if err != nil { - return fmt.Errorf("unable to unmarshal 'trunks': %w", err) - } - } - - r.Trunks = &a - } else { - var a []int - r.Trunks = &a - } - case "type": - r.Type = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomRootFS string to an object. -func (r *CustomRootFS) UnmarshalJSON(b []byte) error { - var s string - - err := json.Unmarshal(b, &s) - if err != nil { - return fmt.Errorf("unable to unmarshal CustomRootFS: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 1 { - r.Volume = v[0] - } else if len(v) == 2 { - switch v[0] { - case "acl": - bv := types.CustomBool(v[1] == "1") - r.ACL = &bv - case "mountoptions": - if v[1] != "" { - a := strings.Split(v[1], ";") - r.MountOptions = &a - } else { - var a []string - r.MountOptions = &a - } - case "quota": - bv := types.CustomBool(v[1] == "1") - r.Quota = &bv - case "ro": - bv := types.CustomBool(v[1] == "1") - r.ReadOnly = &bv - case "replicate": - bv := types.CustomBool(v[1] == "1") - r.Replicate = &bv - case "shared": - bv := types.CustomBool(v[1] == "1") - r.Shared = &bv - case "size": - r.Size = new(types.DiskSize) - err := r.Size.UnmarshalJSON([]byte(v[1])) - if err != nil { - return fmt.Errorf("failed to unmarshal disk size: %w", err) - } - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomStartupBehavior string to an object. -func (r *CustomStartupBehavior) UnmarshalJSON(b []byte) error { - var s string - - err := json.Unmarshal(b, &s) - if err != nil { - return fmt.Errorf("unable to unmarshal CustomStartupBehavior: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 2 { - switch v[0] { - case "down": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("unable to unmarshal 'down': %w", err) - } - - r.Down = &iv - case "order": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("unable to unmarshal 'order': %w", err) - } - - r.Order = &iv - case "up": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("unable to unmarshal 'up': %w", err) - } - - r.Up = &iv - } - } - } - - return nil -} diff --git a/proxmox/nodes/containers/firewall/client.go b/proxmox/nodes/containers/firewall/client.go deleted file mode 100644 index 24e7e880..00000000 --- a/proxmox/nodes/containers/firewall/client.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" -) - -// Client is an interface for accessing the Proxmox container firewall API. -type Client struct { - firewall.Client -} diff --git a/proxmox/nodes/dns.go b/proxmox/nodes/dns.go deleted file mode 100644 index 2b5cb754..00000000 --- a/proxmox/nodes/dns.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "context" - "fmt" - "net/http" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// GetDNS retrieves the DNS configuration for a node. -func (c *Client) GetDNS(ctx context.Context) (*DNSGetResponseData, error) { - resBody := &DNSGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("dns"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving DNS configuration: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// UpdateDNS updates the DNS configuration for a node. -func (c *Client) UpdateDNS(ctx context.Context, d *DNSUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("dns"), d, nil) - if err != nil { - return fmt.Errorf("error updating DNS configuration: %w", err) - } - - return nil -} diff --git a/proxmox/nodes/dns_types.go b/proxmox/nodes/dns_types.go deleted file mode 100644 index 2b4abb4d..00000000 --- a/proxmox/nodes/dns_types.go +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -// DNSGetResponseBody contains the body from a DNS get response. -type DNSGetResponseBody struct { - Data *DNSGetResponseData `json:"data,omitempty"` -} - -// DNSGetResponseData contains the data from a DNS get response. -type DNSGetResponseData struct { - Server1 *string `json:"dns1,omitempty" url:"dns1,omitempty"` - Server2 *string `json:"dns2,omitempty" url:"dns2,omitempty"` - Server3 *string `json:"dns3,omitempty" url:"dns3,omitempty"` - SearchDomain *string `json:"search,omitempty" url:"search,omitempty"` -} - -// DNSUpdateRequestBody contains the body for a DNS update request. -type DNSUpdateRequestBody struct { - Server1 *string `json:"dns1,omitempty" url:"dns1,omitempty"` - Server2 *string `json:"dns2,omitempty" url:"dns2,omitempty"` - Server3 *string `json:"dns3,omitempty" url:"dns3,omitempty"` - SearchDomain *string `json:"search,omitempty" url:"search,omitempty"` -} diff --git a/proxmox/nodes/hosts.go b/proxmox/nodes/hosts.go deleted file mode 100644 index ab738879..00000000 --- a/proxmox/nodes/hosts.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "context" - "fmt" - "net/http" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// GetHosts retrieves the Hosts configuration for a node. -func (c *Client) GetHosts(ctx context.Context) (*HostsGetResponseData, error) { - resBody := &HostsGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("hosts"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving hosts configuration: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// UpdateHosts updates the Hosts configuration for a node. -func (c *Client) UpdateHosts(ctx context.Context, d *HostsUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("hosts"), d, nil) - if err != nil { - return fmt.Errorf("error updating hosts configuration: %w", err) - } - - return nil -} diff --git a/proxmox/nodes/hosts_types.go b/proxmox/nodes/hosts_types.go deleted file mode 100644 index 019736a8..00000000 --- a/proxmox/nodes/hosts_types.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -// HostsGetResponseBody contains the body from a hosts get response. -type HostsGetResponseBody struct { - Data *HostsGetResponseData `json:"data,omitempty"` -} - -// HostsGetResponseData contains the data from a hosts get response. -type HostsGetResponseData struct { - Data string `json:"data"` - Digest *string `json:"digest,omitempty"` -} - -// HostsUpdateRequestBody contains the body for a hosts update request. -type HostsUpdateRequestBody struct { - Data string `json:"data" url:"data"` - Digest *string `json:"digest,omitempty" url:"digest,omitempty"` -} diff --git a/proxmox/nodes/network.go b/proxmox/nodes/network.go deleted file mode 100644 index b055e17b..00000000 --- a/proxmox/nodes/network.go +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "context" - "fmt" - "net/http" - "net/url" - "sort" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -const ( - networkReloadTimeoutSec = 5 -) - -// ListNetworkInterfaces retrieves a list of network interfaces for a specific nodes. -func (c *Client) ListNetworkInterfaces(ctx context.Context) ([]*NetworkInterfaceListResponseData, error) { - resBody := &NetworkInterfaceListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("network"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get network interfaces for node \"%s\": %w", c.NodeName, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].Priority < resBody.Data[j].Priority - }) - - return resBody.Data, nil -} - -// CreateNetworkInterface creates a network interface for a specific node. -func (c *Client) CreateNetworkInterface(ctx context.Context, d *NetworkInterfaceCreateUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("network"), d, nil) - if err != nil { - return fmt.Errorf( - "failed to create network interface \"%s\" for node \"%s\": %w", - d.Iface, c.NodeName, err, - ) - } - - return nil -} - -// ReloadNetworkConfiguration reloads the network configuration for a specific node. -func (c *Client) ReloadNetworkConfiguration(ctx context.Context) error { - resBody := &ReloadNetworkResponseBody{} - - err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("network"), nil, resBody) - if err != nil { - return fmt.Errorf("failed to reload network configuration for node \"%s\": %w", c.NodeName, err) - } - - if resBody.Data == nil { - return api.ErrNoDataObjectInResponse - } - - err = c.Tasks().WaitForTask(ctx, *resBody.Data, networkReloadTimeoutSec, 1) - if err == nil { - return nil - } - - return nil -} - -// RevertNetworkConfiguration reverts the network configuration changes for a specific node. -func (c *Client) RevertNetworkConfiguration(ctx context.Context) error { - err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath("network"), nil, nil) - if err != nil { - return fmt.Errorf("failed to revert network configuration for node \"%s\": %w", c.NodeName, err) - } - - return nil -} - -// UpdateNetworkInterface updates a network interface for a specific node. -func (c *Client) UpdateNetworkInterface( - ctx context.Context, - iface string, - d *NetworkInterfaceCreateUpdateRequestBody, -) error { - err := c.DoRequest( - ctx, - http.MethodPut, - c.ExpandPath(fmt.Sprintf("network/%s", url.PathEscape(iface))), - d, - nil, - ) - if err != nil { - return fmt.Errorf("failed to update network interface \"%s\" for node \"%s\": %w", - d.Iface, c.NodeName, err, - ) - } - - return nil -} - -// DeleteNetworkInterface deletes a network interface configuration for a specific node. -func (c *Client) DeleteNetworkInterface(ctx context.Context, iface string) error { - err := c.DoRequest( - ctx, - http.MethodDelete, - c.ExpandPath(fmt.Sprintf("network/%s", url.PathEscape(iface))), - nil, - nil, - ) - if err != nil { - return fmt.Errorf("failed to delete network interface \"%s\" for node \"%s\": %w", - iface, c.NodeName, err, - ) - } - - return nil -} diff --git a/proxmox/nodes/network_types.go b/proxmox/nodes/network_types.go deleted file mode 100644 index 59e393e1..00000000 --- a/proxmox/nodes/network_types.go +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// NetworkInterfaceListResponseBody contains the body from a node network interface list response. -type NetworkInterfaceListResponseBody struct { - Data []*NetworkInterfaceListResponseData `json:"data,omitempty"` -} - -// NetworkInterfaceListResponseData contains the data from a node network interface list response. -type NetworkInterfaceListResponseData struct { - // There seems to be inconsistency in the APIs between certain versions of Proxmox. - // See https://github.com/bpg/terraform-provider-proxmox/issues/410 - // BridgeFD *int `json:"bridge_fd,omitempty"` - - Active *types.CustomBool `json:"active,omitempty"` - Address *string `json:"address,omitempty"` - Address6 *string `json:"address6,omitempty"` - Autostart *types.CustomBool `json:"autostart,omitempty"` - BridgePorts *string `json:"bridge_ports,omitempty"` - BridgeSTP *string `json:"bridge_stp,omitempty"` - BridgeVIDs *string `json:"bridge_vids,omitempty"` - BridgeVLANAware *types.CustomBool `json:"bridge_vlan_aware,omitempty"` - CIDR *string `json:"cidr,omitempty"` - CIDR6 *string `json:"cidr6,omitempty"` - Comments *string `json:"comments,omitempty"` - Exists *types.CustomBool `json:"exists,omitempty"` - Families *[]string `json:"families,omitempty"` - Gateway *string `json:"gateway,omitempty"` - Gateway6 *string `json:"gateway6,omitempty"` - Iface string `json:"iface"` - MethodIPv4 *string `json:"method,omitempty"` - MethodIPv6 *string `json:"method6,omitempty"` - MTU *string `json:"mtu,omitempty"` - Netmask *string `json:"netmask,omitempty"` - VLANID *string `json:"vlan-id,omitempty"` - VLANRawDevice *string `json:"vlan-raw-device,omitempty"` - Priority int `json:"priority"` - Type string `json:"type"` -} - -// NetworkInterfaceCreateUpdateRequestBody contains the body for a node network interface create / update request. -type NetworkInterfaceCreateUpdateRequestBody struct { - Iface string `json:"iface" url:"iface"` - Type string `json:"type" url:"type"` - - Address *string `json:"address,omitempty" url:"address,omitempty"` - Address6 *string `json:"address6,omitempty" url:"address6,omitempty"` - Autostart *types.CustomBool `json:"autostart,omitempty" url:"autostart,omitempty,int"` - BondPrimary *string `json:"bond-primary,omitempty" url:"bond-primary,omitempty"` - BondMode *string `json:"bond_mode,omitempty" url:"bond_mode,omitempty"` - BondXmitHashPolicy *string `json:"bond_xmit_hash_policy,omitempty" url:"bond_xmit_hash_policy,omitempty"` - BridgePorts *string `json:"bridge_ports,omitempty" url:"bridge_ports,omitempty"` - BridgeVLANAware *types.CustomBool `json:"bridge_vlan_aware,omitempty" url:"bridge_vlan_aware,omitempty,int"` - CIDR *string `json:"cidr,omitempty" url:"cidr,omitempty"` - CIDR6 *string `json:"cidr6,omitempty" url:"cidr6,omitempty"` - Comments *string `json:"comments,omitempty" url:"comments,omitempty"` - Comments6 *string `json:"comments6,omitempty" url:"comments6,omitempty"` - Gateway *string `json:"gateway,omitempty" url:"gateway,omitempty"` - Gateway6 *string `json:"gateway6,omitempty" url:"gateway6,omitempty"` - Delete *[]string `json:"delete,omitempty" url:"delete,omitempty"` - MTU *int64 `json:"mtu,omitempty" url:"mtu,omitempty"` - Netmask *string `json:"netmask,omitempty" url:"netmask,omitempty"` - Netmask6 *string `json:"netmask6,omitempty" url:"netmask6,omitempty"` - OVSBonds *string `json:"ovs_bonds,omitempty" url:"ovs_bonds,omitempty"` - OVSBridge *string `json:"ovs_bridge,omitempty" url:"ovs_bridge,omitempty"` - OVSOptions *string `json:"ovs_options,omitempty" url:"ovs_options,omitempty"` - OVSPorts *string `json:"ovs_ports,omitempty" url:"ovs_ports,omitempty"` - OVSTag *string `json:"ovs_tag,omitempty" url:"ovs_tag,omitempty"` - Slaves *string `json:"slaves,omitempty" url:"slaves,omitempty"` - VLANID *int64 `json:"vlan_id,omitempty" url:"vlan_id,omitempty"` - VLANRawDevice *string `json:"vlan-raw-device,omitempty" url:"vlan-raw-device,omitempty"` -} - -// ReloadNetworkResponseBody contains the body from a node network reload response. -type ReloadNetworkResponseBody struct { - Data *string `json:"data,omitempty"` -} diff --git a/proxmox/nodes/nodes.go b/proxmox/nodes/nodes.go deleted file mode 100644 index d11519ee..00000000 --- a/proxmox/nodes/nodes.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "context" - "fmt" - "net/http" - "sort" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// ListNodes retrieves a list of nodes. -func (c *Client) ListNodes(ctx context.Context) ([]*ListResponseData, error) { - resBody := &ListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, "nodes", nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get nodes: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].Name < resBody.Data[j].Name - }) - - return resBody.Data, nil -} - -// GetTime retrieves the time information for a node. -func (c *Client) GetTime(ctx context.Context) (*GetTimeResponseData, error) { - resBody := &GetTimeResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("time"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get time information for node \"%s\": %w", c.NodeName, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// UpdateTime updates the time on a node. -func (c *Client) UpdateTime(ctx context.Context, d *UpdateTimeRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("time"), d, nil) - if err != nil { - return fmt.Errorf("failed to update node time: %w", err) - } - - return nil -} diff --git a/proxmox/nodes/nodes_types.go b/proxmox/nodes/nodes_types.go deleted file mode 100644 index c86da585..00000000 --- a/proxmox/nodes/nodes_types.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "encoding/json" - "fmt" - "net/url" - - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// CustomCommands contains an array of commands to execute. -type CustomCommands []string - -// ExecuteRequestBody contains the data for a node execute request. -type ExecuteRequestBody struct { - Commands CustomCommands `json:"commands" url:"commands"` -} - -// GetTimeResponseBody contains the body from a node time zone get response. -type GetTimeResponseBody struct { - Data *GetTimeResponseData `json:"data,omitempty"` -} - -// GetTimeResponseData contains the data from a node list response. -type GetTimeResponseData struct { - LocalTime types.CustomTimestamp `json:"localtime"` - TimeZone string `json:"timezone"` - UTCTime types.CustomTimestamp `json:"time"` -} - -// ListResponseBody contains the body from a node list response. -type ListResponseBody struct { - Data []*ListResponseData `json:"data,omitempty"` -} - -// ListResponseData contains the data from a node list response. -type ListResponseData struct { - CPUCount *int `json:"maxcpu,omitempty"` - CPUUtilization *float64 `json:"cpu,omitempty"` - MemoryAvailable *int `json:"maxmem,omitempty"` - MemoryUsed *int `json:"mem,omitempty"` - Name string `json:"node"` - SSLFingerprint *string `json:"ssl_fingerprint,omitempty"` - Status *string `json:"status"` - SupportLevel *string `json:"level,omitempty"` - Uptime *int `json:"uptime"` -} - -// UpdateTimeRequestBody contains the body for a node time update request. -type UpdateTimeRequestBody struct { - TimeZone string `json:"timezone" url:"timezone"` -} - -// EncodeValues converts a CustomCommands array to a JSON encoded URL value. -func (r CustomCommands) EncodeValues(key string, v *url.Values) error { - jsonArrayBytes, err := json.Marshal(r) - if err != nil { - return fmt.Errorf("error marshalling CustomCommands array: %w", err) - } - - v.Add(key, string(jsonArrayBytes)) - - return nil -} diff --git a/proxmox/nodes/storage.go b/proxmox/nodes/storage.go deleted file mode 100644 index 23437214..00000000 --- a/proxmox/nodes/storage.go +++ /dev/null @@ -1,280 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "context" - "fmt" - "io" - "mime/multipart" - "net/http" - "net/url" - "os" - "sort" - - "github.com/hashicorp/terraform-plugin-log/tflog" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// DeleteDatastoreFile deletes a file in a datastore. -func (c *Client) DeleteDatastoreFile( - ctx context.Context, - datastoreID, volumeID string, -) error { - err := c.DoRequest( - ctx, - http.MethodDelete, - c.ExpandPath( - fmt.Sprintf( - "storage/%s/content/%s", - url.PathEscape(datastoreID), - url.PathEscape(volumeID), - ), - ), - nil, - nil, - ) - if err != nil { - return fmt.Errorf("error deleting file %s from datastore %s: %w", volumeID, datastoreID, err) - } - - return nil -} - -// GetDatastoreStatus gets status information for a given datastore. -func (c *Client) GetDatastoreStatus( - ctx context.Context, - datastoreID string, -) (*DatastoreGetStatusResponseData, error) { - resBody := &DatastoreGetStatusResponseBody{} - - err := c.DoRequest( - ctx, - http.MethodGet, - c.ExpandPath( - fmt.Sprintf( - "storage/%s/status", - url.PathEscape(datastoreID), - ), - ), - nil, - resBody, - ) - if err != nil { - return nil, fmt.Errorf("error retrieving status for datastore %s: %w", datastoreID, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// ListDatastoreFiles retrieves a list of the files in a datastore. -func (c *Client) ListDatastoreFiles( - ctx context.Context, - datastoreID string, -) ([]*DatastoreFileListResponseData, error) { - resBody := &DatastoreFileListResponseBody{} - - err := c.DoRequest( - ctx, - http.MethodGet, - c.ExpandPath( - fmt.Sprintf( - "storage/%s/content", - url.PathEscape(datastoreID), - ), - ), - nil, - resBody, - ) - if err != nil { - return nil, fmt.Errorf("error retrieving files from datastore %s: %w", datastoreID, err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].VolumeID < resBody.Data[j].VolumeID - }) - - return resBody.Data, nil -} - -// ListDatastores retrieves a list of nodes. -func (c *Client) ListDatastores( - ctx context.Context, - d *DatastoreListRequestBody, -) ([]*DatastoreListResponseData, error) { - resBody := &DatastoreListResponseBody{} - - err := c.DoRequest( - ctx, - http.MethodGet, - c.ExpandPath("storage"), - d, - resBody, - ) - if err != nil { - return nil, fmt.Errorf("error retrieving datastores: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].ID < resBody.Data[j].ID - }) - - return resBody.Data, nil -} - -// APIUpload uploads a file to a datastore using the Proxmox API. -func (c *Client) APIUpload( - ctx context.Context, - datastoreID string, - d *api.FileUploadRequest, -) (*DatastoreUploadResponseBody, error) { - tflog.Debug(ctx, "uploading file to datastore using PVE API", map[string]interface{}{ - "file_name": d.FileName, - "content_type": d.ContentType, - }) - - r, w := io.Pipe() - - defer func(r *io.PipeReader) { - err := r.Close() - if err != nil { - tflog.Error(ctx, "failed to close pipe reader", map[string]interface{}{ - "error": err, - }) - } - }(r) - - m := multipart.NewWriter(w) - - go func() { - defer func(w *io.PipeWriter) { - err := w.Close() - if err != nil { - tflog.Error(ctx, "failed to close pipe writer", map[string]interface{}{ - "error": err, - }) - } - }(w) - defer func(m *multipart.Writer) { - err := m.Close() - if err != nil { - tflog.Error(ctx, "failed to close multipart writer", map[string]interface{}{ - "error": err, - }) - } - }(m) - - err := m.WriteField("content", d.ContentType) - if err != nil { - tflog.Error(ctx, "failed to write 'content' field", map[string]interface{}{ - "error": err, - }) - - return - } - - part, err := m.CreateFormFile("filename", d.FileName) - if err != nil { - return - } - - _, err = io.Copy(part, d.File) - - if err != nil { - return - } - }() - - // We need to store the multipart content in a temporary file to avoid using high amounts of memory. - // This is necessary due to Proxmox VE not supporting chunked transfers in v6.1 and earlier versions. - tempMultipartFile, err := os.CreateTemp("", "multipart") - if err != nil { - return nil, fmt.Errorf("failed to create temporary file: %w", err) - } - - tempMultipartFileName := tempMultipartFile.Name() - - _, err = io.Copy(tempMultipartFile, r) - if err != nil { - return nil, fmt.Errorf("failed to copy multipart data to temporary file: %w", err) - } - - err = tempMultipartFile.Close() - if err != nil { - return nil, fmt.Errorf("failed to close temporary file: %w", err) - } - - defer func(name string) { - e := os.Remove(name) - if e != nil { - tflog.Error(ctx, "failed to remove temporary file", map[string]interface{}{ - "error": e, - }) - } - }(tempMultipartFileName) - - // Now that the multipart data is stored in a file, we can go ahead and do an HTTP POST request. - fileReader, err := os.Open(tempMultipartFileName) - if err != nil { - return nil, fmt.Errorf("failed to open temporary file: %w", err) - } - - defer func(fileReader *os.File) { - e := fileReader.Close() - if e != nil { - tflog.Error(ctx, "failed to close file reader", map[string]interface{}{ - "error": e, - }) - } - }(fileReader) - - fileInfo, err := fileReader.Stat() - if err != nil { - return nil, fmt.Errorf("failed to get file info: %w", err) - } - - fileSize := fileInfo.Size() - - reqBody := &api.MultiPartData{ - Boundary: m.Boundary(), - Reader: fileReader, - Size: &fileSize, - } - - resBody := &DatastoreUploadResponseBody{} - err = c.DoRequest( - ctx, - http.MethodPost, - c.ExpandPath( - fmt.Sprintf( - "storage/%s/upload", - url.PathEscape(datastoreID), - ), - ), - reqBody, - resBody, - ) - - if err != nil { - return nil, fmt.Errorf("error uploading file to datastore %s: %w", datastoreID, err) - } - - return resBody, nil -} diff --git a/proxmox/nodes/storage_types.go b/proxmox/nodes/storage_types.go deleted file mode 100644 index ac54232b..00000000 --- a/proxmox/nodes/storage_types.go +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package nodes - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// DatastoreFileListResponseBody contains the body from a datastore content list response. -type DatastoreFileListResponseBody struct { - Data []*DatastoreFileListResponseData `json:"data,omitempty"` -} - -// DatastoreFileListResponseData contains the data from a datastore content list response. -type DatastoreFileListResponseData struct { - ContentType string `json:"content"` - FileFormat string `json:"format"` - FileSize int `json:"size"` - ParentVolumeID *string `json:"parent,omitempty"` - SpaceUsed *int `json:"used,omitempty"` - VMID *int `json:"vmid,omitempty"` - VolumeID string `json:"volid"` -} - -// DatastoreGetStatusResponseBody contains the body from a datastore status get request. -type DatastoreGetStatusResponseBody struct { - Data *DatastoreGetStatusResponseData `json:"data,omitempty"` -} - -// DatastoreGetStatusResponseData contains the data from a datastore status get request. -type DatastoreGetStatusResponseData struct { - Active *types.CustomBool `json:"active,omitempty"` - AvailableBytes *int64 `json:"avail,omitempty"` - Content *types.CustomCommaSeparatedList `json:"content,omitempty" url:"content,omitempty,comma"` - Enabled *types.CustomBool `json:"enabled,omitempty"` - Shared *types.CustomBool `json:"shared,omitempty"` - TotalBytes *int64 `json:"total,omitempty"` - Type *string `json:"type,omitempty"` - UsedBytes *int64 `json:"used,omitempty"` -} - -// DatastoreListRequestBody contains the body for a datastore list request. -type DatastoreListRequestBody struct { - ContentTypes types.CustomCommaSeparatedList `json:"content,omitempty" url:"content,omitempty,comma"` - Enabled *types.CustomBool `json:"enabled,omitempty" url:"enabled,omitempty,int"` - Format *types.CustomBool `json:"format,omitempty" url:"format,omitempty,int"` - ID *string `json:"storage,omitempty" url:"storage,omitempty"` - Target *string `json:"target,omitempty" url:"target,omitempty"` -} - -// DatastoreListResponseBody contains the body from a datastore list response. -type DatastoreListResponseBody struct { - Data []*DatastoreListResponseData `json:"data,omitempty"` -} - -// DatastoreListResponseData contains the data from a datastore list response. -type DatastoreListResponseData struct { - Active *types.CustomBool `json:"active,omitempty"` - ContentTypes *types.CustomCommaSeparatedList `json:"content,omitempty"` - Enabled *types.CustomBool `json:"enabled,omitempty"` - ID string `json:"storage,omitempty"` - Shared *types.CustomBool `json:"shared,omitempty"` - SpaceAvailable *int `json:"avail,omitempty"` - SpaceTotal *int `json:"total,omitempty"` - SpaceUsed *int `json:"used,omitempty"` - SpaceUsedPercentage *float64 `json:"used_fraction,omitempty"` - Type string `json:"type,omitempty"` -} - -// DatastoreUploadResponseBody contains the body from a datastore upload response. -type DatastoreUploadResponseBody struct { - UploadID *string `json:"data,omitempty"` -} diff --git a/proxmox/nodes/tasks/client.go b/proxmox/nodes/tasks/client.go deleted file mode 100644 index b48105f4..00000000 --- a/proxmox/nodes/tasks/client.go +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package tasks - -import ( - "fmt" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Client is an interface for performing requests against the Proxmox 'tasks' API. -type Client struct { - api.Client -} - -// ExpandPath expands a path relative to the client's base path. -func (c *Client) ExpandPath(path string) string { - return c.Client.ExpandPath( - fmt.Sprintf("tasks/%s", path), - ) -} diff --git a/proxmox/nodes/tasks/tasks.go b/proxmox/nodes/tasks/tasks.go deleted file mode 100644 index 3e7e0a96..00000000 --- a/proxmox/nodes/tasks/tasks.go +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package tasks - -import ( - "context" - "fmt" - "net/http" - "net/url" - "time" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// GetTaskStatus retrieves the status of a task. -func (c *Client) GetTaskStatus(ctx context.Context, upid string) (*GetTaskStatusResponseData, error) { - resBody := &GetTaskStatusResponseBody{} - - err := c.DoRequest( - ctx, - http.MethodGet, - c.ExpandPath(fmt.Sprintf("%s/status", url.PathEscape(upid))), - nil, - resBody, - ) - if err != nil { - return nil, fmt.Errorf("error retrievinf task status: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// WaitForTask waits for a specific task to complete. -func (c *Client) WaitForTask(ctx context.Context, upid string, timeoutSec, delaySec int) error { - timeDelay := int64(delaySec) - timeMax := float64(timeoutSec) - timeStart := time.Now() - timeElapsed := timeStart.Sub(timeStart) - - for timeElapsed.Seconds() < timeMax { - if int64(timeElapsed.Seconds())%timeDelay == 0 { - status, err := c.GetTaskStatus(ctx, upid) - if err != nil { - return err - } - - if status.Status != "running" { - if status.ExitCode != "OK" { - return fmt.Errorf( - "task \"%s\" failed to complete with exit code: %s", - upid, - status.ExitCode, - ) - } - - return nil - } - - time.Sleep(1 * time.Second) - } - - time.Sleep(200 * time.Millisecond) - - timeElapsed = time.Since(timeStart) - - if ctx.Err() != nil { - return fmt.Errorf( - "context error while waiting for task \"%s\" to complete: %w", - upid, ctx.Err(), - ) - } - } - - return fmt.Errorf( - "timeout while waiting for task \"%s\" to complete", - upid, - ) -} diff --git a/proxmox/nodes/tasks/tasks_types.go b/proxmox/nodes/tasks/tasks_types.go deleted file mode 100644 index 419b76eb..00000000 --- a/proxmox/nodes/tasks/tasks_types.go +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package tasks - -// GetTaskStatusResponseBody contains the body from a node get task status response. -type GetTaskStatusResponseBody struct { - Data *GetTaskStatusResponseData `json:"data,omitempty"` -} - -// GetTaskStatusResponseData contains the data from a node get task status response. -type GetTaskStatusResponseData struct { - PID int `json:"pid,omitempty"` - Status string `json:"status,omitempty"` - ExitCode string `json:"exitstatus,omitempty"` -} diff --git a/proxmox/nodes/vms/client.go b/proxmox/nodes/vms/client.go deleted file mode 100644 index ff45ce23..00000000 --- a/proxmox/nodes/vms/client.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package vms - -import ( - "fmt" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/tasks" - vmfirewall "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms/firewall" -) - -// Client is an interface for accessing the Proxmox VM API. -type Client struct { - api.Client - VMID int -} - -func (c *Client) basePath() string { - return c.Client.ExpandPath("qemu") -} - -// ExpandPath expands a relative path to a full VM API path. -func (c *Client) ExpandPath(path string) string { - ep := fmt.Sprintf("%s/%d", c.basePath(), c.VMID) - if path != "" { - ep = fmt.Sprintf("%s/%s", ep, path) - } - - return ep -} - -// Tasks returns a client for managing VM tasks. -func (c *Client) Tasks() *tasks.Client { - return &tasks.Client{ - Client: c.Client, - } -} - -// Firewall returns a client for managing the VM firewall. -func (c *Client) Firewall() firewall.API { - return &vmfirewall.Client{ - Client: firewall.Client{Client: c}, - } -} diff --git a/proxmox/nodes/vms/firewall/client.go b/proxmox/nodes/vms/firewall/client.go deleted file mode 100644 index 442d8007..00000000 --- a/proxmox/nodes/vms/firewall/client.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package firewall - -import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" -) - -// Client is an interface for accessing the Proxmox VM firewall API. -type Client struct { - firewall.Client -} diff --git a/proxmox/nodes/vms/vms.go b/proxmox/nodes/vms/vms.go deleted file mode 100644 index 7735f6a6..00000000 --- a/proxmox/nodes/vms/vms.go +++ /dev/null @@ -1,581 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package vms - -import ( - "context" - "fmt" - "net" - "net/http" - "strings" - "time" - - "github.com/hashicorp/terraform-plugin-log/tflog" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// CloneVM clones a virtual machine. -func (c *Client) CloneVM(ctx context.Context, retries int, d *CloneRequestBody, timeout int) error { - var err error - - resBody := &MoveDiskResponseBody{} - - // just a guard in case someone sets retries to 0 unknowingly - if retries <= 0 { - retries = 1 - } - - for i := 0; i < retries; i++ { - err = c.DoRequest(ctx, http.MethodPost, c.ExpandPath("clone"), d, resBody) - - if err != nil { - return fmt.Errorf("error cloning VM: %w", err) - } - - if resBody.Data == nil { - return api.ErrNoDataObjectInResponse - } - - err = c.Tasks().WaitForTask(ctx, *resBody.Data, timeout, 5) - if err == nil { - return nil - } - - time.Sleep(10 * time.Second) - } - - if err != nil { - return fmt.Errorf("error waiting for VM clone: %w", err) - } - - return nil -} - -// CreateVM creates a virtual machine. -func (c *Client) CreateVM(ctx context.Context, d *CreateRequestBody, timeout int) error { - taskID, err := c.CreateVMAsync(ctx, d) - if err != nil { - return err - } - - err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 1) - - if err != nil { - return fmt.Errorf("error waiting for VM creation: %w", err) - } - - return nil -} - -// CreateVMAsync creates a virtual machine asynchronously. -func (c *Client) CreateVMAsync(ctx context.Context, d *CreateRequestBody) (*string, error) { - resBody := &CreateResponseBody{} - - err := c.DoRequest(ctx, http.MethodPost, c.basePath(), d, resBody) - if err != nil { - return nil, fmt.Errorf("error creating VM: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// DeleteVM deletes a virtual machine. -func (c *Client) DeleteVM(ctx context.Context) error { - err := c.DoRequest(ctx, http.MethodDelete, c.ExpandPath("?destroy-unreferenced-disks=1&purge=1"), nil, nil) - if err != nil { - return fmt.Errorf("error deleting VM: %w", err) - } - - return nil -} - -// GetVM retrieves a virtual machine. -func (c *Client) GetVM(ctx context.Context) (*GetResponseData, error) { - resBody := &GetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("config"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving VM: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// GetVMNetworkInterfacesFromAgent retrieves the network interfaces reported by the QEMU agent. -func (c *Client) GetVMNetworkInterfacesFromAgent(ctx context.Context) (*GetQEMUNetworkInterfacesResponseData, error) { - resBody := &GetQEMUNetworkInterfacesResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("agent/network-get-interfaces"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving VM network interfaces from agent: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// GetVMStatus retrieves the status for a virtual machine. -func (c *Client) GetVMStatus(ctx context.Context) (*GetStatusResponseData, error) { - resBody := &GetStatusResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.ExpandPath("status/current"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving VM status: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// MigrateVM migrates a virtual machine. -func (c *Client) MigrateVM(ctx context.Context, d *MigrateRequestBody, timeout int) error { - taskID, err := c.MigrateVMAsync(ctx, d) - if err != nil { - return err - } - - err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5) - if err != nil { - return fmt.Errorf("error waiting for VM migration: %w", err) - } - - return nil -} - -// MigrateVMAsync migrates a virtual machine asynchronously. -func (c *Client) MigrateVMAsync(ctx context.Context, d *MigrateRequestBody) (*string, error) { - resBody := &MigrateResponseBody{} - - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("migrate"), d, resBody) - if err != nil { - return nil, fmt.Errorf("error migrating VM: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// MoveVMDisk moves a virtual machine disk. -func (c *Client) MoveVMDisk(ctx context.Context, d *MoveDiskRequestBody, timeout int) error { - taskID, err := c.MoveVMDiskAsync(ctx, d) - if err != nil { - if strings.Contains(err.Error(), "you can't move to the same storage with same format") { - // if someone tries to move to the same storage, the move is considered to be successful - return nil - } - - return err - } - - err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5) - if err != nil { - return fmt.Errorf("error waiting for VM disk move: %w", err) - } - - return nil -} - -// MoveVMDiskAsync moves a virtual machine disk asynchronously. -func (c *Client) MoveVMDiskAsync(ctx context.Context, d *MoveDiskRequestBody) (*string, error) { - resBody := &MoveDiskResponseBody{} - - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("move_disk"), d, resBody) - if err != nil { - return nil, fmt.Errorf("error moving VM disk: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// ListVMs retrieves a list of virtual machines. -func (c *Client) ListVMs(ctx context.Context) ([]*ListResponseData, error) { - resBody := &ListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, c.basePath(), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error retrieving VMs: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// RebootVM reboots a virtual machine. -func (c *Client) RebootVM(ctx context.Context, d *RebootRequestBody, timeout int) error { - taskID, err := c.RebootVMAsync(ctx, d) - if err != nil { - return err - } - - err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5) - if err != nil { - return fmt.Errorf("error waiting for VM reboot: %w", err) - } - - return nil -} - -// RebootVMAsync reboots a virtual machine asynchronously. -func (c *Client) RebootVMAsync(ctx context.Context, d *RebootRequestBody) (*string, error) { - resBody := &RebootResponseBody{} - - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/reboot"), d, resBody) - if err != nil { - return nil, fmt.Errorf("error rebooting VM: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// ResizeVMDisk resizes a virtual machine disk. -func (c *Client) ResizeVMDisk(ctx context.Context, d *ResizeDiskRequestBody) error { - var err error - - tflog.Debug(ctx, "resize disk", map[string]interface{}{ - "disk": d.Disk, - "size": d.Size, - }) - - for i := 0; i < 5; i++ { - err = c.DoRequest( - ctx, - http.MethodPut, - c.ExpandPath("resize"), - d, - nil, - ) - if err == nil { - return nil - } - - tflog.Debug(ctx, "resize disk failed", map[string]interface{}{ - "retry": i, - }) - time.Sleep(5 * time.Second) - - if ctx.Err() != nil { - return fmt.Errorf("error resizing VM disk: %w", ctx.Err()) - } - } - - if err != nil { - return fmt.Errorf("error resizing VM disk: %w", err) - } - - return nil -} - -// ShutdownVM shuts down a virtual machine. -func (c *Client) ShutdownVM(ctx context.Context, d *ShutdownRequestBody, timeout int) error { - taskID, err := c.ShutdownVMAsync(ctx, d) - if err != nil { - return err - } - - err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5) - if err != nil { - return fmt.Errorf("error waiting for VM shutdown: %w", err) - } - - return nil -} - -// ShutdownVMAsync shuts down a virtual machine asynchronously. -func (c *Client) ShutdownVMAsync(ctx context.Context, d *ShutdownRequestBody) (*string, error) { - resBody := &ShutdownResponseBody{} - - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/shutdown"), d, resBody) - if err != nil { - return nil, fmt.Errorf("error shutting down VM: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// StartVM starts a virtual machine. -func (c *Client) StartVM(ctx context.Context, timeout int) error { - taskID, err := c.StartVMAsync(ctx) - if err != nil { - return err - } - - err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5) - if err != nil { - return fmt.Errorf("error waiting for VM start: %w", err) - } - - return nil -} - -// StartVMAsync starts a virtual machine asynchronously. -func (c *Client) StartVMAsync(ctx context.Context) (*string, error) { - resBody := &StartResponseBody{} - - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/start"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error starting VM: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// StopVM stops a virtual machine. -func (c *Client) StopVM(ctx context.Context, timeout int) error { - taskID, err := c.StopVMAsync(ctx) - if err != nil { - return err - } - - err = c.Tasks().WaitForTask(ctx, *taskID, timeout, 5) - if err != nil { - return fmt.Errorf("error waiting for VM stop: %w", err) - } - - return nil -} - -// StopVMAsync stops a virtual machine asynchronously. -func (c *Client) StopVMAsync(ctx context.Context) (*string, error) { - resBody := &StopResponseBody{} - - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("status/stop"), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error stopping VM: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// UpdateVM updates a virtual machine. -func (c *Client) UpdateVM(ctx context.Context, d *UpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, c.ExpandPath("config"), d, nil) - if err != nil { - return fmt.Errorf("error updating VM: %w", err) - } - - return nil -} - -// UpdateVMAsync updates a virtual machine asynchronously. -func (c *Client) UpdateVMAsync(ctx context.Context, d *UpdateRequestBody) (*string, error) { - resBody := &UpdateAsyncResponseBody{} - - err := c.DoRequest(ctx, http.MethodPost, c.ExpandPath("config"), d, resBody) - if err != nil { - return nil, fmt.Errorf("error updating VM: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} - -// WaitForNetworkInterfacesFromVMAgent waits for a virtual machine's QEMU agent to publish the network interfaces. -func (c *Client) WaitForNetworkInterfacesFromVMAgent( - ctx context.Context, - timeout int, - delay int, - waitForIP bool, -) (*GetQEMUNetworkInterfacesResponseData, error) { - timeDelay := int64(delay) - timeMax := float64(timeout) - timeStart := time.Now() - timeElapsed := timeStart.Sub(timeStart) - - for timeElapsed.Seconds() < timeMax { - //nolint:nestif - if int64(timeElapsed.Seconds())%timeDelay == 0 { - data, err := c.GetVMNetworkInterfacesFromAgent(ctx) - - if err == nil && data != nil && data.Result != nil { - hasAnyGlobalUnicast := false - - if waitForIP { - for _, nic := range *data.Result { - if nic.Name == "lo" { - continue - } - - if nic.IPAddresses == nil || - (nic.IPAddresses != nil && len(*nic.IPAddresses) == 0) { - break - } - - for _, addr := range *nic.IPAddresses { - if ip := net.ParseIP(addr.Address); ip != nil && ip.IsGlobalUnicast() { - hasAnyGlobalUnicast = true - } - } - } - } - - if hasAnyGlobalUnicast { - return data, err - } - } - - time.Sleep(1 * time.Second) - } - - time.Sleep(200 * time.Millisecond) - - timeElapsed = time.Since(timeStart) - - if ctx.Err() != nil { - return nil, fmt.Errorf("error waiting for VM network interfaces: %w", ctx.Err()) - } - } - - return nil, fmt.Errorf( - "timeout while waiting for the QEMU agent on VM \"%d\" to publish the network interfaces", - c.VMID, - ) -} - -// WaitForNoNetworkInterfacesFromVMAgent waits for a virtual machine's QEMU agent to unpublish the network interfaces. -func (c *Client) WaitForNoNetworkInterfacesFromVMAgent(ctx context.Context, timeout int, delay int) error { - timeDelay := int64(delay) - timeMax := float64(timeout) - timeStart := time.Now() - timeElapsed := timeStart.Sub(timeStart) - - for timeElapsed.Seconds() < timeMax { - if int64(timeElapsed.Seconds())%timeDelay == 0 { - _, err := c.GetVMNetworkInterfacesFromAgent(ctx) - if err == nil { - return nil - } - - time.Sleep(1 * time.Second) - } - - time.Sleep(200 * time.Millisecond) - - timeElapsed = time.Since(timeStart) - - if ctx.Err() != nil { - return fmt.Errorf("error waiting for VM network interfaces: %w", ctx.Err()) - } - } - - return fmt.Errorf( - "timeout while waiting for the QEMU agent on VM \"%d\" to unpublish the network interfaces", - c.VMID, - ) -} - -// WaitForVMConfigUnlock waits for a virtual machine configuration to become unlocked. -func (c *Client) WaitForVMConfigUnlock(ctx context.Context, timeout int, delay int, ignoreErrorResponse bool) error { - timeDelay := int64(delay) - timeMax := float64(timeout) - timeStart := time.Now() - timeElapsed := timeStart.Sub(timeStart) - - for timeElapsed.Seconds() < timeMax { - if int64(timeElapsed.Seconds())%timeDelay == 0 { - data, err := c.GetVMStatus(ctx) - - if err != nil { - if !ignoreErrorResponse { - return err - } - } else if data.Lock == nil || *data.Lock == "" { - return nil - } - - time.Sleep(1 * time.Second) - } - - time.Sleep(200 * time.Millisecond) - - timeElapsed = time.Since(timeStart) - - if ctx.Err() != nil { - return fmt.Errorf("error waiting for VM configuration to become unlocked: %w", ctx.Err()) - } - } - - return fmt.Errorf("timeout while waiting for VM \"%d\" configuration to become unlocked", c.VMID) -} - -// WaitForVMState waits for a virtual machine to reach a specific state. -func (c *Client) WaitForVMState(ctx context.Context, state string, timeout int, delay int) error { - state = strings.ToLower(state) - - timeDelay := int64(delay) - timeMax := float64(timeout) - timeStart := time.Now() - timeElapsed := timeStart.Sub(timeStart) - - for timeElapsed.Seconds() < timeMax { - if int64(timeElapsed.Seconds())%timeDelay == 0 { - data, err := c.GetVMStatus(ctx) - if err != nil { - return err - } - - if data.Status == state { - return nil - } - - time.Sleep(1 * time.Second) - } - - time.Sleep(200 * time.Millisecond) - - timeElapsed = time.Since(timeStart) - - if ctx.Err() != nil { - return fmt.Errorf("error waiting for VM state: %w", ctx.Err()) - } - } - - return fmt.Errorf("timeout while waiting for VM \"%d\" to enter the state \"%s\"", c.VMID, state) -} diff --git a/proxmox/nodes/vms/vms_types.go b/proxmox/nodes/vms/vms_types.go deleted file mode 100644 index db6f93b9..00000000 --- a/proxmox/nodes/vms/vms_types.go +++ /dev/null @@ -1,1852 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package vms - -import ( - "encoding/json" - "errors" - "fmt" - "net/url" - "path/filepath" - "strconv" - "strings" - - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// CustomAgent handles QEMU agent parameters. -type CustomAgent struct { - Enabled *types.CustomBool `json:"enabled,omitempty" url:"enabled,int"` - TrimClonedDisks *types.CustomBool `json:"fstrim_cloned_disks" url:"fstrim_cloned_disks,int"` - Type *string `json:"type" url:"type"` -} - -// CustomAudioDevice handles QEMU audio parameters. -type CustomAudioDevice struct { - Device string `json:"device" url:"device"` - Driver *string `json:"driver" url:"driver"` - Enabled bool `json:"-" url:"-"` -} - -// CustomAudioDevices handles QEMU audio device parameters. -type CustomAudioDevices []CustomAudioDevice - -// CustomBoot handles QEMU boot parameters. -type CustomBoot struct { - Order *[]string `json:"order,omitempty" url:"order,omitempty,semicolon"` -} - -// CustomCloudInitConfig handles QEMU cloud-init parameters. -type CustomCloudInitConfig struct { - Files *CustomCloudInitFiles `json:"cicustom,omitempty" url:"cicustom,omitempty"` - IPConfig []CustomCloudInitIPConfig `json:"ipconfig,omitempty" url:"ipconfig,omitempty,numbered"` - Nameserver *string `json:"nameserver,omitempty" url:"nameserver,omitempty"` - Password *string `json:"cipassword,omitempty" url:"cipassword,omitempty"` - SearchDomain *string `json:"searchdomain,omitempty" url:"searchdomain,omitempty"` - SSHKeys *CustomCloudInitSSHKeys `json:"sshkeys,omitempty" url:"sshkeys,omitempty"` - Type *string `json:"citype,omitempty" url:"citype,omitempty"` - Username *string `json:"ciuser,omitempty" url:"ciuser,omitempty"` -} - -// CustomCloudInitFiles handles QEMU cloud-init custom files parameters. -type CustomCloudInitFiles struct { - MetaVolume *string `json:"meta,omitempty" url:"meta,omitempty"` - NetworkVolume *string `json:"network,omitempty" url:"network,omitempty"` - UserVolume *string `json:"user,omitempty" url:"user,omitempty"` - VendorVolume *string `json:"vendor,omitempty" url:"vendor,omitempty"` -} - -// CustomCloudInitIPConfig handles QEMU cloud-init IP configuration parameters. -type CustomCloudInitIPConfig struct { - GatewayIPv4 *string `json:"gw,omitempty" url:"gw,omitempty"` - GatewayIPv6 *string `json:"gw6,omitempty" url:"gw6,omitempty"` - IPv4 *string `json:"ip,omitempty" url:"ip,omitempty"` - IPv6 *string `json:"ip6,omitempty" url:"ip6,omitempty"` -} - -// CustomCloudInitSSHKeys handles QEMU cloud-init SSH keys parameters. -type CustomCloudInitSSHKeys []string - -// CustomCPUEmulation handles QEMU CPU emulation parameters. -type CustomCPUEmulation struct { - Flags *[]string `json:"flags,omitempty" url:"flags,omitempty,semicolon"` - Hidden *types.CustomBool `json:"hidden,omitempty" url:"hidden,omitempty,int"` - HVVendorID *string `json:"hv-vendor-id,omitempty" url:"hv-vendor-id,omitempty"` - Type string `json:"cputype,omitempty" url:"cputype,omitempty"` -} - -// CustomEFIDisk handles QEMU EFI disk parameters. -type CustomEFIDisk struct { - FileVolume string `json:"file" url:"file"` - Format *string `json:"format,omitempty" url:"format,omitempty"` - Type *string `json:"efitype,omitempty" url:"efitype,omitempty"` - PreEnrolledKeys *types.CustomBool `json:"pre-enrolled-keys,omitempty" url:"pre-enrolled-keys,omitempty,int"` -} - -// CustomNetworkDevice handles QEMU network device parameters. -type CustomNetworkDevice struct { - Model string `json:"model" url:"model"` - Bridge *string `json:"bridge,omitempty" url:"bridge,omitempty"` - Enabled bool `json:"-" url:"-"` - Firewall *types.CustomBool `json:"firewall,omitempty" url:"firewall,omitempty,int"` - LinkDown *types.CustomBool `json:"link_down,omitempty" url:"link_down,omitempty,int"` - MACAddress *string `json:"macaddr,omitempty" url:"macaddr,omitempty"` - Queues *int `json:"queues,omitempty" url:"queues,omitempty"` - RateLimit *float64 `json:"rate,omitempty" url:"rate,omitempty"` - Tag *int `json:"tag,omitempty" url:"tag,omitempty"` - MTU *int `json:"mtu,omitempty" url:"mtu,omitempty"` - Trunks []int `json:"trunks,omitempty" url:"trunks,omitempty"` -} - -// CustomNetworkDevices handles QEMU network device parameters. -type CustomNetworkDevices []CustomNetworkDevice - -// CustomNUMADevice handles QEMU NUMA device parameters. -type CustomNUMADevice struct { - CPUIDs []string `json:"cpus" url:"cpus,semicolon"` - HostNodeNames *[]string `json:"hostnodes,omitempty" url:"hostnodes,omitempty,semicolon"` - Memory *float64 `json:"memory,omitempty" url:"memory,omitempty"` - Policy *string `json:"policy,omitempty" url:"policy,omitempty"` -} - -// CustomNUMADevices handles QEMU NUMA device parameters. -type CustomNUMADevices []CustomNUMADevice - -// CustomPCIDevice handles QEMU host PCI device mapping parameters. -type CustomPCIDevice struct { - DeviceIDs []string `json:"host" url:"host,semicolon"` - MDev *string `json:"mdev,omitempty" url:"mdev,omitempty"` - PCIExpress *types.CustomBool `json:"pcie,omitempty" url:"pcie,omitempty,int"` - ROMBAR *types.CustomBool `json:"rombar,omitempty" url:"rombar,omitempty,int"` - ROMFile *string `json:"romfile,omitempty" url:"romfile,omitempty"` - XVGA *types.CustomBool `json:"x-vga,omitempty" url:"x-vga,omitempty,int"` -} - -// CustomPCIDevices handles QEMU host PCI device mapping parameters. -type CustomPCIDevices []CustomPCIDevice - -// CustomSerialDevices handles QEMU serial device parameters. -type CustomSerialDevices []string - -// CustomSharedMemory handles QEMU Inter-VM shared memory parameters. -type CustomSharedMemory struct { - Name *string `json:"name,omitempty" url:"name,omitempty"` - Size int `json:"size" url:"size"` -} - -// CustomSMBIOS handles QEMU SMBIOS parameters. -type CustomSMBIOS struct { - Base64 *types.CustomBool `json:"base64,omitempty" url:"base64,omitempty"` - Family *string `json:"family,omitempty" url:"family,omitempty"` - Manufacturer *string `json:"manufacturer,omitempty" url:"manufacturer,omitempty"` - Product *string `json:"product,omitempty" url:"product,omitempty"` - Serial *string `json:"serial,omitempty" url:"serial,omitempty"` - SKU *string `json:"sku,omitempty" url:"sku,omitempty"` - UUID *string `json:"uuid,omitempty" url:"uuid,omitempty"` - Version *string `json:"version,omitempty" url:"version,omitempty"` -} - -// CustomSpiceEnhancements handles QEMU spice enhancement parameters. -type CustomSpiceEnhancements struct { - FolderSharing *types.CustomBool `json:"foldersharing,omitempty" url:"foldersharing,omitempty"` - VideoStreaming *string `json:"videostreaming,omitempty" url:"videostreaming,omitempty"` -} - -// CustomStartupOrder handles QEMU startup order parameters. -type CustomStartupOrder struct { - Down *int `json:"down,omitempty" url:"down,omitempty"` - Order *int `json:"order,omitempty" url:"order,omitempty"` - Up *int `json:"up,omitempty" url:"up,omitempty"` -} - -// CustomStorageDevice handles QEMU SATA device parameters. -type CustomStorageDevice struct { - AIO *string `json:"aio,omitempty" url:"aio,omitempty"` - BackupEnabled *types.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` - BurstableReadSpeedMbps *int `json:"mbps_rd_max,omitempty" url:"mbps_rd_max,omitempty"` - BurstableWriteSpeedMbps *int `json:"mbps_wr_max,omitempty" url:"mbps_wr_max,omitempty"` - Discard *string `json:"discard,omitempty" url:"discard,omitempty"` - Enabled bool `json:"-" url:"-"` - FileVolume string `json:"file" url:"file"` - Format *string `json:"format,omitempty" url:"format,omitempty"` - IOThread *types.CustomBool `json:"iothread,omitempty" url:"iothread,omitempty,int"` - SSD *types.CustomBool `json:"ssd,omitempty" url:"ssd,omitempty,int"` - MaxReadSpeedMbps *int `json:"mbps_rd,omitempty" url:"mbps_rd,omitempty"` - MaxWriteSpeedMbps *int `json:"mbps_wr,omitempty" url:"mbps_wr,omitempty"` - Media *string `json:"media,omitempty" url:"media,omitempty"` - Size *types.DiskSize `json:"size,omitempty" url:"size,omitempty"` - Interface *string - ID *string - FileID *string - SizeInt *int -} - -// CustomStorageDevices handles QEMU SATA device parameters. -type CustomStorageDevices map[string]CustomStorageDevice - -// CustomUSBDevice handles QEMU USB device parameters. -type CustomUSBDevice struct { - HostDevice string `json:"host" url:"host"` - USB3 *types.CustomBool `json:"usb3,omitempty" url:"usb3,omitempty,int"` -} - -// CustomUSBDevices handles QEMU USB device parameters. -type CustomUSBDevices []CustomUSBDevice - -// CustomVGADevice handles QEMU VGA device parameters. -type CustomVGADevice struct { - Memory *int `json:"memory,omitempty" url:"memory,omitempty"` - Type *string `json:"type,omitempty" url:"type,omitempty"` -} - -// CustomVirtualIODevice handles QEMU VirtIO device parameters. -type CustomVirtualIODevice struct { - AIO *string `json:"aio,omitempty" url:"aio,omitempty"` - BackupEnabled *types.CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` - Enabled bool `json:"-" url:"-"` - FileVolume string `json:"file" url:"file"` -} - -// CustomVirtualIODevices handles QEMU VirtIO device parameters. -type CustomVirtualIODevices []CustomVirtualIODevice - -// CustomWatchdogDevice handles QEMU watchdog device parameters. -type CustomWatchdogDevice struct { - Action *string `json:"action,omitempty" url:"action,omitempty"` - Model *string `json:"model" url:"model"` -} - -// CloneRequestBody contains the data for an virtual machine clone request. -type CloneRequestBody struct { - BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - Description *string `json:"description,omitempty" url:"description,omitempty"` - FullCopy *types.CustomBool `json:"full,omitempty" url:"full,omitempty,int"` - Name *string `json:"name,omitempty" url:"name,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - SnapshotName *string `json:"snapname,omitempty" url:"snapname,omitempty"` - TargetNodeName *string `json:"target,omitempty" url:"target,omitempty"` - TargetStorage *string `json:"storage,omitempty" url:"storage,omitempty"` - TargetStorageFormat *string `json:"format,omitempty" url:"format,omitempty"` - VMIDNew int `json:"newid" url:"newid"` -} - -// CreateRequestBody contains the data for a virtual machine create request. -type CreateRequestBody struct { - ACPI *types.CustomBool `json:"acpi,omitempty" url:"acpi,omitempty,int"` - Agent *CustomAgent `json:"agent,omitempty" url:"agent,omitempty"` - AllowReboot *types.CustomBool `json:"reboot,omitempty" url:"reboot,omitempty,int"` - AudioDevices CustomAudioDevices `json:"audio,omitempty" url:"audio,omitempty"` - Autostart *types.CustomBool `json:"autostart,omitempty" url:"autostart,omitempty,int"` - BackupFile *string `json:"archive,omitempty" url:"archive,omitempty"` - BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - BIOS *string `json:"bios,omitempty" url:"bios,omitempty"` - Boot *CustomBoot `json:"boot,omitempty" url:"boot,omitempty"` - CDROM *string `json:"cdrom,omitempty" url:"cdrom,omitempty"` - CloudInitConfig *CustomCloudInitConfig `json:"cloudinit,omitempty" url:"cloudinit,omitempty"` - CPUArchitecture *string `json:"arch,omitempty" url:"arch,omitempty"` - CPUCores *int `json:"cores,omitempty" url:"cores,omitempty"` - CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty" url:"cpu,omitempty"` - CPULimit *int `json:"cpulimit,omitempty" url:"cpulimit,omitempty"` - CPUSockets *int `json:"sockets,omitempty" url:"sockets,omitempty"` - CPUUnits *int `json:"cpuunits,omitempty" url:"cpuunits,omitempty"` - DedicatedMemory *int `json:"memory,omitempty" url:"memory,omitempty"` - Delete []string `json:"delete,omitempty" url:"delete,omitempty,comma"` - DeletionProtection *types.CustomBool `json:"protection,omitempty" url:"force,omitempty,int"` - Description *string `json:"description,omitempty" url:"description,omitempty"` - EFIDisk *CustomEFIDisk `json:"efidisk0,omitempty" url:"efidisk0,omitempty"` - FloatingMemory *int `json:"balloon,omitempty" url:"balloon,omitempty"` - FloatingMemoryShares *int `json:"shares,omitempty" url:"shares,omitempty"` - Freeze *types.CustomBool `json:"freeze,omitempty" url:"freeze,omitempty,int"` - HookScript *string `json:"hookscript,omitempty" url:"hookscript,omitempty"` - Hotplug types.CustomCommaSeparatedList `json:"hotplug,omitempty" url:"hotplug,omitempty,comma"` - Hugepages *string `json:"hugepages,omitempty" url:"hugepages,omitempty"` - IDEDevices CustomStorageDevices `json:"ide,omitempty" url:",omitempty"` - KeyboardLayout *string `json:"keyboard,omitempty" url:"keyboard,omitempty"` - KVMArguments *string `json:"args,omitempty" url:"args,omitempty,space"` - KVMEnabled *types.CustomBool `json:"kvm,omitempty" url:"kvm,omitempty,int"` - LocalTime *types.CustomBool `json:"localtime,omitempty" url:"localtime,omitempty,int"` - Lock *string `json:"lock,omitempty" url:"lock,omitempty"` - Machine *string `json:"machine,omitempty" url:"machine,omitempty"` - MigrateDowntime *float64 `json:"migrate_downtime,omitempty" url:"migrate_downtime,omitempty"` - MigrateSpeed *int `json:"migrate_speed,omitempty" url:"migrate_speed,omitempty"` - Name *string `json:"name,omitempty" url:"name,omitempty"` - NetworkDevices CustomNetworkDevices `json:"net,omitempty" url:"net,omitempty"` - NUMADevices CustomNUMADevices `json:"numa_devices,omitempty" url:"numa,omitempty"` - NUMAEnabled *types.CustomBool `json:"numa,omitempty" url:"numa,omitempty,int"` - OSType *string `json:"ostype,omitempty" url:"ostype,omitempty"` - Overwrite *types.CustomBool `json:"force,omitempty" url:"force,omitempty,int"` - PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - Revert *string `json:"revert,omitempty" url:"revert,omitempty"` - SATADevices CustomStorageDevices `json:"sata,omitempty" url:"sata,omitempty"` - SCSIDevices CustomStorageDevices `json:"scsi,omitempty" url:"scsi,omitempty"` - SCSIHardware *string `json:"scsihw,omitempty" url:"scsihw,omitempty"` - SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"` - SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"` - SkipLock *types.CustomBool `json:"skiplock,omitempty" url:"skiplock,omitempty,int"` - SMBIOS *CustomSMBIOS `json:"smbios1,omitempty" url:"smbios1,omitempty"` - SpiceEnhancements *CustomSpiceEnhancements `json:"spice_enhancements,omitempty" url:"spice_enhancements,omitempty"` - StartDate *string `json:"startdate,omitempty" url:"startdate,omitempty"` - StartOnBoot *types.CustomBool `json:"onboot,omitempty" url:"onboot,omitempty,int"` - StartupOrder *CustomStartupOrder `json:"startup,omitempty" url:"startup,omitempty"` - TabletDeviceEnabled *types.CustomBool `json:"tablet,omitempty" url:"tablet,omitempty,int"` - Tags *string `json:"tags,omitempty" url:"tags,omitempty"` - Template *types.CustomBool `json:"template,omitempty" url:"template,omitempty,int"` - TimeDriftFixEnabled *types.CustomBool `json:"tdf,omitempty" url:"tdf,omitempty,int"` - USBDevices CustomUSBDevices `json:"usb,omitempty" url:"usb,omitempty"` - VGADevice *CustomVGADevice `json:"vga,omitempty" url:"vga,omitempty"` - VirtualCPUCount *int `json:"vcpus,omitempty" url:"vcpus,omitempty"` - VirtualIODevices CustomStorageDevices `json:"virtio,omitempty" url:"virtio,omitempty"` - VMGenerationID *string `json:"vmgenid,omitempty" url:"vmgenid,omitempty"` - VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"` - VMStateDatastoreID *string `json:"vmstatestorage,omitempty" url:"vmstatestorage,omitempty"` - WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"` -} - -// CreateResponseBody contains the body from a create response. -type CreateResponseBody struct { - Data *string `json:"data,omitempty"` -} - -// GetQEMUNetworkInterfacesResponseBody contains the body from a QEMU get network interfaces response. -type GetQEMUNetworkInterfacesResponseBody struct { - Data *GetQEMUNetworkInterfacesResponseData `json:"data,omitempty"` -} - -// GetQEMUNetworkInterfacesResponseData contains the data from a QEMU get network interfaces response. -type GetQEMUNetworkInterfacesResponseData struct { - Result *[]GetQEMUNetworkInterfacesResponseResult `json:"result,omitempty"` -} - -// GetQEMUNetworkInterfacesResponseResult contains the result from a QEMU get network interfaces response. -type GetQEMUNetworkInterfacesResponseResult struct { - MACAddress string `json:"hardware-address"` - Name string `json:"name"` - Statistics *GetQEMUNetworkInterfacesResponseResultStatistics `json:"statistics,omitempty"` - IPAddresses *[]GetQEMUNetworkInterfacesResponseResultIPAddress `json:"ip-addresses,omitempty"` -} - -// GetQEMUNetworkInterfacesResponseResultIPAddress contains the IP address from a QEMU get network interfaces response. -type GetQEMUNetworkInterfacesResponseResultIPAddress struct { - Address string `json:"ip-address"` - Prefix int `json:"prefix"` - Type string `json:"ip-address-type"` -} - -// GetQEMUNetworkInterfacesResponseResultStatistics contains the statistics from a QEMU get network interfaces response. -type GetQEMUNetworkInterfacesResponseResultStatistics struct { - RXBytes int `json:"rx-bytes"` - RXDropped int `json:"rx-dropped"` - RXErrors int `json:"rx-errs"` - RXPackets int `json:"rx-packets"` - TXBytes int `json:"tx-bytes"` - TXDropped int `json:"tx-dropped"` - TXErrors int `json:"tx-errs"` - TXPackets int `json:"tx-packets"` -} - -// GetResponseBody contains the body from a virtual machine get response. -type GetResponseBody struct { - Data *GetResponseData `json:"data,omitempty"` -} - -// GetResponseData contains the data from an virtual machine get response. -type GetResponseData struct { - ACPI *types.CustomBool `json:"acpi,omitempty"` - Agent *CustomAgent `json:"agent,omitempty"` - AllowReboot *types.CustomBool `json:"reboot,omitempty"` - AudioDevice *CustomAudioDevice `json:"audio0,omitempty"` - Autostart *types.CustomBool `json:"autostart,omitempty"` - BackupFile *string `json:"archive,omitempty"` - BandwidthLimit *int `json:"bwlimit,omitempty"` - BIOS *string `json:"bios,omitempty"` - BootDisk *string `json:"bootdisk,omitempty"` - BootOrder *string `json:"boot,omitempty"` - CDROM *string `json:"cdrom,omitempty"` - CloudInitDNSDomain *string `json:"searchdomain,omitempty"` - CloudInitDNSServer *string `json:"nameserver,omitempty"` - CloudInitFiles *CustomCloudInitFiles `json:"cicustom,omitempty"` - CloudInitPassword *string `json:"cipassword,omitempty"` - CloudInitSSHKeys *CustomCloudInitSSHKeys `json:"sshkeys,omitempty"` - CloudInitType *string `json:"citype,omitempty"` - CloudInitUsername *string `json:"ciuser,omitempty"` - CPUArchitecture *string `json:"arch,omitempty"` - CPUCores *int `json:"cores,omitempty"` - CPUEmulation *CustomCPUEmulation `json:"cpu,omitempty"` - CPULimit *int `json:"cpulimit,omitempty"` - CPUSockets *int `json:"sockets,omitempty"` - CPUUnits *int `json:"cpuunits,omitempty"` - DedicatedMemory *int `json:"memory,omitempty"` - DeletionProtection *types.CustomBool `json:"protection,omitempty"` - Description *string `json:"description,omitempty"` - EFIDisk *CustomEFIDisk `json:"efidisk0,omitempty"` - FloatingMemory *int `json:"balloon,omitempty"` - FloatingMemoryShares *int `json:"shares,omitempty"` - Freeze *types.CustomBool `json:"freeze,omitempty"` - HookScript *string `json:"hookscript,omitempty"` - Hotplug *types.CustomCommaSeparatedList `json:"hotplug,omitempty"` - Hugepages *string `json:"hugepages,omitempty"` - IDEDevice0 *CustomStorageDevice `json:"ide0,omitempty"` - IDEDevice1 *CustomStorageDevice `json:"ide1,omitempty"` - IDEDevice2 *CustomStorageDevice `json:"ide2,omitempty"` - IDEDevice3 *CustomStorageDevice `json:"ide3,omitempty"` - IPConfig0 *CustomCloudInitIPConfig `json:"ipconfig0,omitempty"` - IPConfig1 *CustomCloudInitIPConfig `json:"ipconfig1,omitempty"` - IPConfig2 *CustomCloudInitIPConfig `json:"ipconfig2,omitempty"` - IPConfig3 *CustomCloudInitIPConfig `json:"ipconfig3,omitempty"` - IPConfig4 *CustomCloudInitIPConfig `json:"ipconfig4,omitempty"` - IPConfig5 *CustomCloudInitIPConfig `json:"ipconfig5,omitempty"` - IPConfig6 *CustomCloudInitIPConfig `json:"ipconfig6,omitempty"` - IPConfig7 *CustomCloudInitIPConfig `json:"ipconfig7,omitempty"` - KeyboardLayout *string `json:"keyboard,omitempty"` - KVMArguments *string `json:"args,omitempty"` - KVMEnabled *types.CustomBool `json:"kvm,omitempty"` - LocalTime *types.CustomBool `json:"localtime,omitempty"` - Lock *string `json:"lock,omitempty"` - Machine *string `json:"machine,omitempty"` - MigrateDowntime *float64 `json:"migrate_downtime,omitempty"` - MigrateSpeed *int `json:"migrate_speed,omitempty"` - Name *string `json:"name,omitempty"` - NetworkDevice0 *CustomNetworkDevice `json:"net0,omitempty"` - NetworkDevice1 *CustomNetworkDevice `json:"net1,omitempty"` - NetworkDevice2 *CustomNetworkDevice `json:"net2,omitempty"` - NetworkDevice3 *CustomNetworkDevice `json:"net3,omitempty"` - NetworkDevice4 *CustomNetworkDevice `json:"net4,omitempty"` - NetworkDevice5 *CustomNetworkDevice `json:"net5,omitempty"` - NetworkDevice6 *CustomNetworkDevice `json:"net6,omitempty"` - NetworkDevice7 *CustomNetworkDevice `json:"net7,omitempty"` - NUMADevices *CustomNUMADevices `json:"numa_devices,omitempty"` - NUMAEnabled *types.CustomBool `json:"numa,omitempty"` - OSType *string `json:"ostype,omitempty"` - Overwrite *types.CustomBool `json:"force,omitempty"` - PCIDevice0 *CustomPCIDevice `json:"hostpci0,omitempty"` - PCIDevice1 *CustomPCIDevice `json:"hostpci1,omitempty"` - PCIDevice2 *CustomPCIDevice `json:"hostpci2,omitempty"` - PCIDevice3 *CustomPCIDevice `json:"hostpci3,omitempty"` - PoolID *string `json:"pool,omitempty" url:"pool,omitempty"` - Revert *string `json:"revert,omitempty"` - SATADevice0 *CustomStorageDevice `json:"sata0,omitempty"` - SATADevice1 *CustomStorageDevice `json:"sata1,omitempty"` - SATADevice2 *CustomStorageDevice `json:"sata2,omitempty"` - SATADevice3 *CustomStorageDevice `json:"sata3,omitempty"` - SATADevice4 *CustomStorageDevice `json:"sata4,omitempty"` - SATADevice5 *CustomStorageDevice `json:"sata5,omitempty"` - SCSIDevice0 *CustomStorageDevice `json:"scsi0,omitempty"` - SCSIDevice1 *CustomStorageDevice `json:"scsi1,omitempty"` - SCSIDevice2 *CustomStorageDevice `json:"scsi2,omitempty"` - SCSIDevice3 *CustomStorageDevice `json:"scsi3,omitempty"` - SCSIDevice4 *CustomStorageDevice `json:"scsi4,omitempty"` - SCSIDevice5 *CustomStorageDevice `json:"scsi5,omitempty"` - SCSIDevice6 *CustomStorageDevice `json:"scsi6,omitempty"` - SCSIDevice7 *CustomStorageDevice `json:"scsi7,omitempty"` - SCSIDevice8 *CustomStorageDevice `json:"scsi8,omitempty"` - SCSIDevice9 *CustomStorageDevice `json:"scsi9,omitempty"` - SCSIDevice10 *CustomStorageDevice `json:"scsi10,omitempty"` - SCSIDevice11 *CustomStorageDevice `json:"scsi11,omitempty"` - SCSIDevice12 *CustomStorageDevice `json:"scsi12,omitempty"` - SCSIDevice13 *CustomStorageDevice `json:"scsi13,omitempty"` - SCSIHardware *string `json:"scsihw,omitempty"` - SerialDevice0 *string `json:"serial0,omitempty"` - SerialDevice1 *string `json:"serial1,omitempty"` - SerialDevice2 *string `json:"serial2,omitempty"` - SerialDevice3 *string `json:"serial3,omitempty"` - SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty"` - SkipLock *types.CustomBool `json:"skiplock,omitempty"` - SMBIOS *CustomSMBIOS `json:"smbios1,omitempty"` - SpiceEnhancements *CustomSpiceEnhancements `json:"spice_enhancements,omitempty"` - StartDate *string `json:"startdate,omitempty"` - StartOnBoot *types.CustomBool `json:"onboot,omitempty"` - StartupOrder *CustomStartupOrder `json:"startup,omitempty"` - TabletDeviceEnabled *types.CustomBool `json:"tablet,omitempty"` - Tags *string `json:"tags,omitempty"` - Template *types.CustomBool `json:"template,omitempty"` - TimeDriftFixEnabled *types.CustomBool `json:"tdf,omitempty"` - USBDevices *CustomUSBDevices `json:"usb,omitempty"` - VGADevice *CustomVGADevice `json:"vga,omitempty"` - VirtualCPUCount *int `json:"vcpus,omitempty"` - VirtualIODevice0 *CustomStorageDevice `json:"virtio0,omitempty"` - VirtualIODevice1 *CustomStorageDevice `json:"virtio1,omitempty"` - VirtualIODevice2 *CustomStorageDevice `json:"virtio2,omitempty"` - VirtualIODevice3 *CustomStorageDevice `json:"virtio3,omitempty"` - VirtualIODevice4 *CustomStorageDevice `json:"virtio4,omitempty"` - VirtualIODevice5 *CustomStorageDevice `json:"virtio5,omitempty"` - VirtualIODevice6 *CustomStorageDevice `json:"virtio6,omitempty"` - VirtualIODevice7 *CustomStorageDevice `json:"virtio7,omitempty"` - VirtualIODevice8 *CustomStorageDevice `json:"virtio8,omitempty"` - VirtualIODevice9 *CustomStorageDevice `json:"virtio9,omitempty"` - VirtualIODevice10 *CustomStorageDevice `json:"virtio10,omitempty"` - VirtualIODevice11 *CustomStorageDevice `json:"virtio11,omitempty"` - VirtualIODevice12 *CustomStorageDevice `json:"virtio12,omitempty"` - VirtualIODevice13 *CustomStorageDevice `json:"virtio13,omitempty"` - VirtualIODevice14 *CustomStorageDevice `json:"virtio14,omitempty"` - VirtualIODevice15 *CustomStorageDevice `json:"virtio15,omitempty"` - VMGenerationID *string `json:"vmgenid,omitempty"` - VMStateDatastoreID *string `json:"vmstatestorage,omitempty"` - WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty"` -} - -// GetStatusResponseBody contains the body from a VM get status response. -type GetStatusResponseBody struct { - Data *GetStatusResponseData `json:"data,omitempty"` -} - -// GetStatusResponseData contains the data from a VM get status response. -type GetStatusResponseData struct { - AgentEnabled *types.CustomBool `json:"agent,omitempty"` - CPUCount *float64 `json:"cpus,omitempty"` - Lock *string `json:"lock,omitempty"` - MemoryAllocation *int `json:"maxmem,omitempty"` - Name *string `json:"name,omitempty"` - PID *int `json:"pid,omitempty"` - QMPStatus *string `json:"qmpstatus,omitempty"` - RootDiskSize *int `json:"maxdisk,omitempty"` - SpiceSupport *types.CustomBool `json:"spice,omitempty"` - Status string `json:"status,omitempty"` - Tags *string `json:"tags,omitempty"` - Uptime *int `json:"uptime,omitempty"` - VMID *int `json:"vmid,omitempty"` -} - -// ListResponseBody contains the body from a virtual machine list response. -type ListResponseBody struct { - Data []*ListResponseData `json:"data,omitempty"` -} - -// ListResponseData contains the data from an virtual machine list response. -type ListResponseData struct { - Name *string `json:"name,omitempty"` - Tags *string `json:"tags,omitempty"` - VMID int `json:"vmid,omitempty"` -} - -// MigrateRequestBody contains the body for a VM migration request. -type MigrateRequestBody struct { - OnlineMigration *types.CustomBool `json:"online,omitempty" url:"online,omitempty"` - TargetNode string `json:"target" url:"target"` - TargetStorage *string `json:"targetstorage,omitempty" url:"targetstorage,omitempty"` - WithLocalDisks *types.CustomBool `json:"with-local-disks,omitempty" url:"with-local-disks,omitempty,int"` -} - -// MigrateResponseBody contains the body from a VM migrate response. -type MigrateResponseBody struct { - Data *string `json:"data,omitempty"` -} - -// MoveDiskRequestBody contains the body for a VM move disk request. -type MoveDiskRequestBody struct { - BandwidthLimit *int `json:"bwlimit,omitempty" url:"bwlimit,omitempty"` - DeleteOriginalDisk *types.CustomBool `json:"delete,omitempty" url:"delete,omitempty,int"` - Digest *string `json:"digest,omitempty" url:"digest,omitempty"` - Disk string `json:"disk" url:"disk"` - TargetStorage string `json:"storage" url:"storage"` - TargetStorageFormat *string `json:"format,omitempty" url:"format,omitempty"` -} - -// MoveDiskResponseBody contains the body from a VM move disk response. -type MoveDiskResponseBody struct { - Data *string `json:"data,omitempty"` -} - -// RebootRequestBody contains the body for a VM reboot request. -type RebootRequestBody struct { - Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"` -} - -// RebootResponseBody contains the body from a VM reboot response. -type RebootResponseBody struct { - Data *string `json:"data,omitempty"` -} - -// ResizeDiskRequestBody contains the body for a VM resize disk request. -type ResizeDiskRequestBody struct { - Digest *string `json:"digest,omitempty" url:"digest,omitempty"` - Disk string `json:"disk" url:"disk"` - Size types.DiskSize `json:"size" url:"size"` - SkipLock *types.CustomBool `json:"skiplock,omitempty" url:"skiplock,omitempty,int"` -} - -// ShutdownRequestBody contains the body for a VM shutdown request. -type ShutdownRequestBody struct { - ForceStop *types.CustomBool `json:"forceStop,omitempty" url:"forceStop,omitempty,int"` - KeepActive *types.CustomBool `json:"keepActive,omitempty" url:"keepActive,omitempty,int"` - SkipLock *types.CustomBool `json:"skipLock,omitempty" url:"skipLock,omitempty,int"` - Timeout *int `json:"timeout,omitempty" url:"timeout,omitempty"` -} - -// ShutdownResponseBody contains the body from a VM shutdown response. -type ShutdownResponseBody struct { - Data *string `json:"data,omitempty"` -} - -// StartResponseBody contains the body from a VM start response. -type StartResponseBody struct { - Data *string `json:"data,omitempty"` -} - -// StopResponseBody contains the body from a VM stop response. -type StopResponseBody struct { - Data *string `json:"data,omitempty"` -} - -// UpdateAsyncResponseBody contains the body from a VM async update response. -type UpdateAsyncResponseBody struct { - Data *string `json:"data,omitempty"` -} - -// UpdateRequestBody contains the data for an virtual machine update request. -type UpdateRequestBody CreateRequestBody - -// EncodeValues converts a CustomAgent struct to a URL vlaue. -func (r CustomAgent) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.Enabled != nil { - if *r.Enabled { - values = append(values, "enabled=1") - } else { - values = append(values, "enabled=0") - } - } - - if r.TrimClonedDisks != nil { - if *r.TrimClonedDisks { - values = append(values, "fstrim_cloned_disks=1") - } else { - values = append(values, "fstrim_cloned_disks=0") - } - } - - if r.Type != nil { - values = append(values, fmt.Sprintf("type=%s", *r.Type)) - } - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// EncodeValues converts a CustomAudioDevice struct to a URL vlaue. -func (r CustomAudioDevice) EncodeValues(key string, v *url.Values) error { - values := []string{fmt.Sprintf("device=%s", r.Device)} - - if r.Driver != nil { - values = append(values, fmt.Sprintf("driver=%s", *r.Driver)) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomAudioDevices array to multiple URL values. -func (r CustomAudioDevices) EncodeValues(key string, v *url.Values) error { - for i, d := range r { - if d.Enabled { - if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil { - return fmt.Errorf("unable to encode audio device %d: %w", i, err) - } - } - } - - return nil -} - -// EncodeValues converts a CustomBoot struct to multiple URL values. -func (r CustomBoot) EncodeValues(key string, v *url.Values) error { - if r.Order != nil && len(*r.Order) > 0 { - v.Add(key, fmt.Sprintf("order=%s", strings.Join(*r.Order, ";"))) - } - - return nil -} - -// EncodeValues converts a CustomCloudInitConfig struct to multiple URL values. -func (r CustomCloudInitConfig) EncodeValues(_ string, v *url.Values) error { - //nolint:nestif - if r.Files != nil { - var volumes []string - - if r.Files.MetaVolume != nil { - volumes = append(volumes, fmt.Sprintf("meta=%s", *r.Files.MetaVolume)) - } - - if r.Files.NetworkVolume != nil { - volumes = append(volumes, fmt.Sprintf("network=%s", *r.Files.NetworkVolume)) - } - - if r.Files.UserVolume != nil { - volumes = append(volumes, fmt.Sprintf("user=%s", *r.Files.UserVolume)) - } - - if r.Files.VendorVolume != nil { - volumes = append(volumes, fmt.Sprintf("vendor=%s", *r.Files.VendorVolume)) - } - - if len(volumes) > 0 { - v.Add("cicustom", strings.Join(volumes, ",")) - } - } - - for i, c := range r.IPConfig { - var config []string - - if c.GatewayIPv4 != nil { - config = append(config, fmt.Sprintf("gw=%s", *c.GatewayIPv4)) - } - - if c.GatewayIPv6 != nil { - config = append(config, fmt.Sprintf("gw6=%s", *c.GatewayIPv6)) - } - - if c.IPv4 != nil { - config = append(config, fmt.Sprintf("ip=%s", *c.IPv4)) - } - - if c.IPv6 != nil { - config = append(config, fmt.Sprintf("ip6=%s", *c.IPv6)) - } - - if len(config) > 0 { - v.Add(fmt.Sprintf("ipconfig%d", i), strings.Join(config, ",")) - } - } - - if r.Nameserver != nil { - v.Add("nameserver", *r.Nameserver) - } - - if r.Password != nil { - v.Add("cipassword", *r.Password) - } - - if r.SearchDomain != nil { - v.Add("searchdomain", *r.SearchDomain) - } - - if r.SSHKeys != nil { - v.Add( - "sshkeys", - strings.ReplaceAll(url.QueryEscape(strings.Join(*r.SSHKeys, "\n")), "+", "%20"), - ) - } - - if r.Type != nil { - v.Add("citype", *r.Type) - } - - if r.Username != nil { - v.Add("ciuser", *r.Username) - } - - return nil -} - -// EncodeValues converts a CustomCPUEmulation struct to a URL vlaue. -func (r CustomCPUEmulation) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("cputype=%s", r.Type), - } - - if r.Flags != nil && len(*r.Flags) > 0 { - values = append(values, fmt.Sprintf("flags=%s", strings.Join(*r.Flags, ";"))) - } - - if r.Hidden != nil { - if *r.Hidden { - values = append(values, "hidden=1") - } else { - values = append(values, "hidden=0") - } - } - - if r.HVVendorID != nil { - values = append(values, fmt.Sprintf("hv-vendor-id=%s", *r.HVVendorID)) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomEFIDisk struct to a URL vlaue. -func (r CustomEFIDisk) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("file=%s", r.FileVolume), - } - - if r.Format != nil { - values = append(values, fmt.Sprintf("format=%s", *r.Format)) - } - - if r.Type != nil { - values = append(values, fmt.Sprintf("efitype=%s", *r.Type)) - } - - if r.PreEnrolledKeys != nil { - if *r.PreEnrolledKeys { - values = append(values, "pre-enrolled-keys=1") - } else { - values = append(values, "pre-enrolled-keys=0") - } - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomNetworkDevice struct to a URL vlaue. -func (r CustomNetworkDevice) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("model=%s", r.Model), - } - - if r.Bridge != nil { - values = append(values, fmt.Sprintf("bridge=%s", *r.Bridge)) - } - - if r.Firewall != nil { - if *r.Firewall { - values = append(values, "firewall=1") - } else { - values = append(values, "firewall=0") - } - } - - if r.LinkDown != nil { - if *r.LinkDown { - values = append(values, "link_down=1") - } else { - values = append(values, "link_down=0") - } - } - - if r.MACAddress != nil { - values = append(values, fmt.Sprintf("macaddr=%s", *r.MACAddress)) - } - - if r.Queues != nil { - values = append(values, fmt.Sprintf("queues=%d", *r.Queues)) - } - - if r.RateLimit != nil { - values = append(values, fmt.Sprintf("rate=%f", *r.RateLimit)) - } - - if r.Tag != nil { - values = append(values, fmt.Sprintf("tag=%d", *r.Tag)) - } - - if r.MTU != nil { - values = append(values, fmt.Sprintf("mtu=%d", *r.MTU)) - } - - if len(r.Trunks) > 0 { - trunks := make([]string, len(r.Trunks)) - - for i, v := range r.Trunks { - trunks[i] = strconv.Itoa(v) - } - - values = append(values, fmt.Sprintf("trunks=%s", strings.Join(trunks, ";"))) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomNetworkDevices array to multiple URL values. -func (r CustomNetworkDevices) EncodeValues(key string, v *url.Values) error { - for i, d := range r { - if d.Enabled { - if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil { - return fmt.Errorf("failed to encode network device %d: %w", i, err) - } - } - } - - return nil -} - -// EncodeValues converts a CustomNUMADevice struct to a URL vlaue. -func (r CustomNUMADevice) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("cpus=%s", strings.Join(r.CPUIDs, ";")), - } - - if r.HostNodeNames != nil { - values = append(values, fmt.Sprintf("hostnodes=%s", strings.Join(*r.HostNodeNames, ";"))) - } - - if r.Memory != nil { - values = append(values, fmt.Sprintf("memory=%f", *r.Memory)) - } - - if r.Policy != nil { - values = append(values, fmt.Sprintf("policy=%s", *r.Policy)) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomNUMADevices array to multiple URL values. -func (r CustomNUMADevices) EncodeValues(key string, v *url.Values) error { - for i, d := range r { - if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil { - return fmt.Errorf("failed to encode NUMA device %d: %w", i, err) - } - } - - return nil -} - -// EncodeValues converts a CustomPCIDevice struct to a URL vlaue. -func (r CustomPCIDevice) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("host=%s", strings.Join(r.DeviceIDs, ";")), - } - - if r.MDev != nil { - values = append(values, fmt.Sprintf("mdev=%s", *r.MDev)) - } - - if r.PCIExpress != nil { - if *r.PCIExpress { - values = append(values, "pcie=1") - } else { - values = append(values, "pcie=0") - } - } - - if r.ROMBAR != nil { - if *r.ROMBAR { - values = append(values, "rombar=1") - } else { - values = append(values, "rombar=0") - } - } - - if r.ROMFile != nil { - values = append(values, fmt.Sprintf("romfile=%s", *r.ROMFile)) - } - - if r.XVGA != nil { - if *r.XVGA { - values = append(values, "x-vga=1") - } else { - values = append(values, "x-vga=0") - } - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomPCIDevices array to multiple URL values. -func (r CustomPCIDevices) EncodeValues(key string, v *url.Values) error { - for i, d := range r { - if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil { - return fmt.Errorf("failed to encode PCI device %d: %w", i, err) - } - } - - return nil -} - -// EncodeValues converts a CustomSerialDevices array to multiple URL values. -func (r CustomSerialDevices) EncodeValues(key string, v *url.Values) error { - for i, d := range r { - v.Add(fmt.Sprintf("%s%d", key, i), d) - } - - return nil -} - -// EncodeValues converts a CustomSharedMemory struct to a URL vlaue. -func (r CustomSharedMemory) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("size=%d", r.Size), - } - - if r.Name != nil { - values = append(values, fmt.Sprintf("name=%s", *r.Name)) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomSMBIOS struct to a URL vlaue. -func (r CustomSMBIOS) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.Base64 != nil { - if *r.Base64 { - values = append(values, "base64=1") - } else { - values = append(values, "base64=0") - } - } - - if r.Family != nil { - values = append(values, fmt.Sprintf("family=%s", *r.Family)) - } - - if r.Manufacturer != nil { - values = append(values, fmt.Sprintf("manufacturer=%s", *r.Manufacturer)) - } - - if r.Product != nil { - values = append(values, fmt.Sprintf("product=%s", *r.Product)) - } - - if r.Serial != nil { - values = append(values, fmt.Sprintf("serial=%s", *r.Serial)) - } - - if r.SKU != nil { - values = append(values, fmt.Sprintf("sku=%s", *r.SKU)) - } - - if r.UUID != nil { - values = append(values, fmt.Sprintf("uuid=%s", *r.UUID)) - } - - if r.Version != nil { - values = append(values, fmt.Sprintf("version=%s", *r.Version)) - } - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// EncodeValues converts a CustomSpiceEnhancements struct to a URL vlaue. -func (r CustomSpiceEnhancements) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.FolderSharing != nil { - if *r.FolderSharing { - values = append(values, "foldersharing=1") - } else { - values = append(values, "foldersharing=0") - } - } - - if r.VideoStreaming != nil { - values = append(values, fmt.Sprintf("videostreaming=%s", *r.VideoStreaming)) - } - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// EncodeValues converts a CustomStartupOrder struct to a URL vlaue. -func (r CustomStartupOrder) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.Order != nil { - values = append(values, fmt.Sprintf("order=%d", *r.Order)) - } - - if r.Up != nil { - values = append(values, fmt.Sprintf("up=%d", *r.Up)) - } - - if r.Down != nil { - values = append(values, fmt.Sprintf("down=%d", *r.Down)) - } - - if len(values) > 0 { - v.Add(key, strings.Join(values, ",")) - } - - return nil -} - -// EncodeValues converts a CustomStorageDevice struct to a URL vlaue. -func (r CustomStorageDevice) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("file=%s", r.FileVolume), - } - - if r.AIO != nil { - values = append(values, fmt.Sprintf("aio=%s", *r.AIO)) - } - - if r.BackupEnabled != nil { - if *r.BackupEnabled { - values = append(values, "backup=1") - } else { - values = append(values, "backup=0") - } - } - - if r.BurstableReadSpeedMbps != nil { - values = append(values, fmt.Sprintf("mbps_rd_max=%d", *r.BurstableReadSpeedMbps)) - } - - if r.BurstableWriteSpeedMbps != nil { - values = append(values, fmt.Sprintf("mbps_wr_max=%d", *r.BurstableWriteSpeedMbps)) - } - - if r.Format != nil { - values = append(values, fmt.Sprintf("format=%s", *r.Format)) - } - - if r.MaxReadSpeedMbps != nil { - values = append(values, fmt.Sprintf("mbps_rd=%d", *r.MaxReadSpeedMbps)) - } - - if r.MaxWriteSpeedMbps != nil { - values = append(values, fmt.Sprintf("mbps_wr=%d", *r.MaxWriteSpeedMbps)) - } - - if r.Media != nil { - values = append(values, fmt.Sprintf("media=%s", *r.Media)) - } - - if r.Size != nil { - values = append(values, fmt.Sprintf("size=%s", *r.Size)) - } - - if r.IOThread != nil { - if *r.IOThread { - values = append(values, "iothread=1") - } else { - values = append(values, "iothread=0") - } - } - - if r.SSD != nil { - if *r.SSD { - values = append(values, "ssd=1") - } else { - values = append(values, "ssd=0") - } - } - - if r.Discard != nil && *r.Discard != "" { - values = append(values, fmt.Sprintf("discard=%s", *r.Discard)) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomStorageDevices array to multiple URL values. -func (r CustomStorageDevices) EncodeValues(_ string, v *url.Values) error { - for s, d := range r { - if d.Enabled { - if err := d.EncodeValues(s, v); err != nil { - return fmt.Errorf("error encoding storage device %s: %w", s, err) - } - } - } - - return nil -} - -// EncodeValues converts a CustomUSBDevice struct to a URL vlaue. -func (r CustomUSBDevice) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("host=%s", r.HostDevice), - } - - if r.USB3 != nil { - if *r.USB3 { - values = append(values, "usb3=1") - } else { - values = append(values, "usb3=0") - } - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomUSBDevices array to multiple URL values. -func (r CustomUSBDevices) EncodeValues(key string, v *url.Values) error { - for i, d := range r { - if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil { - return fmt.Errorf("error encoding USB device %d: %w", i, err) - } - } - - return nil -} - -// EncodeValues converts a CustomVGADevice struct to a URL vlaue. -func (r CustomVGADevice) EncodeValues(key string, v *url.Values) error { - var values []string - - if r.Memory != nil { - values = append(values, fmt.Sprintf("memory=%d", *r.Memory)) - } - - if r.Type != nil { - values = append(values, fmt.Sprintf("type=%s", *r.Type)) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomVirtualIODevice struct to a URL vlaue. -func (r CustomVirtualIODevice) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("file=%s", r.FileVolume), - } - - if r.AIO != nil { - values = append(values, fmt.Sprintf("aio=%s", *r.AIO)) - } - - if r.BackupEnabled != nil { - if *r.BackupEnabled { - values = append(values, "backup=1") - } else { - values = append(values, "backup=0") - } - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// EncodeValues converts a CustomVirtualIODevices array to multiple URL values. -func (r CustomVirtualIODevices) EncodeValues(key string, v *url.Values) error { - for i, d := range r { - if d.Enabled { - if err := d.EncodeValues(fmt.Sprintf("%s%d", key, i), v); err != nil { - return fmt.Errorf("error encoding virtual IO device %d: %w", i, err) - } - } - } - - return nil -} - -// EncodeValues converts a CustomWatchdogDevice struct to a URL vlaue. -func (r CustomWatchdogDevice) EncodeValues(key string, v *url.Values) error { - values := []string{ - fmt.Sprintf("model=%+v", r.Model), - } - - if r.Action != nil { - values = append(values, fmt.Sprintf("action=%s", *r.Action)) - } - - v.Add(key, strings.Join(values, ",")) - - return nil -} - -// UnmarshalJSON converts a CustomAgent string to an object. -func (r *CustomAgent) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("error unmarshalling CustomAgent: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 1 { - enabled := types.CustomBool(v[0] == "1") - r.Enabled = &enabled - } else if len(v) == 2 { - switch v[0] { - case "enabled": - enabled := types.CustomBool(v[1] == "1") - r.Enabled = &enabled - case "fstrim_cloned_disks": - fstrim := types.CustomBool(v[1] == "1") - r.TrimClonedDisks = &fstrim - case "type": - r.Type = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomAgent string to an object. -func (r *CustomAudioDevice) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("error unmarshalling CustomAudioDevice: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 2 { - switch v[0] { - case "device": - r.Device = v[1] - case "driver": - r.Driver = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomBoot string to an object. -func (r *CustomBoot) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("error unmarshalling CustomBoot: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 2 { - if v[0] == "order" { - o := strings.Split(strings.TrimSpace(v[1]), ";") - r.Order = &o - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomCloudInitFiles string to an object. -func (r *CustomCloudInitFiles) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("error unmarshalling CustomCloudInitFiles: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 2 { - switch v[0] { - case "meta": - r.MetaVolume = &v[1] - case "network": - r.NetworkVolume = &v[1] - case "user": - r.UserVolume = &v[1] - case "vendor": - r.VendorVolume = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomCloudInitIPConfig string to an object. -func (r *CustomCloudInitIPConfig) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("error unmarshalling CustomCloudInitIPConfig: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 2 { - switch v[0] { - case "gw": - r.GatewayIPv4 = &v[1] - case "gw6": - r.GatewayIPv6 = &v[1] - case "ip": - r.IPv4 = &v[1] - case "ip6": - r.IPv6 = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomCloudInitFiles string to an object. -func (r *CustomCloudInitSSHKeys) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("error unmarshalling CustomCloudInitSSHKeys: %w", err) - } - - s, err := url.QueryUnescape(s) - if err != nil { - return fmt.Errorf("error unescaping CustomCloudInitSSHKeys: %w", err) - } - - if s != "" { - *r = strings.Split(strings.TrimSpace(s), "\n") - } else { - *r = []string{} - } - - return nil -} - -// UnmarshalJSON converts a CustomCPUEmulation string to an object. -func (r *CustomCPUEmulation) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("error unmarshalling CustomCPUEmulation: %w", err) - } - - if s == "" { - return errors.New("unexpected empty string") - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 1 { - r.Type = v[0] - } else if len(v) == 2 { - switch v[0] { - case "cputype": - r.Type = v[1] - case "flags": - if v[1] != "" { - f := strings.Split(v[1], ";") - r.Flags = &f - } else { - var f []string - r.Flags = &f - } - case "hidden": - bv := types.CustomBool(v[1] == "1") - r.Hidden = &bv - case "hv-vendor-id": - r.HVVendorID = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomEFIDisk string to an object. -func (r *CustomEFIDisk) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("failed to unmarshal CustomEFIDisk: %w", err) - } - - pairs := strings.Split(s, ",") - - for i, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 1 && i == 0 { - r.FileVolume = v[0] - } - - if len(v) == 2 { - switch v[0] { - case "file": - r.FileVolume = v[1] - case "format": - r.Format = &v[1] - case "efitype": - t := strings.ToLower(v[1]) - r.Type = &t - case "pre-enrolled-keys": - bv := types.CustomBool(v[1] == "1") - r.PreEnrolledKeys = &bv - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomNetworkDevice string to an object. -func (r *CustomNetworkDevice) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("failed to unmarshal CustomNetworkDevice: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - //nolint:nestif - if len(v) == 2 { - switch v[0] { - case "bridge": - r.Bridge = &v[1] - case "firewall": - bv := types.CustomBool(v[1] == "1") - r.Firewall = &bv - case "link_down": - bv := types.CustomBool(v[1] == "1") - r.LinkDown = &bv - case "macaddr": - r.MACAddress = &v[1] - case "model": - r.Model = v[1] - case "queues": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to parse queues: %w", err) - } - - r.Queues = &iv - case "rate": - fv, err := strconv.ParseFloat(v[1], 64) - if err != nil { - return fmt.Errorf("failed to parse rate: %w", err) - } - - r.RateLimit = &fv - - case "mtu": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to parse mtu: %w", err) - } - - r.MTU = &iv - - case "tag": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to parse tag: %w", err) - } - - r.Tag = &iv - case "trunks": - trunks := strings.Split(v[1], ";") - r.Trunks = make([]int, len(trunks)) - - for i, trunk := range trunks { - iv, err := strconv.Atoi(trunk) - if err != nil { - return fmt.Errorf("failed to parse trunk %d: %w", i, err) - } - - r.Trunks[i] = iv - } - default: - r.MACAddress = &v[1] - r.Model = v[0] - } - } - } - - r.Enabled = true - - return nil -} - -// UnmarshalJSON converts a CustomPCIDevice string to an object. -func (r *CustomPCIDevice) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("failed to unmarshal CustomPCIDevice: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - if len(v) == 1 { - r.DeviceIDs = strings.Split(v[1], ";") - } else if len(v) == 2 { - switch v[0] { - case "host": - r.DeviceIDs = strings.Split(v[1], ";") - case "mdev": - r.MDev = &v[1] - case "pcie": - bv := types.CustomBool(v[1] == "1") - r.PCIExpress = &bv - case "rombar": - bv := types.CustomBool(v[1] == "1") - r.ROMBAR = &bv - case "romfile": - r.ROMFile = &v[1] - case "x-vga": - bv := types.CustomBool(v[1] == "1") - r.XVGA = &bv - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomSharedMemory string to an object. -func (r *CustomSharedMemory) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("failed to unmarshal CustomSharedMemory: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 2 { - switch v[0] { - case "name": - r.Name = &v[1] - case "size": - var err error - - r.Size, err = strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to parse shared memory size: %w", err) - } - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomSMBIOS string to an object. -func (r *CustomSMBIOS) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("failed to unmarshal CustomSMBIOS: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 2 { - switch v[0] { - case "base64": - base64 := types.CustomBool(v[1] == "1") - r.Base64 = &base64 - case "family": - r.Family = &v[1] - case "manufacturer": - r.Manufacturer = &v[1] - case "product": - r.Product = &v[1] - case "serial": - r.Serial = &v[1] - case "sku": - r.SKU = &v[1] - case "uuid": - r.UUID = &v[1] - case "version": - r.Version = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomStorageDevice string to an object. -func (r *CustomStorageDevice) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("failed to unmarshal CustomStorageDevice: %w", err) - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - //nolint:nestif - if len(v) == 1 { - r.FileVolume = v[0] - - ext := filepath.Ext(v[0]) - if ext != "" { - format := string([]byte(ext)[1:]) - r.Format = &format - } - } else if len(v) == 2 { - switch v[0] { - case "aio": - r.AIO = &v[1] - case "backup": - bv := types.CustomBool(v[1] == "1") - r.BackupEnabled = &bv - case "file": - r.FileVolume = v[1] - case "mbps_rd": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to convert mbps_rd to int: %w", err) - } - - r.MaxReadSpeedMbps = &iv - case "mbps_rd_max": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to convert mbps_rd_max to int: %w", err) - } - - r.BurstableReadSpeedMbps = &iv - case "mbps_wr": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to convert mbps_wr to int: %w", err) - } - - r.MaxWriteSpeedMbps = &iv - case "mbps_wr_max": - iv, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to convert mbps_wr_max to int: %w", err) - } - - r.BurstableWriteSpeedMbps = &iv - case "media": - r.Media = &v[1] - case "size": - r.Size = new(types.DiskSize) - err := r.Size.UnmarshalJSON([]byte(v[1])) - if err != nil { - return fmt.Errorf("failed to unmarshal disk size: %w", err) - } - case "format": - r.Format = &v[1] - case "iothread": - bv := types.CustomBool(v[1] == "1") - r.IOThread = &bv - case "ssd": - bv := types.CustomBool(v[1] == "1") - r.SSD = &bv - case "discard": - r.Discard = &v[1] - } - } - } - - r.Enabled = true - - return nil -} - -// UnmarshalJSON converts a CustomVGADevice string to an object. -func (r *CustomVGADevice) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("failed to unmarshal CustomVGADevice: %w", err) - } - - if s == "" { - return nil - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 1 { - r.Type = &v[0] - } else if len(v) == 2 { - switch v[0] { - case "memory": - m, err := strconv.Atoi(v[1]) - if err != nil { - return fmt.Errorf("failed to convert memory to int: %w", err) - } - - r.Memory = &m - case "type": - r.Type = &v[1] - } - } - } - - return nil -} - -// UnmarshalJSON converts a CustomWatchdogDevice string to an object. -func (r *CustomWatchdogDevice) UnmarshalJSON(b []byte) error { - var s string - - if err := json.Unmarshal(b, &s); err != nil { - return fmt.Errorf("failed to unmarshal CustomWatchdogDevice: %w", err) - } - - if s == "" { - return nil - } - - pairs := strings.Split(s, ",") - - for _, p := range pairs { - v := strings.Split(strings.TrimSpace(p), "=") - - if len(v) == 1 { - r.Model = &v[0] - } else if len(v) == 2 { - switch v[0] { - case "action": - r.Action = &v[1] - case "model": - r.Model = &v[1] - } - } - } - - return nil -} diff --git a/proxmox/nodes/vms/vms_types_test.go b/proxmox/nodes/vms/vms_types_test.go deleted file mode 100644 index a5a04c4e..00000000 --- a/proxmox/nodes/vms/vms_types_test.go +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package vms - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -func TestCustomStorageDevice_UnmarshalJSON(t *testing.T) { - ds8gig := types.DiskSizeFromGigabytes(8) - tests := []struct { - name string - line string - want *CustomStorageDevice - wantErr bool - }{ - { - name: "simple volume", - line: `"local-lvm:vm-2041-disk-0,discard=on,ssd=1,iothread=1,size=8G"`, - want: &CustomStorageDevice{ - Discard: types.StrPtr("on"), - Enabled: true, - FileVolume: "local-lvm:vm-2041-disk-0", - IOThread: types.BoolPtr(true), - Size: &ds8gig, - SSD: types.BoolPtr(true), - }, - }, - { - name: "raw volume type", - line: `"nfs:2041/vm-2041-disk-0.raw,discard=ignore,ssd=1,iothread=1,size=8G"`, - want: &CustomStorageDevice{ - Discard: types.StrPtr("ignore"), - Enabled: true, - FileVolume: "nfs:2041/vm-2041-disk-0.raw", - Format: types.StrPtr("raw"), - IOThread: types.BoolPtr(true), - Size: &ds8gig, - SSD: types.BoolPtr(true), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := &CustomStorageDevice{} - if err := r.UnmarshalJSON([]byte(tt.line)); (err != nil) != tt.wantErr { - t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - require.Equal(t, tt.want, r) - }) - } -} diff --git a/proxmox/pools/client.go b/proxmox/pools/client.go deleted file mode 100644 index a98b914d..00000000 --- a/proxmox/pools/client.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package pools - -import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Client is an interface for accessing the Proxmox pools API. -type Client struct { - api.Client -} diff --git a/proxmox/pools/pool.go b/proxmox/pools/pool.go deleted file mode 100644 index 71931d35..00000000 --- a/proxmox/pools/pool.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package pools - -import ( - "context" - "fmt" - "net/http" - "net/url" - "sort" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// CreatePool creates a pool. -func (c *Client) CreatePool(ctx context.Context, d *PoolCreateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPost, "pools", d, nil) - if err != nil { - return fmt.Errorf("error creating pool: %w", err) - } - - return nil -} - -// DeletePool deletes a pool. -func (c *Client) DeletePool(ctx context.Context, id string) error { - err := c.DoRequest(ctx, http.MethodDelete, fmt.Sprintf("pools/%s", url.PathEscape(id)), nil, nil) - if err != nil { - return fmt.Errorf("error deleting pool: %w", err) - } - - return nil -} - -// GetPool retrieves a pool. -func (c *Client) GetPool(ctx context.Context, id string) (*PoolGetResponseData, error) { - resBody := &PoolGetResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, fmt.Sprintf("pools/%s", url.PathEscape(id)), nil, resBody) - if err != nil { - return nil, fmt.Errorf("error getting pool: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data.Members, func(i, j int) bool { - return resBody.Data.Members[i].ID < resBody.Data.Members[j].ID - }) - - return resBody.Data, nil -} - -// ListPools retrieves a list of pools. -func (c *Client) ListPools(ctx context.Context) ([]*PoolListResponseData, error) { - resBody := &PoolListResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, "pools", nil, resBody) - if err != nil { - return nil, fmt.Errorf("error listing pools: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - sort.Slice(resBody.Data, func(i, j int) bool { - return resBody.Data[i].ID < resBody.Data[j].ID - }) - - return resBody.Data, nil -} - -// UpdatePool updates a pool. -func (c *Client) UpdatePool(ctx context.Context, id string, d *PoolUpdateRequestBody) error { - err := c.DoRequest(ctx, http.MethodPut, fmt.Sprintf("pools/%s", url.PathEscape(id)), d, nil) - if err != nil { - return fmt.Errorf("error updating pool: %w", err) - } - - return nil -} diff --git a/proxmox/pools/pool_types.go b/proxmox/pools/pool_types.go deleted file mode 100644 index 625934ef..00000000 --- a/proxmox/pools/pool_types.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package pools - -// PoolCreateRequestBody contains the data for a pool create request. -type PoolCreateRequestBody struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` - ID string `json:"groupid" url:"poolid"` -} - -// PoolGetResponseBody contains the body from a pool get response. -type PoolGetResponseBody struct { - Data *PoolGetResponseData `json:"data,omitempty"` -} - -// PoolGetResponseData contains the data from a pool get response. -type PoolGetResponseData struct { - Comment *string `json:"comment,omitempty"` - Members []VirtualEnvironmentPoolGetResponseMembers `json:"members,omitempty"` -} - -// VirtualEnvironmentPoolGetResponseMembers contains the members data from a pool get response. -type VirtualEnvironmentPoolGetResponseMembers struct { - ID string `json:"id"` - Node string `json:"node"` - DatastoreID *string `json:"storage,omitempty"` - Type string `json:"type"` - VMID *int `json:"vmid"` -} - -// PoolListResponseBody contains the body from a pool list response. -type PoolListResponseBody struct { - Data []*PoolListResponseData `json:"data,omitempty"` -} - -// PoolListResponseData contains the data from a pool list response. -type PoolListResponseData struct { - Comment *string `json:"comment,omitempty"` - ID string `json:"poolid"` -} - -// PoolUpdateRequestBody contains the data for an pool update request. -type PoolUpdateRequestBody struct { - Comment *string `json:"comment,omitempty" url:"comment,omitempty"` -} diff --git a/proxmox/ssh/client.go b/proxmox/ssh/client.go deleted file mode 100644 index 49917e43..00000000 --- a/proxmox/ssh/client.go +++ /dev/null @@ -1,343 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package ssh - -import ( - "context" - "errors" - "fmt" - "net" - "os" - "path" - "path/filepath" - "runtime" - "strings" - - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/pkg/sftp" - "github.com/skeema/knownhosts" - "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/agent" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/utils" -) - -// Client is an interface for performing SSH requests against the Proxmox Nodes. -type Client interface { - // ExecuteNodeCommands executes a command on a node. - ExecuteNodeCommands(ctx context.Context, nodeName string, commands []string) error - - // NodeUpload uploads a file to a node. - NodeUpload(ctx context.Context, nodeName string, - remoteFileDir string, fileUploadRequest *api.FileUploadRequest) error -} - -type client struct { - username string - password string - agent bool - agentSocket string - nodeLookup NodeResolver -} - -// NewClient creates a new SSH client. -func NewClient( - username string, password string, - agent bool, agentSocket string, - nodeLookup NodeResolver, -) (Client, error) { - //goland:noinspection GoBoolExpressions - if agent && runtime.GOOS != "linux" && runtime.GOOS != "darwin" && runtime.GOOS != "freebsd" { - return nil, errors.New( - "the ssh agent flag is only supported on POSIX systems, please set it to 'false'" + - " or remove it from your provider configuration", - ) - } - - if nodeLookup == nil { - return nil, errors.New("node lookup is required") - } - - return &client{ - username: username, - password: password, - agent: agent, - agentSocket: agentSocket, - nodeLookup: nodeLookup, - }, nil -} - -// ExecuteNodeCommands executes commands on a given node. -func (c *client) ExecuteNodeCommands(ctx context.Context, nodeName string, commands []string) error { - ip, err := c.nodeLookup.Resolve(ctx, nodeName) - if err != nil { - return fmt.Errorf("failed to find node endpoint: %w", err) - } - - tflog.Debug(ctx, "executing commands on the node using SSH", map[string]interface{}{ - "node_address": ip, - "commands": commands, - }) - - closeOrLogError := utils.CloseOrLogError(ctx) - - sshClient, err := c.openNodeShell(ctx, ip) - if err != nil { - return err - } - - defer closeOrLogError(sshClient) - - sshSession, err := sshClient.NewSession() - if err != nil { - return fmt.Errorf("failed to create SSH session: %w", err) - } - - defer closeOrLogError(sshSession) - - script := strings.Join(commands, " && \\\n") - - output, err := sshSession.CombinedOutput( - fmt.Sprintf( - "/bin/bash -c '%s'", - strings.ReplaceAll(script, "'", "'\"'\"'"), - ), - ) - if err != nil { - return errors.New(string(output)) - } - - return nil -} - -func (c *client) NodeUpload( - ctx context.Context, - nodeName string, - remoteFileDir string, - d *api.FileUploadRequest, -) error { - ip, err := c.nodeLookup.Resolve(ctx, nodeName) - if err != nil { - return fmt.Errorf("failed to find node endpoint: %w", err) - } - - tflog.Debug(ctx, "uploading file to the node datastore using SFTP", map[string]interface{}{ - "node_address": ip, - "remote_dir": remoteFileDir, - "file_name": d.FileName, - "content_type": d.ContentType, - }) - - fileInfo, err := d.File.Stat() - if err != nil { - return fmt.Errorf("failed to get file info: %w", err) - } - - fileSize := fileInfo.Size() - - sshClient, err := c.openNodeShell(ctx, ip) - if err != nil { - return fmt.Errorf("failed to open SSH client: %w", err) - } - - defer func(sshClient *ssh.Client) { - e := sshClient.Close() - if e != nil { - tflog.Error(ctx, "failed to close SSH client", map[string]interface{}{ - "error": e, - }) - } - }(sshClient) - - if d.ContentType != "" { - remoteFileDir = filepath.Join(remoteFileDir, d.ContentType) - } - - remoteFilePath := strings.ReplaceAll(filepath.Join(remoteFileDir, d.FileName), `\`, `/`) - - sftpClient, err := sftp.NewClient(sshClient) - if err != nil { - return fmt.Errorf("failed to create SFTP client: %w", err) - } - - defer func(sftpClient *sftp.Client) { - e := sftpClient.Close() - if e != nil { - tflog.Error(ctx, "failed to close SFTP client", map[string]interface{}{ - "error": e, - }) - } - }(sftpClient) - - err = sftpClient.MkdirAll(remoteFileDir) - if err != nil { - return fmt.Errorf("failed to create directory %s: %w", remoteFileDir, err) - } - - remoteFile, err := sftpClient.Create(remoteFilePath) - if err != nil { - return fmt.Errorf("failed to create file %s: %w", remoteFilePath, err) - } - - defer func(remoteFile *sftp.File) { - e := remoteFile.Close() - if e != nil { - tflog.Error(ctx, "failed to close remote file", map[string]interface{}{ - "error": e, - }) - } - }(remoteFile) - - bytesUploaded, err := remoteFile.ReadFrom(d.File) - if err != nil { - return fmt.Errorf("failed to upload file %s: %w", remoteFilePath, err) - } - - if bytesUploaded != fileSize { - return fmt.Errorf("failed to upload file %s: uploaded %d bytes, expected %d bytes", - remoteFilePath, bytesUploaded, fileSize) - } - - tflog.Debug(ctx, "uploaded file to datastore", map[string]interface{}{ - "remote_file_path": remoteFilePath, - "size": bytesUploaded, - }) - - return nil -} - -// openNodeShell establishes a new SSH connection to a node. -func (c *client) openNodeShell(ctx context.Context, nodeAddress string) (*ssh.Client, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return nil, fmt.Errorf("failed to determine the home directory: %w", err) - } - - sshHost := fmt.Sprintf("%s:22", nodeAddress) - - sshPath := path.Join(homeDir, ".ssh") - if _, err = os.Stat(sshPath); os.IsNotExist(err) { - e := os.Mkdir(sshPath, 0o700) - if e != nil { - return nil, fmt.Errorf("failed to create %s: %w", sshPath, e) - } - } - - khPath := path.Join(sshPath, "known_hosts") - if _, err = os.Stat(khPath); os.IsNotExist(err) { - e := os.WriteFile(khPath, []byte{}, 0o600) - if e != nil { - return nil, fmt.Errorf("failed to create %s: %w", khPath, e) - } - } - - kh, err := knownhosts.New(khPath) - if err != nil { - return nil, fmt.Errorf("failed to read %s: %w", khPath, err) - } - - // Create a custom permissive hostkey callback which still errors on hosts - // with changed keys, but allows unknown hosts and adds them to known_hosts - cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { - kherr := kh(hostname, remote, key) - if knownhosts.IsHostKeyChanged(kherr) { - return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for host %s! This may indicate a MitM attack", hostname) - } - - if knownhosts.IsHostUnknown(kherr) { - f, ferr := os.OpenFile(khPath, os.O_APPEND|os.O_WRONLY, 0o600) - if ferr == nil { - defer utils.CloseOrLogError(ctx)(f) - ferr = knownhosts.WriteKnownHost(f, hostname, remote, key) - } - if ferr == nil { - tflog.Info(ctx, fmt.Sprintf("Added host %s to known_hosts", hostname)) - } else { - tflog.Error(ctx, fmt.Sprintf("Failed to add host %s to known_hosts", hostname), map[string]interface{}{ - "error": kherr, - }) - } - return nil - } - return kherr - }) - - sshConfig := &ssh.ClientConfig{ - User: c.username, - Auth: []ssh.AuthMethod{ssh.Password(c.password)}, - HostKeyCallback: cb, - HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost), - } - - tflog.Info(ctx, fmt.Sprintf("agent is set to %t", c.agent)) - - var sshClient *ssh.Client - if c.agent { - sshClient, err = c.createSSHClientAgent(ctx, cb, kh, sshHost) - if err != nil { - tflog.Error(ctx, "Failed ssh connection through agent, "+ - "falling back to password authentication", - map[string]interface{}{ - "error": err, - }) - } else { - return sshClient, nil - } - } - - sshClient, err = ssh.Dial("tcp", sshHost, sshConfig) - if err != nil { - return nil, fmt.Errorf("failed to dial %s: %w", sshHost, err) - } - - tflog.Debug(ctx, "SSH connection established", map[string]interface{}{ - "host": sshHost, - "user": c.username, - }) - - return sshClient, nil -} - -// createSSHClientAgent establishes an ssh connection through the agent authentication mechanism. -func (c *client) createSSHClientAgent( - ctx context.Context, - cb ssh.HostKeyCallback, - kh knownhosts.HostKeyCallback, - sshHost string, -) (*ssh.Client, error) { - if c.agentSocket == "" { - return nil, errors.New("failed connecting to SSH agent socket: the socket file is not defined, " + - "authentication will fall back to password") - } - - conn, err := net.Dial("unix", c.agentSocket) - if err != nil { - return nil, fmt.Errorf("failed connecting to SSH auth socket '%s': %w", c.agentSocket, err) - } - - ag := agent.NewClient(conn) - - sshConfig := &ssh.ClientConfig{ - User: c.username, - Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(ag.Signers), ssh.Password(c.password)}, - HostKeyCallback: cb, - HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost), - } - - sshClient, err := ssh.Dial("tcp", sshHost, sshConfig) - if err != nil { - return nil, fmt.Errorf("failed to dial %s: %w", sshHost, err) - } - - tflog.Debug(ctx, "SSH connection established", map[string]interface{}{ - "host": sshHost, - "user": c.username, - }) - - return sshClient, nil -} diff --git a/proxmox/ssh/resolver.go b/proxmox/ssh/resolver.go deleted file mode 100644 index cbe476bc..00000000 --- a/proxmox/ssh/resolver.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package ssh - -import ( - "context" -) - -// NodeResolver is an interface for resolving node names to IP addresses to use for SSH connection. -type NodeResolver interface { - Resolve(ctx context.Context, nodeName string) (string, error) -} diff --git a/proxmox/storage/client.go b/proxmox/storage/client.go deleted file mode 100644 index e66b2585..00000000 --- a/proxmox/storage/client.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package storage - -import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Client is an interface for accessing the Proxmox storage API. -type Client struct { - api.Client -} diff --git a/proxmox/storage/storage.go b/proxmox/storage/storage.go deleted file mode 100644 index c988c593..00000000 --- a/proxmox/storage/storage.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package storage - -import ( - "context" - "fmt" - "net/http" - "net/url" -) - -// GetDatastore retrieves information about a datastore. -/* -Using undocumented API endpoints is not recommended, but sometimes it's the only way to get things done. -$ pvesh get /storage/local -┌─────────┬───────────────────────────────────────────┐ -│ key │ value │ -╞═════════╪═══════════════════════════════════════════╡ -│ content │ images,vztmpl,iso,backup,snippets,rootdir │ -├─────────┼───────────────────────────────────────────┤ -│ digest │ 5b65ede80f34631d6039e6922845cfa4abc956be │ -├─────────┼───────────────────────────────────────────┤ -│ path │ /var/lib/vz │ -├─────────┼───────────────────────────────────────────┤ -│ shared │ 0 │ -├─────────┼───────────────────────────────────────────┤ -│ storage │ local │ -├─────────┼───────────────────────────────────────────┤ -│ type │ dir │ -└─────────┴───────────────────────────────────────────┘. -*/ -func (c *Client) GetDatastore( - ctx context.Context, - datastoreID string, -) (*DatastoreGetResponseData, error) { - resBody := &DatastoreGetResponseBody{} - - err := c.DoRequest( - ctx, - http.MethodGet, - fmt.Sprintf("storage/%s", url.PathEscape(datastoreID)), - nil, - resBody, - ) - if err != nil { - return nil, fmt.Errorf("error retrieving datastore %s: %w", datastoreID, err) - } - - return resBody.Data, nil -} diff --git a/proxmox/storage/storage_types.go b/proxmox/storage/storage_types.go deleted file mode 100644 index 2c2a71b7..00000000 --- a/proxmox/storage/storage_types.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package storage - -import ( - "github.com/bpg/terraform-provider-proxmox/internal/types" -) - -// DatastoreGetResponseBody contains the body from a datastore get response. -type DatastoreGetResponseBody struct { - Data *DatastoreGetResponseData `json:"data,omitempty"` -} - -// DatastoreGetResponseData contains the data from a datastore get response. -type DatastoreGetResponseData struct { - Content types.CustomCommaSeparatedList `json:"content,omitempty" url:"content,omitempty,comma"` - Digest *string `json:"digest,omitempty"` - Path *string `json:"path,omitempty"` - Shared *types.CustomBool `json:"shared,omitempty"` - Storage *string `json:"storage,omitempty"` - Type *string `json:"type,omitempty"` -} diff --git a/proxmox/version/client.go b/proxmox/version/client.go deleted file mode 100644 index b35fb237..00000000 --- a/proxmox/version/client.go +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package version - -import ( - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Client is an interface for accessing the Proxmox version API. -type Client struct { - api.Client -} diff --git a/proxmox/version/version.go b/proxmox/version/version.go deleted file mode 100644 index 2be02189..00000000 --- a/proxmox/version/version.go +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package version - -import ( - "context" - "fmt" - "net/http" - - "github.com/bpg/terraform-provider-proxmox/proxmox/api" -) - -// Version retrieves the version information. -func (c *Client) Version(ctx context.Context) (*ResponseData, error) { - resBody := &ResponseBody{} - - err := c.DoRequest(ctx, http.MethodGet, "version", nil, resBody) - if err != nil { - return nil, fmt.Errorf("failed to get version information: %w", err) - } - - if resBody.Data == nil { - return nil, api.ErrNoDataObjectInResponse - } - - return resBody.Data, nil -} diff --git a/proxmox/version/version_types.go b/proxmox/version/version_types.go deleted file mode 100644 index f908d552..00000000 --- a/proxmox/version/version_types.go +++ /dev/null @@ -1,20 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package version - -// ResponseBody contains the body from a version response. -type ResponseBody struct { - Data *ResponseData `json:"data,omitempty"` -} - -// ResponseData contains the data from a version response. -type ResponseData struct { - Keyboard string `json:"keyboard"` - Release string `json:"release"` - RepositoryID string `json:"repoid"` - Version string `json:"version"` -} diff --git a/proxmoxtf/config.go b/proxmoxtf/config.go index 6dde876c..e69bc9ef 100644 --- a/proxmoxtf/config.go +++ b/proxmoxtf/config.go @@ -9,31 +9,31 @@ package proxmoxtf import ( "errors" - "github.com/bpg/terraform-provider-proxmox/proxmox" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/ssh" + "github.com/bpg/proxmox-api" + "github.com/bpg/proxmox-api/rest" + "github.com/bpg/proxmox-api/ssh" ) // ProviderConfiguration is the configuration for the provider. type ProviderConfiguration struct { - apiClient api.Client - sshClient ssh.Client + restClient rest.Client + sshClient ssh.Client } // NewProviderConfiguration creates a new provider configuration. func NewProviderConfiguration( - apiClient api.Client, + restClient rest.Client, sshClient ssh.Client, ) ProviderConfiguration { return ProviderConfiguration{ - apiClient: apiClient, - sshClient: sshClient, + restClient: restClient, + sshClient: sshClient, } } // GetClient returns the Proxmox API client. func (c *ProviderConfiguration) GetClient() (proxmox.Client, error) { - if c.apiClient == nil { + if c.restClient == nil { return nil, errors.New( "you must specify the API access details in the provider configuration", ) @@ -45,5 +45,5 @@ func (c *ProviderConfiguration) GetClient() (proxmox.Client, error) { ) } - return proxmox.NewClient(c.apiClient, c.sshClient), nil + return proxmox.NewClient(c.restClient, c.sshClient), nil } diff --git a/proxmoxtf/datasource/cluster/firewall.go b/proxmoxtf/datasource/cluster/firewall.go index ad49d54f..d5cf57f4 100644 --- a/proxmoxtf/datasource/cluster/firewall.go +++ b/proxmoxtf/datasource/cluster/firewall.go @@ -12,7 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - fw "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + fw "github.com/bpg/proxmox-api/firewall" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/datasource/firewall" ) diff --git a/proxmoxtf/datasource/firewall/alias.go b/proxmoxtf/datasource/firewall/alias.go index abf144df..8efeba62 100644 --- a/proxmoxtf/datasource/firewall/alias.go +++ b/proxmoxtf/datasource/firewall/alias.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" ) const ( diff --git a/proxmoxtf/datasource/firewall/alias_test.go b/proxmoxtf/datasource/firewall/alias_test.go index 7f2e8a3f..3c3079d2 100644 --- a/proxmoxtf/datasource/firewall/alias_test.go +++ b/proxmoxtf/datasource/firewall/alias_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestAliasSchemaInstantiation tests whether the AliasSchema instance can be instantiated. @@ -25,18 +25,18 @@ func TestAliasSchemaInstantiation(t *testing.T) { func TestAliasSchema(t *testing.T) { t.Parallel() - s := AliasSchema() + r := schema.Resource{Schema: AliasSchema()} - structure.AssertRequiredArguments(t, s, []string{ + test.AssertRequiredArguments(t, &r, []string{ mkAliasName, }) - structure.AssertComputedAttributes(t, s, []string{ + test.AssertComputedAttributes(t, &r, []string{ mkAliasCIDR, mkAliasComment, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, &r, map[string]schema.ValueType{ mkAliasName: schema.TypeString, mkAliasCIDR: schema.TypeString, mkAliasComment: schema.TypeString, diff --git a/proxmoxtf/datasource/firewall/aliases.go b/proxmoxtf/datasource/firewall/aliases.go index 6e850ac0..177b7000 100644 --- a/proxmoxtf/datasource/firewall/aliases.go +++ b/proxmoxtf/datasource/firewall/aliases.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" ) const ( diff --git a/proxmoxtf/datasource/firewall/aliases_test.go b/proxmoxtf/datasource/firewall/aliases_test.go index 17640d18..81c7f9d6 100644 --- a/proxmoxtf/datasource/firewall/aliases_test.go +++ b/proxmoxtf/datasource/firewall/aliases_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestAliasesSchemaInstantiation tests whether the AliasesSchema instance can be instantiated. @@ -25,13 +25,13 @@ func TestAliasesSchemaInstantiation(t *testing.T) { func TestAliasesSchema(t *testing.T) { t.Parallel() - s := AliasesSchema() + r := schema.Resource{Schema: AliasesSchema()} - structure.AssertComputedAttributes(t, s, []string{ + test.AssertComputedAttributes(t, &r, []string{ mkAliasesAliasNames, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, &r, map[string]schema.ValueType{ mkAliasesAliasNames: schema.TypeList, }) } diff --git a/proxmoxtf/datasource/firewall/ipset.go b/proxmoxtf/datasource/firewall/ipset.go index 63b9b82f..4dce6088 100644 --- a/proxmoxtf/datasource/firewall/ipset.go +++ b/proxmoxtf/datasource/firewall/ipset.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" ) const ( diff --git a/proxmoxtf/datasource/firewall/ipset_test.go b/proxmoxtf/datasource/firewall/ipset_test.go index 1dcf25b9..25ea2073 100644 --- a/proxmoxtf/datasource/firewall/ipset_test.go +++ b/proxmoxtf/datasource/firewall/ipset_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestIPSetSchemaInstantiation tests whether the IPSetSchema instance can be instantiated. @@ -25,32 +25,32 @@ func TestIPSetSchemaInstantiation(t *testing.T) { func TestIPSetSchema(t *testing.T) { t.Parallel() - s := IPSetSchema() + r := schema.Resource{Schema: IPSetSchema()} - structure.AssertRequiredArguments(t, s, []string{ + test.AssertRequiredArguments(t, &r, []string{ mkIPSetName, }) - structure.AssertComputedAttributes(t, s, []string{ + test.AssertComputedAttributes(t, &r, []string{ mkIPSetCIDR, mkIPSetCIDRComment, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, &r, map[string]schema.ValueType{ mkIPSetName: schema.TypeString, mkIPSetCIDR: schema.TypeList, mkIPSetCIDRComment: schema.TypeString, }) - cirdSchema := structure.AssertNestedSchemaExistence(t, s, mkIPSetCIDR).Schema + cidr := test.AssertNestedSchemaExistence(t, &r, mkIPSetCIDR) - structure.AssertComputedAttributes(t, cirdSchema, []string{ + test.AssertComputedAttributes(t, cidr, []string{ mkIPSetCIDRName, mkIPSetCIDRNoMatch, mkIPSetCIDRComment, }) - structure.AssertValueTypes(t, cirdSchema, map[string]schema.ValueType{ + test.AssertValueTypes(t, cidr, map[string]schema.ValueType{ mkIPSetCIDRName: schema.TypeString, mkIPSetCIDRNoMatch: schema.TypeBool, mkIPSetCIDRComment: schema.TypeString, diff --git a/proxmoxtf/datasource/firewall/ipsets.go b/proxmoxtf/datasource/firewall/ipsets.go index c25e5d6c..f17fecbb 100644 --- a/proxmoxtf/datasource/firewall/ipsets.go +++ b/proxmoxtf/datasource/firewall/ipsets.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" ) const ( diff --git a/proxmoxtf/datasource/firewall/ipsets_test.go b/proxmoxtf/datasource/firewall/ipsets_test.go index 03446766..d48f0cb4 100644 --- a/proxmoxtf/datasource/firewall/ipsets_test.go +++ b/proxmoxtf/datasource/firewall/ipsets_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestIPSetsSchemaInstantiation tests whether the IPSetsSchema instance can be instantiated. @@ -25,13 +25,13 @@ func TestIPSetsSchemaInstantiation(t *testing.T) { func TestIPSetsSchema(t *testing.T) { t.Parallel() - s := IPSetsSchema() + r := schema.Resource{Schema: IPSetsSchema()} - structure.AssertComputedAttributes(t, s, []string{ + test.AssertComputedAttributes(t, &r, []string{ mkIPSetsIPSetNames, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, &r, map[string]schema.ValueType{ mkIPSetsIPSetNames: schema.TypeList, }) } diff --git a/proxmoxtf/datasource/firewall/rule_test.go b/proxmoxtf/datasource/firewall/rule_test.go index c137d5f7..58d44fdc 100644 --- a/proxmoxtf/datasource/firewall/rule_test.go +++ b/proxmoxtf/datasource/firewall/rule_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestRuleInstantiation tests whether the RuleSchema instance can be instantiated. @@ -25,14 +25,14 @@ func TestRuleSchemaInstantiation(t *testing.T) { func TestRuleSchema(t *testing.T) { t.Parallel() - ruleSchema := RuleSchema() + r := schema.Resource{Schema: RuleSchema()} - structure.AssertRequiredArguments(t, ruleSchema, []string{ + test.AssertRequiredArguments(t, &r, []string{ mkRuleAction, mkRuleType, }) - structure.AssertComputedAttributes(t, ruleSchema, []string{ + test.AssertComputedAttributes(t, &r, []string{ mkRuleComment, mkRuleDest, mkRuleDPort, @@ -45,7 +45,7 @@ func TestRuleSchema(t *testing.T) { mkRuleSPort, }) - structure.AssertValueTypes(t, ruleSchema, map[string]schema.ValueType{ + test.AssertValueTypes(t, &r, map[string]schema.ValueType{ mkRulePos: schema.TypeInt, mkRuleAction: schema.TypeString, mkRuleType: schema.TypeString, diff --git a/proxmoxtf/datasource/firewall/security_group.go b/proxmoxtf/datasource/firewall/security_group.go index aaecd4c2..1d06c51f 100644 --- a/proxmoxtf/datasource/firewall/security_group.go +++ b/proxmoxtf/datasource/firewall/security_group.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/firewall" + "github.com/bpg/proxmox-api/cluster/firewall" ) const ( diff --git a/proxmoxtf/datasource/firewall/security_group_test.go b/proxmoxtf/datasource/firewall/security_group_test.go index 1066c0a0..d9c29011 100644 --- a/proxmoxtf/datasource/firewall/security_group_test.go +++ b/proxmoxtf/datasource/firewall/security_group_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestSecurityGroupSchemaInstantiation tests whether the SecurityGroupSchema instance can be instantiated. @@ -25,18 +25,18 @@ func TestSecurityGroupSchemaInstantiation(t *testing.T) { func TestSecurityGroupSchema(t *testing.T) { t.Parallel() - s := SecurityGroupSchema() + r := schema.Resource{Schema: SecurityGroupSchema()} - structure.AssertRequiredArguments(t, s, []string{ + test.AssertRequiredArguments(t, &r, []string{ mkSecurityGroupName, }) - structure.AssertComputedAttributes(t, s, []string{ + test.AssertComputedAttributes(t, &r, []string{ mkSecurityGroupComment, mkRules, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, &r, map[string]schema.ValueType{ mkSecurityGroupName: schema.TypeString, mkSecurityGroupComment: schema.TypeString, mkRules: schema.TypeList, diff --git a/proxmoxtf/datasource/firewall/security_groups.go b/proxmoxtf/datasource/firewall/security_groups.go index ea8beb05..33d30e5a 100644 --- a/proxmoxtf/datasource/firewall/security_groups.go +++ b/proxmoxtf/datasource/firewall/security_groups.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/firewall" + "github.com/bpg/proxmox-api/cluster/firewall" ) const ( diff --git a/proxmoxtf/datasource/firewall/security_groups_test.go b/proxmoxtf/datasource/firewall/security_groups_test.go index d3995905..9ed00972 100644 --- a/proxmoxtf/datasource/firewall/security_groups_test.go +++ b/proxmoxtf/datasource/firewall/security_groups_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestSecurityGroupsSchemaInstantiation tests whether the SecurityGroupsSchema instance can be instantiated. @@ -25,13 +25,13 @@ func TestSecurityGroupsSchemaInstantiation(t *testing.T) { func TestSecurityGroupsSchema(t *testing.T) { t.Parallel() - s := SecurityGroupsSchema() + r := schema.Resource{Schema: SecurityGroupsSchema()} - structure.AssertComputedAttributes(t, s, []string{ + test.AssertComputedAttributes(t, &r, []string{ mkSecurityGroupsSecurityGroupNames, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, &r, map[string]schema.ValueType{ mkSecurityGroupsSecurityGroupNames: schema.TypeList, }) } diff --git a/proxmoxtf/datasource/vms.go b/proxmoxtf/datasource/vms.go index 5bbcd204..62d49a7c 100644 --- a/proxmoxtf/datasource/vms.go +++ b/proxmoxtf/datasource/vms.go @@ -17,7 +17,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "golang.org/x/exp/slices" - "github.com/bpg/terraform-provider-proxmox/proxmox" + "github.com/bpg/proxmox-api" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/provider/provider.go b/proxmoxtf/provider/provider.go index 1a7f8794..851a1443 100644 --- a/proxmoxtf/provider/provider.go +++ b/proxmoxtf/provider/provider.go @@ -11,12 +11,13 @@ import ( "fmt" "strings" + "github.com/bpg/proxmox-api/rest" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" - "github.com/bpg/terraform-provider-proxmox/proxmox/ssh" + "github.com/bpg/proxmox-api/nodes" + "github.com/bpg/proxmox-api/ssh" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/utils" ) @@ -36,13 +37,13 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, var diags diag.Diagnostics - var apiClient api.Client + var restClient rest.Client var sshClient ssh.Client - var creds *api.Credentials + var creds *rest.Credentials - var conn *api.Connection + var conn *rest.Connection // Check environment variables apiToken := utils.GetAnyStringEnv("PROXMOX_VE_API_TOKEN", "PM_VE_API_TOKEN") @@ -76,17 +77,17 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, otp = v.(string) } - creds, err = api.NewCredentials(username, password, otp, apiToken) + creds, err = rest.NewCredentials(username, password, otp, apiToken) diags = append(diags, diag.FromErr(err)...) - conn, err = api.NewConnection(endpoint, insecure) + conn, err = rest.NewConnection(endpoint, insecure) diags = append(diags, diag.FromErr(err)...) if diags.HasError() { return nil, diags } - apiClient, err = api.NewClient(creds, conn) + restClient, err = rest.NewClient(creds, conn) if err != nil { return nil, diag.Errorf("error creating virtual environment client: %s", err) } @@ -144,7 +145,7 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, sshConf[mkProviderSSHAgent].(bool), sshConf[mkProviderSSHAgentSocket].(string), &apiResolverWithOverrides{ - ar: apiResolver{c: apiClient}, + ar: apiResolver{c: restClient}, overrides: nodeOverrides, }, ) @@ -152,13 +153,13 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, return nil, diag.Errorf("error creating SSH client: %s", err) } - config := proxmoxtf.NewProviderConfiguration(apiClient, sshClient) + config := proxmoxtf.NewProviderConfiguration(restClient, sshClient) return config, nil } type apiResolver struct { - c api.Client + c rest.Client } func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (string, error) { diff --git a/proxmoxtf/resource/certificate.go b/proxmoxtf/resource/certificate.go index 65ed7cd2..ebdf22d3 100644 --- a/proxmoxtf/resource/certificate.go +++ b/proxmoxtf/resource/certificate.go @@ -12,11 +12,12 @@ import ( "strings" "time" + "github.com/bpg/proxmox-api/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" + "github.com/bpg/proxmox-api/nodes" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/cluster/firewall/firewall.go b/proxmoxtf/resource/cluster/firewall/firewall.go index 47e2412b..9566a311 100644 --- a/proxmoxtf/resource/cluster/firewall/firewall.go +++ b/proxmoxtf/resource/cluster/firewall/firewall.go @@ -12,8 +12,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/firewall" + "github.com/bpg/proxmox-api/cluster/firewall" + "github.com/bpg/proxmox-api/types" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" ) diff --git a/proxmoxtf/resource/cluster/firewall/security_group.go b/proxmoxtf/resource/cluster/firewall/security_group.go index 710d25d9..0c91e003 100644 --- a/proxmoxtf/resource/cluster/firewall/security_group.go +++ b/proxmoxtf/resource/cluster/firewall/security_group.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - clusterfirewall "github.com/bpg/terraform-provider-proxmox/proxmox/cluster/firewall" + clusterfirewall "github.com/bpg/proxmox-api/cluster/firewall" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/firewall" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" ) diff --git a/proxmoxtf/resource/cluster/firewall/security_group_test.go b/proxmoxtf/resource/cluster/firewall/security_group_test.go index 0c8e599c..591d411a 100644 --- a/proxmoxtf/resource/cluster/firewall/security_group_test.go +++ b/proxmoxtf/resource/cluster/firewall/security_group_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/firewall" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestSecurityGroupInstantiation tests whether the SecurityGroup instance can be instantiated. @@ -26,20 +26,20 @@ func TestSecurityGroupInstantiation(t *testing.T) { func TestSecurityGroupSchema(t *testing.T) { t.Parallel() - s := SecurityGroup().Schema + r := SecurityGroup() - structure.AssertRequiredArguments(t, s, []string{ + test.AssertRequiredArguments(t, r, []string{ mkSecurityGroupName, }) - structure.AssertOptionalArguments(t, s, []string{ + test.AssertOptionalArguments(t, r, []string{ mkSecurityGroupComment, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, r, map[string]schema.ValueType{ mkSecurityGroupName: schema.TypeString, mkSecurityGroupComment: schema.TypeString, }) - structure.AssertNestedSchemaExistence(t, s, firewall.MkRule) + test.AssertNestedSchemaExistence(t, r, firewall.MkRule) } diff --git a/proxmoxtf/resource/container.go b/proxmoxtf/resource/container.go index d8305e3e..f18a601c 100644 --- a/proxmoxtf/resource/container.go +++ b/proxmoxtf/resource/container.go @@ -17,10 +17,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers" + "github.com/bpg/proxmox-api/nodes/containers" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" + + "github.com/bpg/proxmox-api/types" ) const ( diff --git a/proxmoxtf/resource/dns.go b/proxmoxtf/resource/dns.go index 3336baa3..a472a95a 100644 --- a/proxmoxtf/resource/dns.go +++ b/proxmoxtf/resource/dns.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" + "github.com/bpg/proxmox-api/nodes" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/file.go b/proxmoxtf/resource/file.go index c63eaf67..1b814ff1 100644 --- a/proxmoxtf/resource/file.go +++ b/proxmoxtf/resource/file.go @@ -21,13 +21,13 @@ import ( "strings" "time" + "github.com/bpg/proxmox-api/rest" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "golang.org/x/exp/slices" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" "github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/utils" ) @@ -416,7 +416,7 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag return diag.FromErr(err) } - request := &api.FileUploadRequest{ + request := &rest.FileUploadRequest{ ContentType: *contentType, FileName: *fileName, File: file, diff --git a/proxmoxtf/resource/firewall/alias.go b/proxmoxtf/resource/firewall/alias.go index 45ced202..c2314c5d 100644 --- a/proxmoxtf/resource/firewall/alias.go +++ b/proxmoxtf/resource/firewall/alias.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" ) diff --git a/proxmoxtf/resource/firewall/alias_test.go b/proxmoxtf/resource/firewall/alias_test.go index 0f3392d7..d7882817 100644 --- a/proxmoxtf/resource/firewall/alias_test.go +++ b/proxmoxtf/resource/firewall/alias_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestAliasInstantiation tests whether the Alias instance can be instantiated. @@ -25,20 +25,20 @@ func TestAliasInstantiation(t *testing.T) { func TestAliasSchema(t *testing.T) { t.Parallel() - s := Alias().Schema + r := Alias() - structure.AssertRequiredArguments(t, s, []string{ + test.AssertRequiredArguments(t, r, []string{ mkAliasName, mkAliasCIDR, }) - structure.AssertOptionalArguments(t, s, []string{ + test.AssertOptionalArguments(t, r, []string{ mkSelectorVMID, mkSelectorNodeName, mkAliasComment, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, r, map[string]schema.ValueType{ mkAliasName: schema.TypeString, mkAliasCIDR: schema.TypeString, mkAliasComment: schema.TypeString, diff --git a/proxmoxtf/resource/firewall/ipset.go b/proxmoxtf/resource/firewall/ipset.go index ea132c1b..988c46e8 100644 --- a/proxmoxtf/resource/firewall/ipset.go +++ b/proxmoxtf/resource/firewall/ipset.go @@ -13,9 +13,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + + "github.com/bpg/proxmox-api/types" ) const ( diff --git a/proxmoxtf/resource/firewall/ipset_test.go b/proxmoxtf/resource/firewall/ipset_test.go index 8f2b0137..d593e22c 100644 --- a/proxmoxtf/resource/firewall/ipset_test.go +++ b/proxmoxtf/resource/firewall/ipset_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestIPSetInstantiation tests whether the IPSet @@ -26,37 +26,37 @@ func TestIPSetInstantiation(t *testing.T) { func TestIPSetSchema(t *testing.T) { t.Parallel() - s := IPSet().Schema + r := IPSet() - structure.AssertRequiredArguments(t, s, []string{ + test.AssertRequiredArguments(t, r, []string{ mkIPSetName, }) - structure.AssertOptionalArguments(t, s, []string{ + test.AssertOptionalArguments(t, r, []string{ mkSelectorVMID, mkSelectorNodeName, mkIPSetCIDR, mkIPSetCIDRComment, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, r, map[string]schema.ValueType{ mkIPSetName: schema.TypeString, mkIPSetCIDR: schema.TypeList, mkIPSetCIDRComment: schema.TypeString, }) - nested := structure.AssertNestedSchemaExistence(t, s, mkIPSetCIDR).Schema + nested := test.AssertNestedSchemaExistence(t, r, mkIPSetCIDR) - structure.AssertRequiredArguments(t, nested, []string{ + test.AssertRequiredArguments(t, nested, []string{ mkIPSetCIDRName, }) - structure.AssertOptionalArguments(t, nested, []string{ + test.AssertOptionalArguments(t, nested, []string{ mkIPSetCIDRComment, mkIPSetCIDRNoMatch, }) - structure.AssertValueTypes(t, nested, map[string]schema.ValueType{ + test.AssertValueTypes(t, nested, map[string]schema.ValueType{ mkIPSetCIDRName: schema.TypeString, mkIPSetCIDRComment: schema.TypeString, mkIPSetCIDRNoMatch: schema.TypeBool, diff --git a/proxmoxtf/resource/firewall/options.go b/proxmoxtf/resource/firewall/options.go index c7461d8c..24da5a74 100644 --- a/proxmoxtf/resource/firewall/options.go +++ b/proxmoxtf/resource/firewall/options.go @@ -12,10 +12,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + + "github.com/bpg/proxmox-api/types" ) const ( diff --git a/proxmoxtf/resource/firewall/options_test.go b/proxmoxtf/resource/firewall/options_test.go index 179236e6..bfa10122 100644 --- a/proxmoxtf/resource/firewall/options_test.go +++ b/proxmoxtf/resource/firewall/options_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestOptionsInstantiation tests whether the Options instance can be instantiated. @@ -25,9 +25,9 @@ func TestOptionsInstantiation(t *testing.T) { func TestOptionsSchema(t *testing.T) { t.Parallel() - s := Options().Schema + r := Options() - structure.AssertOptionalArguments(t, s, []string{ + test.AssertOptionalArguments(t, r, []string{ mkDHCP, mkEnabled, mkIPFilter, @@ -40,7 +40,7 @@ func TestOptionsSchema(t *testing.T) { mkRadv, }) - structure.AssertValueTypes(t, s, map[string]schema.ValueType{ + test.AssertValueTypes(t, r, map[string]schema.ValueType{ mkDHCP: schema.TypeBool, mkEnabled: schema.TypeBool, mkIPFilter: schema.TypeBool, diff --git a/proxmoxtf/resource/firewall/rules.go b/proxmoxtf/resource/firewall/rules.go index 88c59c49..93fa2554 100644 --- a/proxmoxtf/resource/firewall/rules.go +++ b/proxmoxtf/resource/firewall/rules.go @@ -16,10 +16,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + + "github.com/bpg/proxmox-api/types" ) const ( diff --git a/proxmoxtf/resource/firewall/rules_test.go b/proxmoxtf/resource/firewall/rules_test.go index a66a765d..a12bf5dd 100644 --- a/proxmoxtf/resource/firewall/rules_test.go +++ b/proxmoxtf/resource/firewall/rules_test.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/require" - "github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" ) // TestRuleInstantiation tests whether the Rules instance can be instantiated. @@ -25,20 +25,20 @@ func TestRuleInstantiation(t *testing.T) { func TestRuleSchema(t *testing.T) { t.Parallel() - rulesSchema := Rules().Schema + rules := Rules() - structure.AssertRequiredArguments(t, rulesSchema, []string{ + test.AssertRequiredArguments(t, rules, []string{ MkRule, }) - structure.AssertOptionalArguments(t, rulesSchema, []string{ + test.AssertOptionalArguments(t, rules, []string{ mkSelectorVMID, mkSelectorNodeName, }) - ruleSchema := structure.AssertNestedSchemaExistence(t, rulesSchema, MkRule).Schema + ruleSchema := test.AssertNestedSchemaExistence(t, rules, MkRule) - structure.AssertOptionalArguments(t, ruleSchema, []string{ + test.AssertOptionalArguments(t, ruleSchema, []string{ mkSecurityGroup, mkRuleAction, mkRuleType, @@ -54,7 +54,7 @@ func TestRuleSchema(t *testing.T) { mkRuleSPort, }) - structure.AssertValueTypes(t, ruleSchema, map[string]schema.ValueType{ + test.AssertValueTypes(t, ruleSchema, map[string]schema.ValueType{ mkRulePos: schema.TypeInt, mkRuleAction: schema.TypeString, mkRuleType: schema.TypeString, diff --git a/proxmoxtf/resource/firewall/selector.go b/proxmoxtf/resource/firewall/selector.go index 53f3afa0..0d3cec03 100644 --- a/proxmoxtf/resource/firewall/selector.go +++ b/proxmoxtf/resource/firewall/selector.go @@ -12,7 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/firewall" + "github.com/bpg/proxmox-api/firewall" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" ) diff --git a/proxmoxtf/resource/group.go b/proxmoxtf/resource/group.go index 960e7e82..7a72a4a1 100644 --- a/proxmoxtf/resource/group.go +++ b/proxmoxtf/resource/group.go @@ -10,11 +10,12 @@ import ( "context" "strings" + "github.com/bpg/proxmox-api/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/access" + "github.com/bpg/proxmox-api/access" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/hosts.go b/proxmoxtf/resource/hosts.go index 33e77433..3dc34f72 100644 --- a/proxmoxtf/resource/hosts.go +++ b/proxmoxtf/resource/hosts.go @@ -14,7 +14,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" + "github.com/bpg/proxmox-api/nodes" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/pool.go b/proxmoxtf/resource/pool.go index 658a278a..13984c75 100644 --- a/proxmoxtf/resource/pool.go +++ b/proxmoxtf/resource/pool.go @@ -13,7 +13,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/pools" + "github.com/bpg/proxmox-api/pools" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/role.go b/proxmoxtf/resource/role.go index 4afd96d9..5a9ce9cc 100644 --- a/proxmoxtf/resource/role.go +++ b/proxmoxtf/resource/role.go @@ -10,11 +10,12 @@ import ( "context" "strings" + "github.com/bpg/proxmox-api/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/access" + "github.com/bpg/proxmox-api/access" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/time.go b/proxmoxtf/resource/time.go index 135f92d7..4ffa85c8 100644 --- a/proxmoxtf/resource/time.go +++ b/proxmoxtf/resource/time.go @@ -14,7 +14,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" + "github.com/bpg/proxmox-api/nodes" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/user.go b/proxmoxtf/resource/user.go index 50e45d70..6a0ee218 100644 --- a/proxmoxtf/resource/user.go +++ b/proxmoxtf/resource/user.go @@ -11,12 +11,13 @@ import ( "strings" "time" + "github.com/bpg/proxmox-api/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/access" + "github.com/bpg/proxmox-api/access" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" ) diff --git a/proxmoxtf/resource/utils.go b/proxmoxtf/resource/utils.go index 9bdcdc30..9e6b6483 100644 --- a/proxmoxtf/resource/utils.go +++ b/proxmoxtf/resource/utils.go @@ -18,8 +18,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" + "github.com/bpg/proxmox-api/nodes/vms" + + "github.com/bpg/proxmox-api/types" ) func getBIOSValidator() schema.SchemaValidateDiagFunc { diff --git a/proxmoxtf/resource/vm.go b/proxmoxtf/resource/vm.go index 75200ebc..d81cabcb 100644 --- a/proxmoxtf/resource/vm.go +++ b/proxmoxtf/resource/vm.go @@ -20,11 +20,13 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/bpg/terraform-provider-proxmox/internal/types" - "github.com/bpg/terraform-provider-proxmox/proxmox/cluster" - "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" + "github.com/bpg/proxmox-api/cluster" + "github.com/bpg/proxmox-api/nodes/vms" + "github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" + + "github.com/bpg/proxmox-api/types" ) const ( diff --git a/proxmoxtf/structure/tools.go b/proxmoxtf/structure/merge.go similarity index 100% rename from proxmoxtf/structure/tools.go rename to proxmoxtf/structure/merge.go diff --git a/proxmoxtf/structure/test.go b/proxmoxtf/structure/test.go deleted file mode 100644 index 8b0bad08..00000000 --- a/proxmoxtf/structure/test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package structure - -import ( - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// AssertComputedAttributes asserts that the given keys are present in the schema and are computed. -func AssertComputedAttributes(t *testing.T, s map[string]*schema.Schema, keys []string) { - t.Helper() - - for _, v := range keys { - require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v) - assert.True(t, s[v].Computed, "Error in Schema: Attribute \"%s\" is not computed", v) - } -} - -// AssertNestedSchemaExistence asserts that the given key is present in the schema and is a nested schema. -func AssertNestedSchemaExistence(t *testing.T, s map[string]*schema.Schema, key string) *schema.Resource { - t.Helper() - - sh, ok := s[key].Elem.(*schema.Resource) - - if !ok { - t.Fatalf("Error in Schema: Missing nested schema for \"%s\"", key) - - return nil - } - - return sh -} - -// AssertOptionalArguments asserts that the given keys are present in the schema and are optional. -func AssertOptionalArguments(t *testing.T, s map[string]*schema.Schema, keys []string) { - t.Helper() - - for _, v := range keys { - require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v) - assert.True(t, s[v].Optional, "Error in Schema: Argument \"%s\" is not optional", v) - } -} - -// AssertRequiredArguments asserts that the given keys are present in the schema and are required. -func AssertRequiredArguments(t *testing.T, s map[string]*schema.Schema, keys []string) { - t.Helper() - - for _, v := range keys { - require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v) - assert.True(t, s[v].Required, "Error in Schema: Argument \"%s\" is not required", v) - } -} - -// AssertValueTypes asserts that the given keys are present in the schema and are of the given type. -func AssertValueTypes(t *testing.T, s map[string]*schema.Schema, f map[string]schema.ValueType) { - t.Helper() - - for fn, ft := range f { - require.NotNil(t, s[fn], "Error in Schema: Missing definition for \"%s\"", fn) - assert.Equal(t, ft, s[fn].Type, "Error in Schema: Argument or attribute \"%s\" is not of type \"%v\"", fn, ft) - } -} diff --git a/proxmoxtf/test/test_utils.go b/proxmoxtf/test/utils.go similarity index 100% rename from proxmoxtf/test/test_utils.go rename to proxmoxtf/test/utils.go