From df101366f2458f2006abf4f79c783baa30bfe4b5 Mon Sep 17 00:00:00 2001 From: Dan Petersen Date: Sun, 8 Dec 2019 17:59:05 +0100 Subject: [PATCH] Latest work --- .gitignore | 1 + CHANGELOG.md | 3 + Makefile | 16 +- README.md | 22 ++- ...source_virtual_environment_access_group.go | 2 +- ...ource_virtual_environment_access_groups.go | 10 +- ..._virtual_environment_access_groups_test.go | 2 +- ..._source_virtual_environment_access_role.go | 54 ++++++ ...ce_virtual_environment_access_role_test.go | 37 +++++ ...source_virtual_environment_access_roles.go | 77 +++++++++ ...e_virtual_environment_access_roles_test.go | 39 +++++ ...source_virtual_environment_access_group.tf | 7 +- ...ource_virtual_environment_access_groups.tf | 2 +- ..._source_virtual_environment_access_role.tf | 11 ++ ...source_virtual_environment_access_roles.tf | 13 ++ ...source_virtual_environment_access_group.tf | 6 +- ...esource_virtual_environment_access_role.tf | 14 ++ provider.go | 3 + proxmox/common_types.go | 68 +++++++- proxmox/virtual_environment.go | 16 +- proxmox/virtual_environment_access_groups.go | 7 + proxmox/virtual_environment_access_roles.go | 93 +++++++++++ proxmox/virtual_environment_authentication.go | 10 +- ...e_virtual_environment_access_group_test.go | 37 +++++ resource_virtual_environment_access_role.go | 155 ++++++++++++++++++ ...ce_virtual_environment_access_role_test.go | 35 ++++ 26 files changed, 698 insertions(+), 42 deletions(-) create mode 100644 data_source_virtual_environment_access_role.go create mode 100644 data_source_virtual_environment_access_role_test.go create mode 100644 data_source_virtual_environment_access_roles.go create mode 100644 data_source_virtual_environment_access_roles_test.go create mode 100644 example/data_source_virtual_environment_access_role.tf create mode 100644 example/data_source_virtual_environment_access_roles.tf create mode 100644 example/resource_virtual_environment_access_role.tf create mode 100644 proxmox/virtual_environment_access_roles.go create mode 100644 resource_virtual_environment_access_group_test.go create mode 100644 resource_virtual_environment_access_role.go create mode 100644 resource_virtual_environment_access_role_test.go diff --git a/.gitignore b/.gitignore index 71d01ef2..b8a4b91f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ modules-dev/ *.tfplan *.tfstate *.tfstate.lock.info +*.tfvars .*.swp .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index e61399cd..4d2f9a8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,5 +4,8 @@ FEATURES: * **New Data Source:** `proxmox_virtual_environment_access_group` * **New Data Source:** `proxmox_virtual_environment_access_groups` +* **New Data Source:** `proxmox_virtual_environment_access_role` +* **New Data Source:** `proxmox_virtual_environment_access_roles` * **New Data Source:** `proxmox_virtual_environment_version` * **New Resource:** `proxmox_virtual_environment_access_group` +* **New Resource:** `proxmox_virtual_environment_access_role` diff --git a/Makefile b/Makefile index 744ecd7e..4fcea4ac 100644 --- a/Makefile +++ b/Makefile @@ -8,14 +8,18 @@ default: build build: go build -o "bin/$(NAME)_v$(VERSION)-custom_x4" -example: +example: example-init example-apply example-apply example-destroy + +example-apply: + cd ./example && terraform apply -auto-approve + +example-destroy: + cd ./example && terraform destroy -auto-approve + +example-init: rm -f "example/$(NAME)_v"* go build -o "example/$(NAME)_v$(VERSION)-custom_x4" - cd ./example && \ - terraform init && \ - terraform apply -auto-approve && \ - terraform apply -auto-approve && \ - terraform destroy -auto-approve + cd ./example && terraform init fmt: gofmt -w $(GOFMT_FILES) diff --git a/README.md b/README.md index 49e0e12e..f5bcae32 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ If you're building the provider, follow the instructions to [install it as a plu ##### Access Group (proxmox_virtual_environment_access_group) ###### Arguments -* `id` - (Required) The group id. +* `group_id` - (Required) The group id. ###### Attributes * `comment` - The group comment. @@ -53,7 +53,25 @@ This data source doesn't accept arguments. ###### Attributes * `comments` - The group comments. -* `ids` - The group ids. +* `group_ids` - The group ids. + +##### Access Role (proxmox_virtual_environment_access_role) + +###### Arguments +* `role_id` - (Required) The role id. + +###### Attributes +* `privileges` - The role privileges. + +##### Access Roles (proxmox_virtual_environment_access_roles) + +###### Arguments +This data source doesn't accept arguments. + +###### Attributes +* `privileges` - The role privileges. +* `role_ids` - The role ids. +* `special` - Whether the role is special (built-in). ##### Version (proxmox_virtual_environment_version) diff --git a/data_source_virtual_environment_access_group.go b/data_source_virtual_environment_access_group.go index 254137a2..36be29ea 100644 --- a/data_source_virtual_environment_access_group.go +++ b/data_source_virtual_environment_access_group.go @@ -10,7 +10,7 @@ import ( const ( mkDataSourceVirtualEnvironmentAccessGroupComment = "comment" - mkDataSourceVirtualEnvironmentAccessGroupID = "id" + mkDataSourceVirtualEnvironmentAccessGroupID = "group_id" mkDataSourceVirtualEnvironmentAccessGroupMembers = "members" ) diff --git a/data_source_virtual_environment_access_groups.go b/data_source_virtual_environment_access_groups.go index 4bf95009..0595f08c 100644 --- a/data_source_virtual_environment_access_groups.go +++ b/data_source_virtual_environment_access_groups.go @@ -10,7 +10,7 @@ import ( const ( mkDataSourceVirtualEnvironmentAccessGroupsComments = "comments" - mkDataSourceVirtualEnvironmentAccessGroupsIDs = "ids" + mkDataSourceVirtualEnvironmentAccessGroupsGroupIDs = "group_ids" ) func dataSourceVirtualEnvironmentAccessGroups() *schema.Resource { @@ -22,7 +22,7 @@ func dataSourceVirtualEnvironmentAccessGroups() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - mkDataSourceVirtualEnvironmentAccessGroupsIDs: &schema.Schema{ + mkDataSourceVirtualEnvironmentAccessGroupsGroupIDs: &schema.Schema{ Type: schema.TypeList, Description: "The group ids", Computed: true, @@ -48,17 +48,17 @@ func dataSourceVirtualEnvironmentAccessGroupsRead(d *schema.ResourceData, m inte } comments := make([]interface{}, len(list)) - ids := make([]interface{}, len(list)) + groupIDs := make([]interface{}, len(list)) for i, v := range list { comments[i] = v.Comment - ids[i] = v.ID + groupIDs[i] = v.ID } d.SetId("access_groups") d.Set(mkDataSourceVirtualEnvironmentAccessGroupsComments, comments) - d.Set(mkDataSourceVirtualEnvironmentAccessGroupsIDs, ids) + d.Set(mkDataSourceVirtualEnvironmentAccessGroupsGroupIDs, groupIDs) return nil } diff --git a/data_source_virtual_environment_access_groups_test.go b/data_source_virtual_environment_access_groups_test.go index 65cb7202..7f300c20 100644 --- a/data_source_virtual_environment_access_groups_test.go +++ b/data_source_virtual_environment_access_groups_test.go @@ -23,7 +23,7 @@ func TestDataSourceVirtualEnvironmentAccessGroupsSchema(t *testing.T) { attributeKeys := []string{ mkDataSourceVirtualEnvironmentAccessGroupsComments, - mkDataSourceVirtualEnvironmentAccessGroupsIDs, + mkDataSourceVirtualEnvironmentAccessGroupsGroupIDs, } for _, v := range attributeKeys { diff --git a/data_source_virtual_environment_access_role.go b/data_source_virtual_environment_access_role.go new file mode 100644 index 00000000..8443d1fc --- /dev/null +++ b/data_source_virtual_environment_access_role.go @@ -0,0 +1,54 @@ +/* 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 ( + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + mkDataSourceVirtualEnvironmentAccessRoleID = "role_id" + mkDataSourceVirtualEnvironmentAccessRolePrivileges = "privileges" +) + +func dataSourceVirtualEnvironmentAccessRole() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkDataSourceVirtualEnvironmentAccessRoleID: &schema.Schema{ + Type: schema.TypeString, + Description: "The role id", + Required: true, + }, + mkDataSourceVirtualEnvironmentAccessRolePrivileges: &schema.Schema{ + Type: schema.TypeList, + Description: "The role privileges", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + Read: dataSourceVirtualEnvironmentAccessRoleRead, + } +} + +func dataSourceVirtualEnvironmentAccessRoleRead(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + roleID := d.Get(mkDataSourceVirtualEnvironmentAccessRoleID).(string) + accessRole, err := veClient.GetAccessRole(roleID) + + if err != nil { + return err + } + + d.SetId(roleID) + d.Set(mkDataSourceVirtualEnvironmentAccessRolePrivileges, *accessRole) + + return nil +} diff --git a/data_source_virtual_environment_access_role_test.go b/data_source_virtual_environment_access_role_test.go new file mode 100644 index 00000000..7d3900c3 --- /dev/null +++ b/data_source_virtual_environment_access_role_test.go @@ -0,0 +1,37 @@ +/* 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" +) + +// TestDataSourceVirtualEnvironmentAccessRoleInstantiation tests whether the DataSourceVirtualEnvironmentAccessRole instance can be instantiated. +func TestDataSourceVirtualEnvironmentAccessRoleInstantiation(t *testing.T) { + s := dataSourceVirtualEnvironmentAccessRole() + + if s == nil { + t.Fatalf("Cannot instantiate dataSourceVirtualEnvironmentAccessRole") + } +} + +// TestDataSourceVirtualEnvironmentAccessRoleSchema tests the dataSourceVirtualEnvironmentAccessRole schema. +func TestDataSourceVirtualEnvironmentAccessRoleSchema(t *testing.T) { + s := dataSourceVirtualEnvironmentAccessRole() + + attributeKeys := []string{ + mkDataSourceVirtualEnvironmentAccessRolePrivileges, + } + + for _, v := range attributeKeys { + if s.Schema[v] == nil { + t.Fatalf("Error in dataSourceVirtualEnvironmentAccessRole.Schema: Missing attribute \"%s\"", v) + } + + if s.Schema[v].Computed != true { + t.Fatalf("Error in dataSourceVirtualEnvironmentAccessRole.Schema: Attribute \"%s\" is not computed", v) + } + } +} diff --git a/data_source_virtual_environment_access_roles.go b/data_source_virtual_environment_access_roles.go new file mode 100644 index 00000000..734ad5cc --- /dev/null +++ b/data_source_virtual_environment_access_roles.go @@ -0,0 +1,77 @@ +/* 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 ( + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + mkDataSourceVirtualEnvironmentAccessRolesPrivileges = "privileges" + mkDataSourceVirtualEnvironmentAccessRolesRoleIDs = "role_ids" + mkDataSourceVirtualEnvironmentAccessRolesSpecial = "special" +) + +func dataSourceVirtualEnvironmentAccessRoles() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkDataSourceVirtualEnvironmentAccessRolesPrivileges: &schema.Schema{ + Type: schema.TypeList, + Description: "The role privileges", + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + mkDataSourceVirtualEnvironmentAccessRolesRoleIDs: &schema.Schema{ + Type: schema.TypeList, + Description: "The role ids", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + mkDataSourceVirtualEnvironmentAccessRolesSpecial: &schema.Schema{ + Type: schema.TypeList, + Description: "Whether the role is special (built-in)", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeBool}, + }, + }, + Read: dataSourceVirtualEnvironmentAccessRolesRead, + } +} + +func dataSourceVirtualEnvironmentAccessRolesRead(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + list, err := veClient.ListAccessRoles() + + if err != nil { + return err + } + + privileges := make([]interface{}, len(list)) + roleIDs := make([]interface{}, len(list)) + special := make([]interface{}, len(list)) + + for i, v := range list { + privileges[i] = v.Privileges + roleIDs[i] = v.ID + special[i] = v.Special + } + + d.SetId("access_roles") + + d.Set(mkDataSourceVirtualEnvironmentAccessRolesPrivileges, privileges) + d.Set(mkDataSourceVirtualEnvironmentAccessRolesRoleIDs, roleIDs) + d.Set(mkDataSourceVirtualEnvironmentAccessRolesSpecial, special) + + return nil +} diff --git a/data_source_virtual_environment_access_roles_test.go b/data_source_virtual_environment_access_roles_test.go new file mode 100644 index 00000000..b6048aeb --- /dev/null +++ b/data_source_virtual_environment_access_roles_test.go @@ -0,0 +1,39 @@ +/* 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" +) + +// TestDataSourceVirtualEnvironmentAccessRolesInstantiation tests whether the DataSourceVirtualEnvironmentAccessRoles instance can be instantiated. +func TestDataSourceVirtualEnvironmentAccessRolesInstantiation(t *testing.T) { + s := dataSourceVirtualEnvironmentAccessRoles() + + if s == nil { + t.Fatalf("Cannot instantiate dataSourceVirtualEnvironmentAccessRoles") + } +} + +// TestDataSourceVirtualEnvironmentAccessRolesSchema tests the dataSourceVirtualEnvironmentAccessRoles schema. +func TestDataSourceVirtualEnvironmentAccessRolesSchema(t *testing.T) { + s := dataSourceVirtualEnvironmentAccessRoles() + + attributeKeys := []string{ + mkDataSourceVirtualEnvironmentAccessRolesPrivileges, + mkDataSourceVirtualEnvironmentAccessRolesRoleIDs, + mkDataSourceVirtualEnvironmentAccessRolesSpecial, + } + + for _, v := range attributeKeys { + if s.Schema[v] == nil { + t.Fatalf("Error in dataSourceVirtualEnvironmentAccessRoles.Schema: Missing attribute \"%s\"", v) + } + + if s.Schema[v].Computed != true { + t.Fatalf("Error in dataSourceVirtualEnvironmentAccessRoles.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 index a01b584e..aeca91dd 100644 --- a/example/data_source_virtual_environment_access_group.tf +++ b/example/data_source_virtual_environment_access_group.tf @@ -1,12 +1,11 @@ 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) + group_id = "${proxmox_virtual_environment_access_group.example.id}" } output "data_proxmox_virtual_environment_access_group_example_comments" { - value = data.proxmox_virtual_environment_access_group.example.*.comment + 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 + 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 09e29021..610d3ff2 100644 --- a/example/data_source_virtual_environment_access_groups.tf +++ b/example/data_source_virtual_environment_access_groups.tf @@ -3,6 +3,6 @@ data "proxmox_virtual_environment_access_groups" "example" {} 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, + "group_ids", data.proxmox_virtual_environment_access_groups.example.group_ids, )}" } diff --git a/example/data_source_virtual_environment_access_role.tf b/example/data_source_virtual_environment_access_role.tf new file mode 100644 index 00000000..84e7b241 --- /dev/null +++ b/example/data_source_virtual_environment_access_role.tf @@ -0,0 +1,11 @@ +data "proxmox_virtual_environment_access_role" "example" { + role_id = "${proxmox_virtual_environment_access_role.example.id}" +} + +output "data_proxmox_virtual_environment_access_role_example_privileges" { + value = "${data.proxmox_virtual_environment_access_role.example.privileges}" +} + +output "data_proxmox_virtual_environment_access_role_example_role_id" { + value = "${data.proxmox_virtual_environment_access_role.example.id}" +} diff --git a/example/data_source_virtual_environment_access_roles.tf b/example/data_source_virtual_environment_access_roles.tf new file mode 100644 index 00000000..82db47f5 --- /dev/null +++ b/example/data_source_virtual_environment_access_roles.tf @@ -0,0 +1,13 @@ +data "proxmox_virtual_environment_access_roles" "example" {} + +output "data_proxmox_virtual_environment_access_roles_example_privileges" { + value = "${data.proxmox_virtual_environment_access_roles.example.privileges}" +} + +output "data_proxmox_virtual_environment_access_roles_example_role_ids" { + value = "${data.proxmox_virtual_environment_access_roles.example.role_ids}" +} + +output "data_proxmox_virtual_environment_access_roles_example_special" { + value = "${data.proxmox_virtual_environment_access_roles.example.special}" +} diff --git a/example/resource_virtual_environment_access_group.tf b/example/resource_virtual_environment_access_group.tf index 3beac382..02beed3c 100644 --- a/example/resource_virtual_environment_access_group.tf +++ b/example/resource_virtual_environment_access_group.tf @@ -4,13 +4,13 @@ resource "proxmox_virtual_environment_access_group" "example" { } output "resource_proxmox_virtual_environment_access_group_example_comment" { - value = proxmox_virtual_environment_access_group.example.comment + value = "${proxmox_virtual_environment_access_group.example.comment}" } output "resource_proxmox_virtual_environment_access_group_example_id" { - value = proxmox_virtual_environment_access_group.example.id + value = "${proxmox_virtual_environment_access_group.example.id}" } output "resource_proxmox_virtual_environment_access_group_example_members" { - value = proxmox_virtual_environment_access_group.example.members + value = "${proxmox_virtual_environment_access_group.example.members}" } diff --git a/example/resource_virtual_environment_access_role.tf b/example/resource_virtual_environment_access_role.tf new file mode 100644 index 00000000..9fd7200a --- /dev/null +++ b/example/resource_virtual_environment_access_role.tf @@ -0,0 +1,14 @@ +resource "proxmox_virtual_environment_access_role" "example" { + privileges = [ + "VM.Monitor", + ] + role_id = "terraform-provider-proxmox-example" +} + +output "resource_proxmox_virtual_environment_access_role_example_privileges" { + value = "${proxmox_virtual_environment_access_role.example.privileges}" +} + +output "resource_proxmox_virtual_environment_access_role_example_role_id" { + value = "${proxmox_virtual_environment_access_role.example.role_id}" +} diff --git a/provider.go b/provider.go index 637ce79f..05c0a75c 100644 --- a/provider.go +++ b/provider.go @@ -30,10 +30,13 @@ func Provider() *schema.Provider { DataSourcesMap: map[string]*schema.Resource{ "proxmox_virtual_environment_access_group": dataSourceVirtualEnvironmentAccessGroup(), "proxmox_virtual_environment_access_groups": dataSourceVirtualEnvironmentAccessGroups(), + "proxmox_virtual_environment_access_role": dataSourceVirtualEnvironmentAccessRole(), + "proxmox_virtual_environment_access_roles": dataSourceVirtualEnvironmentAccessRoles(), "proxmox_virtual_environment_version": dataSourceVirtualEnvironmentVersion(), }, ResourcesMap: map[string]*schema.Resource{ "proxmox_virtual_environment_access_group": resourceVirtualEnvironmentAccessGroup(), + "proxmox_virtual_environment_access_role": resourceVirtualEnvironmentAccessRole(), }, Schema: map[string]*schema.Schema{ mkProviderVirtualEnvironment: &schema.Schema{ diff --git a/proxmox/common_types.go b/proxmox/common_types.go index 827d3da2..c809acb9 100644 --- a/proxmox/common_types.go +++ b/proxmox/common_types.go @@ -4,18 +4,17 @@ package proxmox -import "bytes" +import ( + "bytes" + "encoding/json" + "strings" +) -// CustomBool allows a JSON boolean value to also be an integer +// CustomBool allows a JSON boolean value to also be an integer. type CustomBool bool -// UnmarshalJSON converts a JSON value to a boolean. -func (r *CustomBool) UnmarshalJSON(b []byte) error { - s := string(b) - *r = CustomBool(s == "1" || s == "true") - - return nil -} +// CustomPrivileges allows a JSON object of privileges to also be a string array. +type CustomPrivileges []string // MarshalJSON converts a boolean to a JSON value. func (r CustomBool) MarshalJSON() ([]byte, error) { @@ -29,3 +28,54 @@ func (r CustomBool) MarshalJSON() ([]byte, error) { return buffer.Bytes(), nil } + +// UnmarshalJSON converts a JSON value to a boolean. +func (r *CustomBool) UnmarshalJSON(b []byte) error { + s := string(b) + *r = CustomBool(s == "1" || s == "true") + + return nil +} + +// MarshalJSON converts a boolean to a JSON value. +func (r *CustomPrivileges) MarshalJSON() ([]byte, error) { + var 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 privileges.(type) { + case string: + s := privileges.(string) + + if s != "" { + *r = strings.Split(s, ",") + } else { + *r = make(CustomPrivileges, 0) + } + default: + *r = make(CustomPrivileges, 0) + + for k, v := range privileges.(map[string]interface{}) { + if v.(float64) >= 1 { + *r = append(*r, k) + } + } + } + + return nil +} diff --git a/proxmox/virtual_environment.go b/proxmox/virtual_environment.go index a4ca59b9..685dee67 100644 --- a/proxmox/virtual_environment.go +++ b/proxmox/virtual_environment.go @@ -10,6 +10,7 @@ import ( "encoding/json" "errors" "fmt" + "log" "net/http" "net/url" "strings" @@ -76,6 +77,8 @@ 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 { + log.Printf("[DEBUG] Performing HTTP %s request (path: %s)", method, path) + urlEncodedRequestBody := new(bytes.Buffer) if requestBody != nil { @@ -86,16 +89,19 @@ func (c *VirtualEnvironmentClient) DoRequest(method, path string, requestBody in v, err := query.Values(requestBody) if err != nil { - return fmt.Errorf("Failed to encode HTTP %s request (path: %s)", method, path) + return fmt.Errorf("Failed to encode HTTP %s request (path: %s) - Reason: %s", method, path, err.Error()) } - urlEncodedRequestBody = bytes.NewBufferString(v.Encode()) + encodedValues := v.Encode() + urlEncodedRequestBody = bytes.NewBufferString(encodedValues) + + log.Printf("[DEBUG] Values: %s", encodedValues) } 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) + return fmt.Errorf("Failed to create HTTP %s request (path: %s) - Reason: %s", method, path, err.Error()) } req.Header.Add("Accept", "application/json") @@ -113,7 +119,7 @@ func (c *VirtualEnvironmentClient) DoRequest(method, path string, requestBody in res, err := c.httpClient.Do(req) if err != nil { - return fmt.Errorf("Failed to perform HTTP %s request (path: %s)", method, path) + return fmt.Errorf("Failed to perform HTTP %s request (path: %s) - Reason: %s", method, path, err.Error()) } err = c.ValidateResponseCode(res) @@ -126,7 +132,7 @@ func (c *VirtualEnvironmentClient) DoRequest(method, path string, requestBody in err = json.NewDecoder(res.Body).Decode(responseBody) if err != nil { - return fmt.Errorf("Failed to decode HTTP %s response (path: %s)", method, path) + return fmt.Errorf("Failed to decode HTTP %s response (path: %s) - Reason: %s", method, path, err.Error()) } } diff --git a/proxmox/virtual_environment_access_groups.go b/proxmox/virtual_environment_access_groups.go index 260afb93..8a561109 100644 --- a/proxmox/virtual_environment_access_groups.go +++ b/proxmox/virtual_environment_access_groups.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "net/url" + "sort" ) // VirtualEnvironmentAccessGroupCreateRequestBody contains the data for an access group create request. @@ -66,6 +67,8 @@ func (c *VirtualEnvironmentClient) GetAccessGroup(id string) (*VirtualEnvironmen return nil, errors.New("The server did not include a data object in the response") } + sort.Strings(resBody.Data.Members) + return resBody.Data, nil } @@ -82,6 +85,10 @@ func (c *VirtualEnvironmentClient) ListAccessGroups() ([]*VirtualEnvironmentAcce return nil, errors.New("The server did not include a data object in the response") } + sort.Slice(resBody.Data, func(i, j int) bool { + return resBody.Data[i].ID < resBody.Data[j].ID + }) + return resBody.Data, nil } diff --git a/proxmox/virtual_environment_access_roles.go b/proxmox/virtual_environment_access_roles.go new file mode 100644 index 00000000..5cd2951d --- /dev/null +++ b/proxmox/virtual_environment_access_roles.go @@ -0,0 +1,93 @@ +/* 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 ( + "errors" + "fmt" + "net/url" + "sort" +) + +// VirtualEnvironmentAccessRoleCreateRequestBody contains the data for an access group create request. +type VirtualEnvironmentAccessRoleCreateRequestBody struct { + ID string `json:"roleid" url:"roleid"` + Privileges CustomPrivileges `json:"privs" url:"privs,comma"` +} + +// VirtualEnvironmentAccessRoleGetResponseBody contains the body from an access group get response. +type VirtualEnvironmentAccessRoleGetResponseBody struct { + Data *CustomPrivileges `json:"data,omitempty"` +} + +// VirtualEnvironmentAccessRoleListResponseBody contains the body from an access group list response. +type VirtualEnvironmentAccessRoleListResponseBody struct { + Data []*VirtualEnvironmentAccessRoleListResponseData `json:"data,omitempty"` +} + +// VirtualEnvironmentAccessRoleListResponseData contains the data from an access group list response. +type VirtualEnvironmentAccessRoleListResponseData struct { + ID string `json:"roleid"` + Privileges *CustomPrivileges `json:"privs"` + Special CustomBool `json:"special"` +} + +// VirtualEnvironmentAccessRoleUpdateRequestBody contains the data for an access group update request. +type VirtualEnvironmentAccessRoleUpdateRequestBody struct { + Privileges CustomPrivileges `json:"privs" url:"privs,comma"` +} + +// CreateAccessRole creates an access role. +func (c *VirtualEnvironmentClient) CreateAccessRole(d *VirtualEnvironmentAccessRoleCreateRequestBody) error { + return c.DoRequest(hmPOST, "access/roles", d, nil) +} + +// DeleteAccessRole deletes an access role. +func (c *VirtualEnvironmentClient) DeleteAccessRole(id string) error { + return c.DoRequest(hmDELETE, fmt.Sprintf("access/roles/%s", id), nil, nil) +} + +// GetAccessRole retrieves an access role. +func (c *VirtualEnvironmentClient) GetAccessRole(id string) (*CustomPrivileges, error) { + resBody := &VirtualEnvironmentAccessRoleGetResponseBody{} + err := c.DoRequest(hmGET, fmt.Sprintf("access/roles/%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") + } + + sort.Strings(*resBody.Data) + + return resBody.Data, nil +} + +// ListAccessRoles retrieves a list of access roles. +func (c *VirtualEnvironmentClient) ListAccessRoles() ([]*VirtualEnvironmentAccessRoleListResponseData, error) { + resBody := &VirtualEnvironmentAccessRoleListResponseBody{} + err := c.DoRequest(hmGET, "access/roles", 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") + } + + sort.Slice(resBody.Data, func(i, j int) bool { + return resBody.Data[i].ID < resBody.Data[j].ID + }) + + return resBody.Data, nil +} + +// UpdateAccessRole updates an access role. +func (c *VirtualEnvironmentClient) UpdateAccessRole(id string, d *VirtualEnvironmentAccessRoleUpdateRequestBody) error { + return c.DoRequest(hmPUT, fmt.Sprintf("access/roles/%s", id), d, nil) +} diff --git a/proxmox/virtual_environment_authentication.go b/proxmox/virtual_environment_authentication.go index 8aeaae56..f4719dcd 100644 --- a/proxmox/virtual_environment_authentication.go +++ b/proxmox/virtual_environment_authentication.go @@ -20,11 +20,11 @@ type VirtualEnvironmentAuthenticationResponseBody struct { // VirtualEnvironmentAuthenticationResponseCapabilities contains the supported capabilities for a session. type VirtualEnvironmentAuthenticationResponseCapabilities struct { - Access *map[string]CustomBool `json:"access,omitempty"` - Datacenter *map[string]CustomBool `json:"dc,omitempty"` - Nodes *map[string]CustomBool `json:"nodes,omitempty"` - Storage *map[string]CustomBool `json:"storage,omitempty"` - VirtualMachines *map[string]CustomBool `json:"vms,omitempty"` + Access *CustomPrivileges `json:"access,omitempty"` + Datacenter *CustomPrivileges `json:"dc,omitempty"` + Nodes *CustomPrivileges `json:"nodes,omitempty"` + Storage *CustomPrivileges `json:"storage,omitempty"` + VirtualMachines *CustomPrivileges `json:"vms,omitempty"` } // VirtualEnvironmentAuthenticationResponseData contains the data from an authentication response. diff --git a/resource_virtual_environment_access_group_test.go b/resource_virtual_environment_access_group_test.go new file mode 100644 index 00000000..04551ff9 --- /dev/null +++ b/resource_virtual_environment_access_group_test.go @@ -0,0 +1,37 @@ +/* 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" +) + +// TestResourceVirtualEnvironmentAccessGroupInstantiation tests whether the ResourceVirtualEnvironmentAccessGroup instance can be instantiated. +func TestResourceVirtualEnvironmentAccessGroupInstantiation(t *testing.T) { + s := resourceVirtualEnvironmentAccessGroup() + + if s == nil { + t.Fatalf("Cannot instantiate resourceVirtualEnvironmentAccessGroup") + } +} + +// TestResourceVirtualEnvironmentAccessGroupSchema tests the resourceVirtualEnvironmentAccessGroup schema. +func TestResourceVirtualEnvironmentAccessGroupSchema(t *testing.T) { + s := resourceVirtualEnvironmentAccessGroup() + + attributeKeys := []string{ + mkResourceVirtualEnvironmentAccessGroupMembers, + } + + for _, v := range attributeKeys { + if s.Schema[v] == nil { + t.Fatalf("Error in resourceVirtualEnvironmentAccessGroup.Schema: Missing attribute \"%s\"", v) + } + + if s.Schema[v].Computed != true { + t.Fatalf("Error in resourceVirtualEnvironmentAccessGroup.Schema: Attribute \"%s\" is not computed", v) + } + } +} diff --git a/resource_virtual_environment_access_role.go b/resource_virtual_environment_access_role.go new file mode 100644 index 00000000..1290b4f5 --- /dev/null +++ b/resource_virtual_environment_access_role.go @@ -0,0 +1,155 @@ +/* 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 ( + "strings" + + "github.com/danitso/terraform-provider-proxmox/proxmox" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + mkResourceVirtualEnvironmentAccessRolePrivileges = "privileges" + mkResourceVirtualEnvironmentAccessRoleRoleID = "role_id" +) + +func resourceVirtualEnvironmentAccessRole() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkResourceVirtualEnvironmentAccessRolePrivileges: &schema.Schema{ + Type: schema.TypeList, + Description: "The role privileges", + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + mkResourceVirtualEnvironmentAccessRoleRoleID: &schema.Schema{ + Type: schema.TypeString, + Description: "The role id", + Required: true, + }, + }, + Create: resourceVirtualEnvironmentAccessRoleCreate, + Read: resourceVirtualEnvironmentAccessRoleRead, + Update: resourceVirtualEnvironmentAccessRoleUpdate, + Delete: resourceVirtualEnvironmentAccessRoleDelete, + } +} + +func resourceVirtualEnvironmentAccessRoleCreate(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + privileges := d.Get(mkResourceVirtualEnvironmentAccessRolePrivileges).([]interface{}) + roleID := d.Get(mkResourceVirtualEnvironmentAccessRoleRoleID).(string) + + customPrivileges := make(proxmox.CustomPrivileges, len(privileges)) + + for i, v := range privileges { + customPrivileges[i] = v.(string) + } + + body := &proxmox.VirtualEnvironmentAccessRoleCreateRequestBody{ + ID: roleID, + Privileges: customPrivileges, + } + + err = veClient.CreateAccessRole(body) + + if err != nil { + return err + } + + d.SetId(roleID) + + return resourceVirtualEnvironmentAccessRoleRead(d, m) +} + +func resourceVirtualEnvironmentAccessRoleRead(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + roleID := d.Id() + accessRole, err := veClient.GetAccessRole(roleID) + + if err != nil { + if strings.Contains(err.Error(), "HTTP 404") { + d.SetId("") + + return nil + } + + return err + } + + d.SetId(roleID) + + d.Set(mkResourceVirtualEnvironmentAccessRolePrivileges, *accessRole) + + return nil +} + +func resourceVirtualEnvironmentAccessRoleUpdate(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + privileges := d.Get(mkResourceVirtualEnvironmentAccessRolePrivileges).([]interface{}) + customPrivileges := make(proxmox.CustomPrivileges, len(privileges)) + + for i, v := range privileges { + customPrivileges[i] = v.(string) + } + + body := &proxmox.VirtualEnvironmentAccessRoleUpdateRequestBody{ + Privileges: customPrivileges, + } + + roleID := d.Id() + err = veClient.UpdateAccessRole(roleID, body) + + if err != nil { + return err + } + + return resourceVirtualEnvironmentAccessRoleRead(d, m) +} + +func resourceVirtualEnvironmentAccessRoleDelete(d *schema.ResourceData, m interface{}) error { + config := m.(providerConfiguration) + veClient, err := config.GetVEClient() + + if err != nil { + return err + } + + roleID := d.Id() + err = veClient.DeleteAccessRole(roleID) + + if err != nil { + if strings.Contains(err.Error(), "HTTP 404") { + d.SetId("") + + return nil + } + + return err + } + + d.SetId("") + + return nil +} diff --git a/resource_virtual_environment_access_role_test.go b/resource_virtual_environment_access_role_test.go new file mode 100644 index 00000000..1d7b29c0 --- /dev/null +++ b/resource_virtual_environment_access_role_test.go @@ -0,0 +1,35 @@ +/* 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" +) + +// TestResourceVirtualEnvironmentAccessRoleInstantiation tests whether the ResourceVirtualEnvironmentAccessRole instance can be instantiated. +func TestResourceVirtualEnvironmentAccessRoleInstantiation(t *testing.T) { + s := resourceVirtualEnvironmentAccessRole() + + if s == nil { + t.Fatalf("Cannot instantiate resourceVirtualEnvironmentAccessRole") + } +} + +// TestResourceVirtualEnvironmentAccessRoleSchema tests the resourceVirtualEnvironmentAccessRole schema. +func TestResourceVirtualEnvironmentAccessRoleSchema(t *testing.T) { + s := resourceVirtualEnvironmentAccessRole() + + attributeKeys := []string{} + + for _, v := range attributeKeys { + if s.Schema[v] == nil { + t.Fatalf("Error in resourceVirtualEnvironmentAccessRole.Schema: Missing attribute \"%s\"", v) + } + + if s.Schema[v].Computed != true { + t.Fatalf("Error in resourceVirtualEnvironmentAccessRole.Schema: Attribute \"%s\" is not computed", v) + } + } +}