From 78496c9e26663aba9bc934a6fad44482c53d4994 Mon Sep 17 00:00:00 2001 From: Dan Petersen Date: Sat, 7 Dec 2019 22:21:53 +0100 Subject: [PATCH] Latest work --- CHANGELOG.md | 1 + ...source_virtual_environment_access_group.go | 66 +++++++++++++++++++ ...e_virtual_environment_access_group_test.go | 38 +++++++++++ ...source_virtual_environment_access_group.tf | 12 ++++ ...ource_virtual_environment_access_groups.tf | 2 +- example/provider_configuration.tf | 10 +-- go.mod | 5 +- go.sum | 1 + provider.go | 1 + proxmox/virtual_environment.go | 31 ++++++--- proxmox/virtual_environment_access_groups.go | 51 ++++++++++++++ 11 files changed, 203 insertions(+), 15 deletions(-) create mode 100644 data_source_virtual_environment_access_group.go create mode 100644 data_source_virtual_environment_access_group_test.go create mode 100644 example/data_source_virtual_environment_access_group.tf diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d434fe6..5bc8ddfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,5 +2,6 @@ FEATURES: +* **New Data Source:** `proxmox_virtual_environment_access_group` * **New Data Source:** `proxmox_virtual_environment_access_groups` * **New Data Source:** `proxmox_virtual_environment_version` diff --git a/data_source_virtual_environment_access_group.go b/data_source_virtual_environment_access_group.go new file mode 100644 index 00000000..a395bebe --- /dev/null +++ b/data_source_virtual_environment_access_group.go @@ -0,0 +1,66 @@ +/* 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 main + +import ( + "errors" + + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + mkDataSourceVirtualEnvironmentAccessGroupComment = "comment" + mkDataSourceVirtualEnvironmentAccessGroupID = "id" + mkDataSourceVirtualEnvironmentAccessGroupMembers = "members" +) + +// dataSourceVirtualEnvironmentAccessGroup retrieves a list of access groups. +func dataSourceVirtualEnvironmentAccessGroup() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkDataSourceVirtualEnvironmentAccessGroupComment: &schema.Schema{ + Type: schema.TypeString, + Description: "The group comment", + Computed: true, + }, + mkDataSourceVirtualEnvironmentAccessGroupID: &schema.Schema{ + Type: schema.TypeString, + Description: "The group id", + Required: true, + }, + mkDataSourceVirtualEnvironmentAccessGroupMembers: &schema.Schema{ + Type: schema.TypeList, + Description: "The group members", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + Read: dataSourceVirtualEnvironmentAccessGroupRead, + } +} + +// dataSourceVirtualEnvironmentAccessGroupRead retrieves a list of access groups. +func dataSourceVirtualEnvironmentAccessGroupRead(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + + if config.veClient == nil { + return errors.New("You must specify the virtual environment details in the provider configuration to use this data source") + } + + groupID := d.Get(mkDataSourceVirtualEnvironmentAccessGroupID).(string) + + accessGroup, err := config.veClient.GetAccessGroup(groupID) + + if err != nil { + return err + } + + d.SetId(groupID) + + d.Set(mkDataSourceVirtualEnvironmentAccessGroupComment, accessGroup.Comment) + d.Set(mkDataSourceVirtualEnvironmentAccessGroupMembers, accessGroup.Members) + + return nil +} diff --git a/data_source_virtual_environment_access_group_test.go b/data_source_virtual_environment_access_group_test.go new file mode 100644 index 00000000..d7b59ec8 --- /dev/null +++ b/data_source_virtual_environment_access_group_test.go @@ -0,0 +1,38 @@ +/* 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 main + +import ( + "testing" +) + +// TestDataSourceVirtualEnvironmentAccessGroupInstantiation tests whether the DataSourceVirtualEnvironmentAccessGroup instance can be instantiated. +func TestDataSourceVirtualEnvironmentAccessGroupInstantiation(t *testing.T) { + s := dataSourceVirtualEnvironmentAccessGroup() + + if s == nil { + t.Fatalf("Cannot instantiate dataSourceVirtualEnvironmentAccessGroup") + } +} + +// TestDataSourceVirtualEnvironmentAccessGroupSchema tests the dataSourceVirtualEnvironmentAccessGroup schema. +func TestDataSourceVirtualEnvironmentAccessGroupSchema(t *testing.T) { + s := dataSourceVirtualEnvironmentAccessGroup() + + attributeKeys := []string{ + mkDataSourceVirtualEnvironmentAccessGroupComment, + mkDataSourceVirtualEnvironmentAccessGroupMembers, + } + + for _, v := range attributeKeys { + if s.Schema[v] == nil { + t.Fatalf("Error in dataSourceVirtualEnvironmentAccessGroup.Schema: Missing attribute \"%s\"", v) + } + + if s.Schema[v].Computed != true { + t.Fatalf("Error in dataSourceVirtualEnvironmentAccessGroup.Schema: Attribute \"%s\" is not computed", v) + } + } +} diff --git a/example/data_source_virtual_environment_access_group.tf b/example/data_source_virtual_environment_access_group.tf new file mode 100644 index 00000000..a01b584e --- /dev/null +++ b/example/data_source_virtual_environment_access_group.tf @@ -0,0 +1,12 @@ +data "proxmox_virtual_environment_access_group" "example" { + count = length(data.proxmox_virtual_environment_access_groups.example.ids) + id = element(data.proxmox_virtual_environment_access_groups.example.ids, count.index) +} + +output "data_proxmox_virtual_environment_access_group_example_comments" { + value = data.proxmox_virtual_environment_access_group.example.*.comment +} + +output "data_proxmox_virtual_environment_access_group_example_members" { + value = data.proxmox_virtual_environment_access_group.example.*.members +} diff --git a/example/data_source_virtual_environment_access_groups.tf b/example/data_source_virtual_environment_access_groups.tf index 0a03f909..09e29021 100644 --- a/example/data_source_virtual_environment_access_groups.tf +++ b/example/data_source_virtual_environment_access_groups.tf @@ -1,6 +1,6 @@ data "proxmox_virtual_environment_access_groups" "example" {} -output "data_proxmox_virtual_environment_access_groups" { +output "data_proxmox_virtual_environment_access_groups_example" { value = "${map( "comments", data.proxmox_virtual_environment_access_groups.example.comments, "ids", data.proxmox_virtual_environment_access_groups.example.ids, diff --git a/example/provider_configuration.tf b/example/provider_configuration.tf index 99254664..adc2c34b 100644 --- a/example/provider_configuration.tf +++ b/example/provider_configuration.tf @@ -12,12 +12,12 @@ variable "virtual_environment_endpoint" { description = "The endpoint for the Proxmox Virtual Environment API (example: https://host:port)" } -variable "virtual_environment_username" { - type = "string" - description = "The username for the Proxmox Virtual Environment API (example: root@pam)" -} - variable "virtual_environment_password" { type = "string" description = "The password for the Proxmox Virtual Environment API" } + +variable "virtual_environment_username" { + type = "string" + description = "The username and realm for the Proxmox Virtual Environment API (example: root@pam)" +} diff --git a/go.mod b/go.mod index 63a4b7e0..b63c5d55 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/danitso/terraform-provider-proxmox go 1.12 -require github.com/hashicorp/terraform v0.12.17 +require ( + github.com/google/go-querystring v1.0.0 + github.com/hashicorp/terraform v0.12.17 +) diff --git a/go.sum b/go.sum index d31ad6b3..3b6b40c1 100644 --- a/go.sum +++ b/go.sum @@ -111,6 +111,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= diff --git a/provider.go b/provider.go index b341bde4..6c67f164 100644 --- a/provider.go +++ b/provider.go @@ -26,6 +26,7 @@ func Provider() *schema.Provider { return &schema.Provider{ ConfigureFunc: providerConfigure, DataSourcesMap: map[string]*schema.Resource{ + "proxmox_virtual_environment_access_group": dataSourceVirtualEnvironmentAccessGroup(), "proxmox_virtual_environment_access_groups": dataSourceVirtualEnvironmentAccessGroups(), "proxmox_virtual_environment_version": dataSourceVirtualEnvironmentVersion(), }, diff --git a/proxmox/virtual_environment.go b/proxmox/virtual_environment.go index 7719b8c4..a4ca59b9 100644 --- a/proxmox/virtual_environment.go +++ b/proxmox/virtual_environment.go @@ -13,12 +13,15 @@ import ( "net/http" "net/url" "strings" + + "github.com/google/go-querystring/query" ) const ( basePathJSONAPI = "api2/json" hmDELETE = "DELETE" hmGET = "GET" + hmHEAD = "HEAD" hmPOST = "POST" hmPUT = "PUT" ) @@ -73,30 +76,40 @@ func NewVirtualEnvironmentClient(endpoint, username, password string, insecure b // DoRequest performs a HTTP request against a JSON API endpoint. func (c *VirtualEnvironmentClient) DoRequest(method, path string, requestBody interface{}, responseBody interface{}) error { - jsonRequestBody := new(bytes.Buffer) + urlEncodedRequestBody := new(bytes.Buffer) if requestBody != nil { - err := json.NewEncoder(jsonRequestBody).Encode(requestBody) + if method == hmGET || method == hmHEAD { + return fmt.Errorf("A request body must not be specified for %s/%s requests", hmGET, hmHEAD) + } + + v, err := query.Values(requestBody) if err != nil { return fmt.Errorf("Failed to encode HTTP %s request (path: %s)", method, path) } + + urlEncodedRequestBody = bytes.NewBufferString(v.Encode()) } - req, err := http.NewRequest(method, fmt.Sprintf("%s/%s/%s", c.Endpoint, basePathJSONAPI, path), jsonRequestBody) + req, err := http.NewRequest(method, fmt.Sprintf("%s/%s/%s", c.Endpoint, basePathJSONAPI, path), urlEncodedRequestBody) if err != nil { return fmt.Errorf("Failed to create HTTP %s request (path: %s)", method, path) } + req.Header.Add("Accept", "application/json") + + if req.Method != hmGET && req.Method != hmHEAD { + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + } + err = c.AuthenticateRequest(req) if err != nil { return err } - req.Header.Add("Content-Type", "application/json") - res, err := c.httpClient.Do(req) if err != nil { @@ -109,10 +122,12 @@ func (c *VirtualEnvironmentClient) DoRequest(method, path string, requestBody in return err } - err = json.NewDecoder(res.Body).Decode(responseBody) + if responseBody != nil { + err = json.NewDecoder(res.Body).Decode(responseBody) - if err != nil { - return fmt.Errorf("Failed to decode HTTP %s response (path: %s)", method, path) + if err != nil { + return fmt.Errorf("Failed to decode HTTP %s response (path: %s)", method, path) + } } return nil diff --git a/proxmox/virtual_environment_access_groups.go b/proxmox/virtual_environment_access_groups.go index a1181deb..7ac7510f 100644 --- a/proxmox/virtual_environment_access_groups.go +++ b/proxmox/virtual_environment_access_groups.go @@ -6,8 +6,27 @@ package proxmox import ( "errors" + "fmt" + "net/url" ) +// VirtualEnvironmentAccessGroupCreateRequestData contains the data for an access group create request. +type VirtualEnvironmentAccessGroupCreateRequestData struct { + Comment string `json:"comment" url:"comment"` + ID string `json:"groupid" url:"groupid"` +} + +// VirtualEnvironmentAccessGroupGetResponseBody contains the body from an access group get response. +type VirtualEnvironmentAccessGroupGetResponseBody struct { + Data *VirtualEnvironmentAccessGroupGetResponseData `json:"data,omitempty"` +} + +// VirtualEnvironmentAccessGroupGetResponseData contains the data from an access group get response. +type VirtualEnvironmentAccessGroupGetResponseData struct { + Comment string `json:"comment"` + Members []string `json:"members"` +} + // VirtualEnvironmentAccessGroupListResponseBody contains the body from an access group list response. type VirtualEnvironmentAccessGroupListResponseBody struct { Data []*VirtualEnvironmentAccessGroupListResponseData `json:"data,omitempty"` @@ -19,6 +38,38 @@ type VirtualEnvironmentAccessGroupListResponseData struct { ID string `json:"groupid"` } +// CreateAccessGroup creates an access group. +func (c *VirtualEnvironmentClient) CreateAccessGroup(id, comment string) error { + reqBody := &VirtualEnvironmentAccessGroupCreateRequestData{ + Comment: comment, + ID: id, + } + + err := c.DoRequest(hmPOST, "access/groups", reqBody, nil) + + if err != nil { + return err + } + + return nil +} + +// GetAccessGroup retrieves an access group. +func (c *VirtualEnvironmentClient) GetAccessGroup(id string) (*VirtualEnvironmentAccessGroupGetResponseData, error) { + resBody := &VirtualEnvironmentAccessGroupGetResponseBody{} + err := c.DoRequest(hmGET, fmt.Sprintf("access/groups/%s", url.PathEscape(id)), nil, resBody) + + if err != nil { + return nil, err + } + + if resBody.Data == nil { + return nil, errors.New("The server did not include a data object in the response") + } + + return resBody.Data, nil +} + // ListAccessGroups retrieves a list of access groups. func (c *VirtualEnvironmentClient) ListAccessGroups() ([]*VirtualEnvironmentAccessGroupListResponseData, error) { resBody := &VirtualEnvironmentAccessGroupListResponseBody{}