From fef6040cc667fcce8d559594fc9db8b064dc0b66 Mon Sep 17 00:00:00 2001 From: kjubybot Date: Thu, 3 Feb 2022 15:20:12 +0300 Subject: [PATCH] k8s, k8s_wg resources --- decort/controller.go | 4 +- decort/models_api.go | 42 ++++++-- decort/node_subresource.go | 5 + decort/provider.go | 1 + decort/resource_k8s.go | 22 +++- decort/resource_k8s_wg.go | 207 +++++++++++++++++++++++++++++++++++++ decort/resource_pfw.go | 6 +- decort/utility_k8s.go | 24 +++++ decort/utility_k8s_wg.go | 66 ++++++++++++ decort/utility_pfw.go | 24 +++++ docs/data-sources/kvmvm.md | 2 +- docs/resources/k8s.md | 4 + docs/resources/k8s_wg.md | 30 ++++++ go.mod | 1 + 14 files changed, 417 insertions(+), 21 deletions(-) create mode 100644 decort/resource_k8s_wg.go create mode 100644 decort/utility_k8s_wg.go create mode 100644 docs/resources/k8s_wg.md diff --git a/decort/controller.go b/decort/controller.go index 6950c51..5861266 100644 --- a/decort/controller.go +++ b/decort/controller.go @@ -393,8 +393,8 @@ func (config *ControllerCfg) decortAPICall(method string, api_name string, url_v if resp.StatusCode == http.StatusOK { return string(body), nil } else { - return "", fmt.Errorf("decortAPICall: unexpected status code %d when calling API %q with request Body %q", - resp.StatusCode, req.URL, params_str) + return "", fmt.Errorf("decortAPICall: unexpected status code %d when calling API %q with request Body %q. Respone:\n%s", + resp.StatusCode, req.URL, params_str, body) } /* diff --git a/decort/models_api.go b/decort/models_api.go index 9b5eba5..c49e7da 100644 --- a/decort/models_api.go +++ b/decort/models_api.go @@ -25,6 +25,8 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p package decort import ( + "bytes" + "strconv" "time" ) @@ -606,17 +608,39 @@ const K8sGetAPI = "/restmachine/cloudapi/k8s/get" const K8sUpdateAPI = "/restmachine/cloudapi/k8s/update" const K8sDeleteAPI = "/restmachine/cloudapi/k8s/delete" +const K8sWgCreateAPI = "/restmachine/cloudapi/k8s/workersGroupAdd" +const K8sWgDeleteAPI = "/restmachine/cloudapi/k8s/workersGroupDelete" + +//Blasphemous workaround for parsing Result value +type TaskResult int + +func (r *TaskResult) UnmarshalJSON(b []byte) error { + b = bytes.Trim(b, `"`) + if len(b) == 0 { + *r = 0 + return nil + } + + n, err := strconv.Atoi(string(b)) + if err != nil { + return err + } + + *r = TaskResult(n) + return nil +} + //AsyncTask represents a long task completion status type AsyncTask struct { - AuditID string `json:"auditId"` - Completed bool `json:"completed"` - Error string `json:"error"` - Log []string `json:"log"` - Result string `json:"result"` - Stage string `json:"stage"` - Status string `json:"status"` - UpdateTime uint64 `json:"updateTime"` - UpdatedTime uint64 `json:"updatedTime"` + AuditID string `json:"auditId"` + Completed bool `json:"completed"` + Error string `json:"error"` + Log []string `json:"log"` + Result TaskResult `json:"result"` + Stage string `json:"stage"` + Status string `json:"status"` + UpdateTime uint64 `json:"updateTime"` + UpdatedTime uint64 `json:"updatedTime"` } const AsyncTaskGetAPI = "/restmachine/cloudapi/tasks/get" diff --git a/decort/node_subresource.go b/decort/node_subresource.go index dcb0b71..26d8992 100644 --- a/decort/node_subresource.go +++ b/decort/node_subresource.go @@ -58,6 +58,7 @@ func parseNode(nodeList []interface{}) K8sNodeRecord { func nodeToResource(node K8sNodeRecord) []interface{} { mp := make(map[string]interface{}) + mp["id"] = node.ID mp["num"] = node.Num mp["cpu"] = node.Cpu mp["ram"] = node.Ram @@ -71,24 +72,28 @@ func nodeK8sSubresourceSchemaMake() map[string]*schema.Schema { "num": { Type: schema.TypeInt, Required: true, + ForceNew: true, Description: "Number of nodes to create.", }, "cpu": { Type: schema.TypeInt, Required: true, + ForceNew: true, Description: "Node CPU count.", }, "ram": { Type: schema.TypeInt, Required: true, + ForceNew: true, Description: "Node RAM in MB.", }, "disk": { Type: schema.TypeInt, Required: true, + ForceNew: true, Description: "Node boot disk size in GB.", }, } diff --git a/decort/provider.go b/decort/provider.go index 62620bd..322c9fd 100644 --- a/decort/provider.go +++ b/decort/provider.go @@ -105,6 +105,7 @@ func Provider() *schema.Provider { "decort_vins": resourceVins(), "decort_pfw": resourcePfw(), "decort_k8s": resourceK8s(), + "decort_k8s_wg": resourceK8sWg(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/decort/resource_k8s.go b/decort/resource_k8s.go index ccdd625..e20d021 100644 --- a/decort/resource_k8s.go +++ b/decort/resource_k8s.go @@ -107,13 +107,20 @@ func resourceK8sCreate(d *schema.ResourceData, m interface{}) error { return fmt.Errorf("cannot create k8s instance: %v", task.Error) } - d.SetId(task.Result) + d.SetId(strconv.Itoa(int(task.Result))) break } time.Sleep(time.Second * 10) } + k8s, err := utilityK8sCheckPresence(d, m) + if err != nil { + return err + } + + d.Set("default_wg_id", k8s.Groups.Workers[0].ID) + return nil } @@ -122,10 +129,8 @@ func resourceK8sRead(d *schema.ResourceData, m interface{}) error { k8s, err := utilityK8sCheckPresence(d, m) if k8s == nil { - if err != nil { - return err - } - return nil + d.SetId("") + return err } d.Set("name", k8s.Name) @@ -133,6 +138,7 @@ func resourceK8sRead(d *schema.ResourceData, m interface{}) error { d.Set("k8sci_id", k8s.CI) d.Set("masters", nodeToResource(k8s.Groups.Masters)) d.Set("workers", nodeToResource(k8s.Groups.Workers[0])) + d.Set("default_wg_id", k8s.Groups.Workers[0].ID) return nil } @@ -253,6 +259,12 @@ func resourceK8sSchemaMake() map[string]*schema.Schema { //Optional: true, //Description: "Text description of this instance.", //}, + + "default_wg_id": { + Type: schema.TypeInt, + Computed: true, + Description: "ID of default workers group for this instace.", + }, } } diff --git a/decort/resource_k8s_wg.go b/decort/resource_k8s_wg.go new file mode 100644 index 0000000..a099f6a --- /dev/null +++ b/decort/resource_k8s_wg.go @@ -0,0 +1,207 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Petr Krutov, + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + +package decort + +import ( + "net/url" + "strconv" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + log "github.com/sirupsen/logrus" +) + +func resourceK8sWgCreate(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceK8sWgCreate: called with k8s id %d", d.Get("k8s_id").(int)) + + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int))) + urlValues.Add("name", uuid.New().String()) + urlValues.Add("workerNum", strconv.Itoa(d.Get("num").(int))) + urlValues.Add("workerCpu", strconv.Itoa(d.Get("cpu").(int))) + urlValues.Add("workerRam", strconv.Itoa(d.Get("ram").(int))) + urlValues.Add("workerDisk", strconv.Itoa(d.Get("disk").(int))) + + resp, err := controller.decortAPICall("POST", K8sWgCreateAPI, urlValues) + if err != nil { + return err + } + + d.SetId(resp) + //urlValues = &url.Values{} + //urlValues.Add("auditId", strings.Trim(resp, `"`)) + + //for { + //resp, err := controller.decortAPICall("POST", AsyncTaskGetAPI, urlValues) + //if err != nil { + //return err + //} + + //task := AsyncTask{} + //if err := json.Unmarshal([]byte(resp), &task); err != nil { + //return err + //} + //log.Debugf("resourceK8sCreate: workers group creating - %s", task.Stage) + + //if task.Completed { + //if task.Error != "" { + //return fmt.Errorf("cannot create workers group: %v", task.Error) + //} + + //d.SetId(strconv.Itoa(int(task.Result))) + //break + //} + + //time.Sleep(time.Second * 5) + //} + + return nil +} + +func resourceK8sWgRead(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceK8sWgRead: called with k8s id %d", d.Get("k8s_id").(int)) + + wg, err := utilityK8sWgCheckPresence(d, m) + if wg == nil { + d.SetId("") + return err + } + + d.Set("num", wg.Num) + d.Set("cpu", wg.Cpu) + d.Set("ram", wg.Ram) + d.Set("disk", wg.Disk) + + return nil +} + +func resourceK8sWgDelete(d *schema.ResourceData, m interface{}) error { + log.Debugf("resourceK8sWgDelete: called with k8s id %d", d.Get("k8s_id").(int)) + + wg, err := utilityK8sWgCheckPresence(d, m) + if wg == nil { + if err != nil { + return err + } + return nil + } + + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int))) + urlValues.Add("workersGroupId", strconv.Itoa(wg.ID)) + + _, err = controller.decortAPICall("POST", K8sWgDeleteAPI, urlValues) + if err != nil { + return err + } + + return nil +} + +func resourceK8sWgExists(d *schema.ResourceData, m interface{}) (bool, error) { + log.Debugf("resourceK8sWgExists: called with k8s id %d", d.Get("k8s_id").(int)) + + wg, err := utilityK8sWgCheckPresence(d, m) + if wg == nil { + if err != nil { + return false, err + } + return false, nil + } + + return true, nil +} + +func resourceK8sWgSchemaMake() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "k8s_id": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + Description: "ID of k8s instance.", + }, + + //Unused but required by creation API. Sending generated UUID each time + //"name": { + //Type: schema.TypeString, + //Required: true, + //ForceNew: true, + //Description: "Name of the worker group.", + //}, + + "num": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: 1, + Description: "Number of worker nodes to create.", + }, + + "cpu": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: 1, + Description: "Worker node CPU count.", + }, + + "ram": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: 1024, + Description: "Worker node RAM in MB.", + }, + + "disk": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Default: 0, + Description: "Worker node boot disk size. If unspecified or 0, size is defined by OS image size.", + }, + } +} + +func resourceK8sWg() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + + Create: resourceK8sWgCreate, + Read: resourceK8sWgRead, + Delete: resourceK8sWgDelete, + Exists: resourceK8sWgExists, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + //TODO timeouts + + Schema: resourceK8sWgSchemaMake(), + } +} diff --git a/decort/resource_pfw.go b/decort/resource_pfw.go index fce1ff5..35976fb 100644 --- a/decort/resource_pfw.go +++ b/decort/resource_pfw.go @@ -72,10 +72,8 @@ func resourcePfwRead(d *schema.ResourceData, m interface{}) error { pfw, err := utilityPfwCheckPresence(d, m) if pfw == nil { - if err != nil { - return err - } - return nil + d.SetId("") + return err } d.Set("compute_id", pfw.ComputeID) diff --git a/decort/utility_k8s.go b/decort/utility_k8s.go index 58200ac..2a3fcdb 100644 --- a/decort/utility_k8s.go +++ b/decort/utility_k8s.go @@ -1,3 +1,27 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Petr Krutov, + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + package decort import ( diff --git a/decort/utility_k8s_wg.go b/decort/utility_k8s_wg.go new file mode 100644 index 0000000..f4ed797 --- /dev/null +++ b/decort/utility_k8s_wg.go @@ -0,0 +1,66 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Petr Krutov, + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + +package decort + +import ( + "encoding/json" + "net/url" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func utilityK8sWgCheckPresence(d *schema.ResourceData, m interface{}) (*K8sNodeRecord, error) { + controller := m.(*ControllerCfg) + urlValues := &url.Values{} + urlValues.Add("k8sId", strconv.Itoa(d.Get("k8s_id").(int))) + + resp, err := controller.decortAPICall("POST", K8sGetAPI, urlValues) + if err != nil { + return nil, err + } + + if resp == "" { + return nil, nil + } + + var k8s K8sRecord + if err := json.Unmarshal([]byte(resp), &k8s); err != nil { + return nil, err + } + + id, err := strconv.Atoi(d.Id()) + if err != nil { + return nil, err + } + + for _, wg := range k8s.Groups.Workers { + if wg.ID == id { + return &wg, nil + } + } + + return nil, nil +} diff --git a/decort/utility_pfw.go b/decort/utility_pfw.go index 65f4e11..7b0fcd5 100644 --- a/decort/utility_pfw.go +++ b/decort/utility_pfw.go @@ -1,3 +1,27 @@ +/* +Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved. +Author: Petr Krutov, + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +Technology platfom. + +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +*/ + package decort import ( diff --git a/docs/data-sources/kvmvm.md b/docs/data-sources/kvmvm.md index af07a8e..4cbb1d5 100644 --- a/docs/data-sources/kvmvm.md +++ b/docs/data-sources/kvmvm.md @@ -29,12 +29,12 @@ description: |- - **account_id** (Number) ID of the account this compute instance belongs to. - **account_name** (String) Name of the account this compute instance belongs to. -- **arch** (String) Hardware architecture of this compute instance. - **boot_disk_id** (Number) This compute instance boot disk ID. - **boot_disk_size** (Number) This compute instance boot disk size in GB. - **cloud_init** (String) Placeholder for cloud_init parameters. - **cpu** (Number) Number of CPUs allocated for this compute instance. - **description** (String) User-defined text description of this compute instance. +- **driver** (String) Hardware architecture of this compute instance. - **extra_disks** (Set of Number) IDs of the extra disk(s) attached to this compute. - **image_id** (Number) ID of the OS image this compute instance is based on. - **image_name** (String) Name of the OS image this compute instance is based on. diff --git a/docs/resources/k8s.md b/docs/resources/k8s.md index 5f97751..5c235f7 100644 --- a/docs/resources/k8s.md +++ b/docs/resources/k8s.md @@ -27,6 +27,10 @@ description: |- - **masters** (Block List, Max: 1) Master node(s) configuration. (see [below for nested schema](#nestedblock--masters)) - **workers** (Block List, Max: 1) Worker node(s) configuration. (see [below for nested schema](#nestedblock--workers)) +### Read-Only + +- **default_wg_id** (Number) ID of default workers group for this instace. + ### Nested Schema for `masters` diff --git a/docs/resources/k8s_wg.md b/docs/resources/k8s_wg.md new file mode 100644 index 0000000..51363a8 --- /dev/null +++ b/docs/resources/k8s_wg.md @@ -0,0 +1,30 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "decort_k8s_wg Resource - terraform-provider-decort" +subcategory: "" +description: |- + +--- + +# decort_k8s_wg (Resource) + + + + + + +## Schema + +### Required + +- **k8s_id** (Number) ID of k8s instance. + +### Optional + +- **cpu** (Number) Worker node CPU count. +- **disk** (Number) Worker node boot disk size. If unspecified or 0, size is defined by OS image size. +- **id** (String) The ID of this resource. +- **num** (Number) Number of worker nodes to create. +- **ram** (Number) Worker node RAM in MB. + + diff --git a/go.mod b/go.mod index 7b9ce6d..047b709 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/google/uuid v1.1.2 github.com/hashicorp/terraform-plugin-docs v0.5.1 github.com/hashicorp/terraform-plugin-sdk v1.16.0 github.com/sirupsen/logrus v1.7.0