This commit is contained in:
2026-06-19 17:45:18 +03:00
parent c00c608ce9
commit 89c77ddcbe
1324 changed files with 199523 additions and 1 deletions

View File

@@ -0,0 +1,180 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
Tim Tkachev, <tvtkachev@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceK8CIListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
list, err := utilityK8CIListCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
d.SetId(uuid.New().String())
flattenK8CIList(d, list)
d.Set("entry_count", list.EntryCount)
return nil
}
func dataSourceK8CIListSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"by_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Filter by ID",
},
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by name",
},
"status": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by status",
},
"worker_driver": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by worker driver",
},
"master_driver": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by master driver",
},
"network_plugin": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by network plugin",
},
"include_disabled": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Include deleted k8cis in result",
},
"sort_by": {
Type: schema.TypeString,
Optional: true,
Description: "sort by one of supported fields, format +|-(field)",
},
"page": {
Type: schema.TypeInt,
Optional: true,
Description: "Page number",
},
"size": {
Type: schema.TypeInt,
Optional: true,
Description: "Page size",
},
"items": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"k8ci_id": {
Type: schema.TypeInt,
Computed: true,
Description: "K8CI ID",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "K8CI name",
},
"lb_image_id": {
Type: schema.TypeInt,
Computed: true,
Description: "LB Image ID",
},
"network_plugins": {
Type: schema.TypeList,
Computed: true,
Description: "List of available network plugins",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "K8CI Status",
},
"desc": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"version": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"entry_count": {
Type: schema.TypeInt,
Computed: true,
},
}
}
func DataSourceK8CIList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceK8CIListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceK8CIListSchemaMake(),
}
}

View File

@@ -0,0 +1,465 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
Tim Tkachev, <tvtkachev@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/lb"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
)
func dataSourceK8sRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
cluster, err := utilityDataK8sCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
d.SetId(strconv.FormatUint(cluster.ID, 10))
k8sList, err := utilityK8sListCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
curK8s := k8s.ItemK8SCluster{}
for _, k8sCluster := range k8sList.Data {
if k8sCluster.ID == cluster.ID {
curK8s = k8sCluster
}
}
if curK8s.ID == 0 {
return diag.Errorf("Cluster with id %d not found in List clusters", cluster.ID)
}
d.Set("vins_id", curK8s.VINSID)
masterComputeList := make([]compute.RecordCompute, 0, len(cluster.K8SGroups.Masters.DetailedInfo))
workersComputeList := make([]compute.RecordCompute, 0, len(cluster.K8SGroups.Workers))
for _, masterNode := range cluster.K8SGroups.Masters.DetailedInfo {
compute, err := utilityComputeCheckPresence(ctx, d, m, masterNode.ID)
if err != nil {
return diag.FromErr(err)
}
masterComputeList = append(masterComputeList, *compute)
}
for _, worker := range cluster.K8SGroups.Workers {
for _, info := range worker.DetailedInfo {
compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID)
if err != nil {
return diag.FromErr(err)
}
workersComputeList = append(workersComputeList, *compute)
}
}
c := m.(*controller.ControllerCfg)
k8sId, _ := strconv.ParseUint(d.Id(), 10, 64)
getConfigReq := k8s.GetConfigRequest{K8SID: k8sId}
kubeconfig, err := c.CloudAPI().K8S().GetConfig(ctx, getConfigReq)
if err != nil {
log.Warnf("could not get kubeconfig: %v", err)
}
d.Set("kubeconfig", kubeconfig)
if cluster.LBID != 0 {
getLbReq := lb.GetRequest{LBID: cluster.LBID}
lb, err := c.CloudAPI().LB().Get(ctx, getLbReq)
if err != nil {
return diag.FromErr(err)
}
d.Set("extnet_id", lb.ExtNetID)
d.Set("lb_ip", lb.PrimaryNode.FrontendIP)
}
flattenK8sData(d, *cluster, masterComputeList, workersComputeList)
return nil
}
func aclListSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"explicit": {
Type: schema.TypeBool,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"right": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"user_group_id": {
Type: schema.TypeString,
Computed: true,
},
}
}
func aclGroupSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"account_acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: aclListSchemaMake(),
},
},
"k8s_acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: aclListSchemaMake(),
},
},
"rg_acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: aclListSchemaMake(),
},
},
}
}
func detailedInfoSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"compute_id": {
Type: schema.TypeInt,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"interfaces": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: interfacesSchemaMake(),
},
},
"natable_vins_ip": {
Type: schema.TypeString,
Computed: true,
},
"natable_vins_network": {
Type: schema.TypeString,
Computed: true,
},
}
}
func interfacesSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"def_gw": {
Type: schema.TypeString,
Computed: true,
},
"ip_address": {
Type: schema.TypeString,
Computed: true,
},
}
}
func masterGroupSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"detailed_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: detailedInfoSchemaMake(),
},
},
"disk": {
Type: schema.TypeInt,
Computed: true,
},
"master_id": {
Type: schema.TypeInt,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"num": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
}
}
func k8sGroupListSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"annotations": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"detailed_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: detailedInfoSchemaMake(),
},
},
"disk": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"id": {
Type: schema.TypeInt,
Computed: true,
},
"labels": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"num": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
"taints": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
}
}
func dataSourceK8sSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"k8s_id": {
Type: schema.TypeInt,
Required: true,
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: aclGroupSchemaMake(),
},
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"zone_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"bservice_id": {
Type: schema.TypeInt,
Computed: true,
},
"k8sci_id": {
Type: schema.TypeInt,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"extnet_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of the external network to connect workers to. If omitted network will be chosen by the platfom.",
},
"k8s_ci_name": {
Type: schema.TypeString,
Computed: true,
},
"masters": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: masterGroupSchemaMake(),
},
},
"workers": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: k8sGroupListSchemaMake(),
},
},
"lb_id": {
Type: schema.TypeInt,
Computed: true,
},
"lb_ip": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of default load balancer.",
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"network_plugin": {
Type: schema.TypeString,
Computed: true,
},
"rg_id": {
Type: schema.TypeInt,
Computed: true,
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"kubeconfig": {
Type: schema.TypeString,
Computed: true,
Description: "Kubeconfig for cluster access.",
},
"vins_id": {
Type: schema.TypeInt,
Computed: true,
},
}
}
func DataSourceK8s() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceK8sRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceK8sSchemaMake(),
}
}

View File

@@ -0,0 +1,119 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
Tim Tkachev, <tvtkachev@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceK8sComputesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
cluster, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
d.SetId(fmt.Sprint(cluster.ID))
flattenK8sDataComputes(d, cluster)
return nil
}
func computesSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"id": {
Type: schema.TypeInt,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"group_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
}
}
func dataSourceK8sComputesSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"k8s_id": {
Type: schema.TypeInt,
Required: true,
},
"masters": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: computesSchemaMake(),
},
},
"workers": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: computesSchemaMake(),
},
},
}
}
func DataSourceK8sComputes() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceK8sComputesRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout60s,
Default: &constants.Timeout60s,
},
Schema: dataSourceK8sComputesSchemaMake(),
}
}

View File

@@ -0,0 +1,362 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceK8sListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
k8sList, err := utilityK8sListCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
flattenK8sList(d, k8sList)
return nil
}
func serviceAccountSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"guid": {
Type: schema.TypeString,
Computed: true,
},
"password": {
Type: schema.TypeString,
Computed: true,
},
"username": {
Type: schema.TypeString,
Computed: true,
},
}
}
func k8sWorkersGroupsSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"annotations": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"cpu": {
Type: schema.TypeInt,
Computed: true,
},
"detailed_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: detailedInfoSchemaMake(),
},
},
"disk": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"detailed_info_id": {
Type: schema.TypeInt,
Computed: true,
},
"labels": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"num": {
Type: schema.TypeInt,
Computed: true,
},
"ram": {
Type: schema.TypeInt,
Computed: true,
},
"taints": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
}
}
func createK8sListSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"by_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Filter by ID",
},
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by name",
},
"ip_address": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by IP address",
},
"rg_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Filter by RG ID",
},
"lb_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Filter by LB ID",
},
"bservice_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Filter by BService ID",
},
"status": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by status",
},
"tech_status": {
Type: schema.TypeString,
Optional: true,
Description: "Filter by tech. status",
},
"includedeleted": {
Type: schema.TypeBool,
Optional: true,
},
"sort_by": {
Type: schema.TypeString,
Optional: true,
Description: "sort by one of supported fields, format +|-(field)",
},
"page": {
Type: schema.TypeInt,
Optional: true,
},
"size": {
Type: schema.TypeInt,
Optional: true,
},
"zone_id": {
Type: schema.TypeInt,
Optional: true,
Description: "Zone ID",
},
"items": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"bservice_id": {
Type: schema.TypeInt,
Computed: true,
},
"zone_id": {
Type: schema.TypeInt,
Computed: true,
},
"ci_id": {
Type: schema.TypeInt,
Computed: true,
},
"config": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"desc": {
Type: schema.TypeString,
Computed: true,
},
"extnet_id": {
Type: schema.TypeInt,
Computed: true,
},
"gid": {
Type: schema.TypeInt,
Computed: true,
},
"guid": {
Type: schema.TypeInt,
Computed: true,
},
"k8s_id": {
Type: schema.TypeInt,
Computed: true,
},
"lb_id": {
Type: schema.TypeInt,
Computed: true,
},
"milestones": {
Type: schema.TypeInt,
Computed: true,
},
"k8s_name": {
Type: schema.TypeString,
Computed: true,
},
"network_plugin": {
Type: schema.TypeString,
Computed: true,
},
"rg_id": {
Type: schema.TypeInt,
Computed: true,
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
},
"service_account": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: serviceAccountSchemaMake(),
},
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"vins_id": {
Type: schema.TypeInt,
Computed: true,
},
"workers_groups": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: k8sWorkersGroupsSchemaMake(),
},
},
},
},
},
"entry_count": {
Type: schema.TypeInt,
Computed: true,
},
}
}
func dataSourceK8sListSchemaMake() map[string]*schema.Schema {
k8sListSchema := createK8sListSchema()
return k8sListSchema
}
func DataSourceK8sList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceK8sListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceK8sListSchemaMake(),
}
}

View File

@@ -0,0 +1,78 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceK8sListDeletedRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
k8sList, err := utilityK8sListDeletedCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
flattenK8sList(d, k8sList)
return nil
}
func dataSourceK8sListDeletedSchemaMake() map[string]*schema.Schema {
k8sListDeleted := createK8sListSchema()
delete(k8sListDeleted, "includedeleted")
delete(k8sListDeleted, "status")
return k8sListDeleted
}
func DataSourceK8sListDeleted() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceK8sListDeletedRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceK8sListDeletedSchemaMake(),
}
}

View File

@@ -0,0 +1,151 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceK8sWgRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("dataSourceK8sWgRead: called with k8s id %d", d.Get("k8s_id").(int))
wg, workersComputeList, err := utilityDataK8sWgCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(d.Get("wg_id").(int)))
flattenWg(d, *wg, workersComputeList)
return nil
}
func dataSourceK8sWgSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"k8s_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of k8s instance.",
},
"wg_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of k8s worker Group.",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the worker group.",
},
"num": {
Type: schema.TypeInt,
Computed: true,
Description: "Number of worker nodes to create.",
},
"cpu": {
Type: schema.TypeInt,
Computed: true,
Description: "Worker node CPU count.",
},
"ram": {
Type: schema.TypeInt,
Computed: true,
Description: "Worker node RAM in MB.",
},
"disk": {
Type: schema.TypeInt,
Computed: true,
Description: "Worker node boot disk size. If unspecified or 0, size is defined by OS image size.",
},
"detailed_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: detailedInfoSchemaMake(),
},
},
"labels": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"annotations": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"taints": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
}
}
func DataSourceK8sWg() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceK8sWgRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceK8sWgSchemaMake(),
}
}

View File

@@ -0,0 +1,94 @@
/*
Copyright (c) 2019-2023 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceK8sWgCloudInitRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("dataSourceK8sWgCloudInitRead: called with k8s id %d and wg id %d", d.Get("k8s_id").(int), d.Get("wg_id").(int))
metaData, err := utilityK8sWgCloudInitCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
id := uuid.New()
d.SetId(id.String())
d.Set("cloud_init", metaData)
return nil
}
func dataSourceK8sWgCloudInitSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"k8s_id": {
Type: schema.TypeInt,
Required: true,
Description: "Kubernetes cluster ID",
},
"wg_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the workers compute group",
},
"cloud_init": {
Type: schema.TypeString,
Computed: true,
Description: "Worker group Cloud init",
},
}
}
func DataSourceK8sWgCloudInit() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceK8sWgCloudInitRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceK8sWgCloudInitSchemaMake(),
}
}

View File

@@ -0,0 +1,109 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
)
func dataSourceK8sWgListRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
wgList, err := utilityK8sWgListCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(d.Get("k8s_id").(int)))
workersComputeList := make(map[uint64][]compute.RecordCompute)
for _, worker := range wgList {
workersComputeList[worker.ID] = make([]compute.RecordCompute, 0, len(worker.DetailedInfo))
for _, info := range worker.DetailedInfo {
compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID)
if err != nil {
return diag.FromErr(err)
}
workersComputeList[worker.ID] = append(workersComputeList[worker.ID], *compute)
}
}
flattenItemsWg(d, wgList, workersComputeList)
return nil
}
func wgSchemaMake() map[string]*schema.Schema {
wgSchema := dataSourceK8sWgSchemaMake()
delete(wgSchema, "k8s_id")
wgSchema["wg_id"] = &schema.Schema{
Type: schema.TypeInt,
Computed: true,
Description: "ID of k8s worker Group.",
}
return wgSchema
}
func dataSourceK8sWgListSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"k8s_id": {
Type: schema.TypeInt,
Required: true,
},
"items": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: wgSchemaMake(),
},
},
}
}
func DataSourceK8sWgList() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
ReadContext: dataSourceK8sWgListRead,
Timeouts: &schema.ResourceTimeout{
Read: &constants.Timeout30s,
Default: &constants.Timeout60s,
},
Schema: dataSourceK8sWgListSchemaMake(),
}
}

View File

@@ -0,0 +1,468 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8ci"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
)
func flattenK8CIItems(list *k8ci.ListK8CI) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(list.Data))
for _, item := range list.Data {
temp := map[string]interface{}{
"k8ci_id": item.ID,
"name": item.Name,
"lb_image_id": item.LBImageID,
"network_plugins": item.NetworkPlugins,
"status": item.Status,
"desc": item.Description,
"created_time": item.CreatedTime,
"version": item.Version,
}
res = append(res, temp)
}
return res
}
func flattenK8CIList(d *schema.ResourceData, list *k8ci.ListK8CI) {
d.Set("items", flattenK8CIItems(list))
}
func flattenK8sDataComputes(d *schema.ResourceData, cluster *k8s.RecordK8S) {
d.Set("k8s_id", cluster.ID)
d.Set("masters", flattenMasterComputes(cluster))
d.Set("workers", flattenWorkerComputes(cluster))
}
func flattenMasterComputes(cluster *k8s.RecordK8S) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(cluster.K8SGroups.Masters.DetailedInfo))
for _, comp := range cluster.K8SGroups.Masters.DetailedInfo {
temp := map[string]interface{}{
"id": comp.ID,
"name": comp.Name,
"status": comp.Status,
"tech_status": comp.TechStatus,
"group_name": cluster.K8SGroups.Masters.Name,
}
res = append(res, temp)
}
return res
}
func flattenWorkerComputes(cluster *k8s.RecordK8S) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(cluster.K8SGroups.Workers))
for _, wg := range cluster.K8SGroups.Workers {
for _, comp := range wg.DetailedInfo {
temp := map[string]interface{}{
"id": comp.ID,
"name": comp.Name,
"status": comp.Status,
"tech_status": comp.TechStatus,
"group_name": wg.Name,
}
res = append(res, temp)
}
}
return res
}
func flattenAclList(aclList k8s.ListACL) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(aclList))
for _, acl := range aclList {
temp := map[string]interface{}{
"explicit": acl.Explicit,
"guid": acl.GUID,
"right": acl.Right,
"status": acl.Status,
"type": acl.Type,
"user_group_id": acl.UserGroupID,
}
res = append(res, temp)
}
return res
}
func flattenAcl(acl k8s.RecordACL) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"account_acl": flattenAclList(acl.AccountACL),
"k8s_acl": flattenAclList(acl.K8SACL),
"rg_acl": flattenAclList(acl.RGACL),
}
res = append(res, temp)
return res
}
func flattenInterfaces(interfaces compute.ListInterfaces) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(interfaces))
for _, interfaceCompute := range interfaces {
temp := map[string]interface{}{
"def_gw": interfaceCompute.DefGW,
"ip_address": interfaceCompute.IPAddress,
}
res = append(res, temp)
}
return res
}
func flattenDetailedInfo(detailedInfoList k8s.ListDetailedInfo, computes []compute.RecordCompute) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(detailedInfoList))
if computes != nil {
for i, detailedInfo := range detailedInfoList {
temp := map[string]interface{}{
"compute_id": detailedInfo.ID,
"name": detailedInfo.Name,
"status": detailedInfo.Status,
"tech_status": detailedInfo.TechStatus,
"interfaces": flattenInterfaces(computes[i].Interfaces),
"natable_vins_ip": computes[i].NatableVINSIP,
"natable_vins_network": computes[i].NatableVINSNetwork,
}
res = append(res, temp)
}
} else {
for _, detailedInfo := range detailedInfoList {
temp := map[string]interface{}{
"compute_id": detailedInfo.ID,
"name": detailedInfo.Name,
"status": detailedInfo.Status,
"tech_status": detailedInfo.TechStatus,
}
res = append(res, temp)
}
}
return res
}
func flattenMasterGroup(mastersGroup k8s.MasterGroup, masters []compute.RecordCompute) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"cpu": mastersGroup.CPU,
"detailed_info": flattenDetailedInfo(mastersGroup.DetailedInfo, masters),
"disk": mastersGroup.Disk,
"master_id": mastersGroup.ID,
"name": mastersGroup.Name,
"num": mastersGroup.Num,
"ram": mastersGroup.RAM,
}
res = append(res, temp)
return res
}
func flattenK8sGroup(k8SGroupList k8s.ListK8SGroups, workers []compute.RecordCompute) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(k8SGroupList))
for _, k8sGroup := range k8SGroupList {
labels := make([]string, 0)
for _, label := range k8sGroup.Labels {
if strings.HasPrefix(label, "workersGroupName") {
continue
}
labels = append(labels, label)
}
temp := map[string]interface{}{
"annotations": k8sGroup.Annotations,
"cpu": k8sGroup.CPU,
"detailed_info": flattenDetailedInfo(k8sGroup.DetailedInfo, workers),
"disk": k8sGroup.Disk,
"guid": k8sGroup.GUID,
"id": k8sGroup.ID,
"labels": labels,
"name": k8sGroup.Name,
"num": k8sGroup.Num,
"ram": k8sGroup.RAM,
"taints": k8sGroup.Taints,
}
res = append(res, temp)
}
return res
}
// func flattenK8sGroups(k8sGroups k8s.RecordK8SGroups, masters []compute.RecordCompute, workers []compute.RecordCompute) []map[string]interface{} {
// res := make([]map[string]interface{}, 0)
// temp := map[string]interface{}{
// "masters": flattenMasterGroup(k8sGroups.Masters, masters),
// "workers": flattenK8sGroup(k8sGroups.Workers, workers),
// }
// res = append(res, temp)
// return res
// }
func flattenK8sData(d *schema.ResourceData, cluster k8s.RecordK8S, masters []compute.RecordCompute, workers []compute.RecordCompute) {
d.Set("acl", flattenAcl(cluster.ACL))
d.Set("account_id", cluster.AccountID)
d.Set("account_name", cluster.AccountName)
d.Set("bservice_id", cluster.BServiceID)
d.Set("k8sci_id", cluster.CIID)
d.Set("created_by", cluster.CreatedBy)
d.Set("created_time", cluster.CreatedTime)
d.Set("desc", cluster.Description)
d.Set("deleted_by", cluster.DeletedBy)
d.Set("deleted_time", cluster.DeletedTime)
d.Set("k8s_ci_name", cluster.K8CIName)
d.Set("masters", flattenMasterGroup(cluster.K8SGroups.Masters, masters))
d.Set("workers", flattenK8sGroup(cluster.K8SGroups.Workers, workers))
d.Set("lb_id", cluster.LBID)
d.Set("name", cluster.Name)
d.Set("network_plugin", cluster.NetworkPlugin)
d.Set("rg_id", cluster.RGID)
d.Set("rg_name", cluster.RGName)
d.Set("status", cluster.Status)
d.Set("tech_status", cluster.TechStatus)
d.Set("updated_by", cluster.UpdatedBy)
d.Set("updated_time", cluster.UpdatedTime)
d.Set("highly_available_lb", cluster.HighlyAvailableLB)
d.Set("address_vip", flattenAddressVIP(cluster.AddressVIP))
d.Set("extnet_only", cluster.ExtnetOnly)
d.Set("with_lb", cluster.WithLB)
d.Set("zone_id", cluster.ZoneID)
}
func flattenAddressVIP(addressVIP k8s.K8SAddressVIP) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"backend_ip": addressVIP.BackendIP,
"frontend_ip": addressVIP.FrontendIP,
}
res = append(res, temp)
return res
}
func flattenServiceAccount(serviceAccount k8s.RecordServiceAccount) []map[string]interface{} {
res := make([]map[string]interface{}, 0)
temp := map[string]interface{}{
"guid": serviceAccount.GUID,
"password": serviceAccount.Password,
"username": serviceAccount.Username,
}
res = append(res, temp)
return res
}
func flattenWorkersGroup(workersGroups k8s.ListK8SGroups) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(workersGroups))
for _, worker := range workersGroups {
temp := map[string]interface{}{
"annotations": worker.Annotations,
"cpu": worker.CPU,
"detailed_info": flattenDetailedInfo(worker.DetailedInfo, nil),
"disk": worker.Disk,
"guid": worker.GUID,
"detailed_info_id": worker.ID,
"labels": worker.Labels,
"name": worker.Name,
"num": worker.Num,
"ram": worker.RAM,
"taints": worker.Taints,
}
res = append(res, temp)
}
return res
}
func flattenK8sItems(k8sItems *k8s.ListK8SClusters) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(k8sItems.Data))
for _, item := range k8sItems.Data {
temp := map[string]interface{}{
"account_id": item.AccountID,
"account_name": item.AccountName,
"acl": item.ACL,
"bservice_id": item.BServiceID,
"ci_id": item.CIID,
"created_by": item.CreatedBy,
"created_time": item.CreatedTime,
"deleted_by": item.DeletedBy,
"deleted_time": item.DeletedTime,
"desc": item.Description,
"extnet_id": item.ExtNetID,
"gid": item.GID,
"guid": item.GUID,
"k8s_id": item.ID,
"lb_id": item.LBID,
"milestones": item.Milestones,
"k8s_name": item.Name,
"network_plugin": item.NetworkPlugin,
"rg_id": item.RGID,
"rg_name": item.RGName,
"service_account": flattenServiceAccount(item.ServiceAccount),
"status": item.Status,
"tech_status": item.TechStatus,
"updated_by": item.UpdatedBy,
"updated_time": item.UpdatedTime,
"vins_id": item.VINSID,
"zone_id": item.ZoneID,
"workers_groups": flattenWorkersGroup(item.WorkersGroup),
}
res = append(res, temp)
}
return res
}
func flattenK8sList(d *schema.ResourceData, k8sItems *k8s.ListK8SClusters) {
d.Set("items", flattenK8sItems(k8sItems))
d.Set("entry_count", k8sItems.EntryCount)
}
func flattenResourceK8sCP(d *schema.ResourceData, k8s k8s.RecordK8S, masters []compute.RecordCompute) {
d.Set("acl", flattenAcl(k8s.ACL))
d.Set("account_id", k8s.AccountID)
d.Set("account_name", k8s.AccountName)
d.Set("k8sci_id", k8s.CIID)
d.Set("bservice_id", k8s.BServiceID)
d.Set("created_by", k8s.CreatedBy)
d.Set("created_time", k8s.CreatedTime)
d.Set("desc", k8s.Description)
d.Set("deleted_by", k8s.DeletedBy)
d.Set("deleted_time", k8s.DeletedTime)
d.Set("k8s_ci_name", k8s.K8CIName)
d.Set("with_lb", k8s.LBID != 0)
d.Set("lb_id", k8s.LBID)
d.Set("k8s_id", k8s.ID)
d.Set("name", k8s.Name)
d.Set("rg_id", k8s.RGID)
d.Set("rg_name", k8s.RGName)
d.Set("status", k8s.Status)
d.Set("tech_status", k8s.TechStatus)
d.Set("updated_by", k8s.UpdatedBy)
d.Set("updated_time", k8s.UpdatedTime)
d.Set("network_plugin", k8s.NetworkPlugin)
d.Set("zone_id", k8s.ZoneID)
flattenCPParams(d, k8s.K8SGroups.Masters, masters)
}
func flattenCPParams(d *schema.ResourceData, mastersGroup k8s.MasterGroup, masters []compute.RecordCompute) {
d.Set("cpu", mastersGroup.CPU)
d.Set("detailed_info", flattenDetailedInfo(mastersGroup.DetailedInfo, masters))
d.Set("disk", mastersGroup.Disk)
d.Set("master_id", mastersGroup.ID)
d.Set("master_name", mastersGroup.Name)
d.Set("num", mastersGroup.Num)
d.Set("ram", mastersGroup.RAM)
d.Set("master_id", mastersGroup.ID)
}
func flattenResourceK8s(d *schema.ResourceData, k8s k8s.RecordK8S, masters []compute.RecordCompute, workers []compute.RecordCompute) {
wg_name := k8s.K8SGroups.Workers[0].Name
d.Set("acl", flattenAcl(k8s.ACL))
d.Set("account_id", k8s.AccountID)
d.Set("account_name", k8s.AccountName)
d.Set("k8sci_id", k8s.CIID)
d.Set("wg_name", wg_name)
d.Set("bservice_id", k8s.BServiceID)
d.Set("created_by", k8s.CreatedBy)
d.Set("created_time", k8s.CreatedTime)
d.Set("desc", k8s.Description)
d.Set("deleted_by", k8s.DeletedBy)
d.Set("deleted_time", k8s.DeletedTime)
d.Set("k8s_ci_name", k8s.K8CIName)
d.Set("masters", flattenMasterGroup(k8s.K8SGroups.Masters, masters))
d.Set("workers", flattenK8sGroup(k8s.K8SGroups.Workers, workers))
d.Set("with_lb", k8s.LBID != 0)
d.Set("lb_id", k8s.LBID)
d.Set("name", k8s.Name)
d.Set("rg_id", k8s.RGID)
d.Set("rg_name", k8s.RGName)
d.Set("status", k8s.Status)
d.Set("tech_status", k8s.TechStatus)
d.Set("updated_by", k8s.UpdatedBy)
d.Set("updated_time", k8s.UpdatedTime)
d.Set("default_wg_id", k8s.K8SGroups.Workers[0].ID)
d.Set("network_plugin", k8s.NetworkPlugin)
d.Set("zone_id", k8s.ZoneID)
}
func flattenWg(d *schema.ResourceData, wg k8s.ItemK8SGroup, computes []compute.RecordCompute) {
labels := make([]string, 0, len(wg.Labels))
for _, label := range wg.Labels {
if strings.HasPrefix(label, "workersGroupName") {
continue
}
labels = append(labels, label)
}
d.Set("annotations", wg.Annotations)
d.Set("cpu", wg.CPU)
d.Set("detailed_info", flattenDetailedInfo(wg.DetailedInfo, computes))
d.Set("disk", wg.Disk)
d.Set("guid", wg.GUID)
d.Set("labels", labels)
d.Set("name", wg.Name)
d.Set("num", wg.Num)
d.Set("ram", wg.RAM)
d.Set("taints", wg.Taints)
}
func flattenWgList(wgList k8s.ListK8SGroups, computesMap map[uint64][]compute.RecordCompute) []map[string]interface{} {
res := make([]map[string]interface{}, 0, len(wgList))
for _, wg := range wgList {
computes := computesMap[wg.ID]
temp := map[string]interface{}{
"annotations": wg.Annotations,
"cpu": wg.CPU,
"wg_id": wg.ID,
"detailed_info": flattenDetailedInfo(wg.DetailedInfo, computes),
"disk": wg.Disk,
"guid": wg.GUID,
"labels": wg.Labels,
"name": wg.Name,
"num": wg.Num,
"ram": wg.RAM,
"taints": wg.Taints,
}
res = append(res, temp)
}
return res
}
func flattenItemsWg(d *schema.ResourceData, wgList k8s.ListK8SGroups, computes map[uint64][]compute.RecordCompute) {
d.Set("items", flattenWgList(wgList, computes))
}

View File

@@ -0,0 +1,262 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"encoding/json"
"fmt"
"strconv"
)
type K8sNodeRecord struct {
ID int `json:"id"`
Name string `json:"name"`
Disk int `json:"disk"`
Cpu int `json:"cpu"`
Num int `json:"num"`
Ram int `json:"ram"`
// coming in future updates (curr. version 4.0.2)
// Labels []interface{} `json:"labels"`
// Annotations []interface{} `json:"annotations"`
// Taints []interface{} `json:"taints"`
DetailedInfo []struct {
ID int `json:"id"`
Name string `json:"name"`
} `json:"detailedInfo"`
SepID int `json:"SepId"`
SepPool string `json:"SepPool"`
}
// K8sRecord represents k8s instance
type K8sRecord struct {
AccountID int `json:"accountId"`
AccountName string `json:"accountName"`
CI int `json:"ciId"`
ID int `json:"id"`
Groups struct {
Masters K8sNodeRecord `json:"masters"`
Workers []K8sNodeRecord `json:"workers"`
} `json:"k8sGroups"`
LbID int `json:"lbId"`
Name string `json:"name"`
RgID int `json:"rgId"`
RgName string `json:"rgName"`
VinsID int `json:"vinsId"`
}
type K8sRecordList []K8sRecord
// LbRecord represents load balancer instance
type LbRecord struct {
ID int `json:"id"`
Name string `json:"name"`
RgID int `json:"rgId"`
VinsID int `json:"vinsId"`
ExtNetID int `json:"extnetId"`
PrimaryNode struct {
BackendIP string `json:"backendIp"`
ComputeID int `json:"computeId"`
FrontendIP string `json:"frontendIp"`
NetworkID int `json:"networkId"`
} `json:"primaryNode"`
}
// Blasphemous workaround for parsing Result value
type TaskResult int
func (r *TaskResult) UnmarshalJSON(b []byte) error {
if b[0] == '"' {
b := b[1 : len(b)-1]
if len(b) == 0 {
*r = 0
return nil
}
n, err := strconv.Atoi(string(b))
if err != nil {
return err
}
*r = TaskResult(n)
} else if b[0] == '[' {
res := []interface{}{}
if err := json.Unmarshal(b, &res); err != nil {
return err
}
if n, ok := res[0].(float64); ok {
*r = TaskResult(n)
} else {
return fmt.Errorf("could not unmarshal %v into int", res[0])
}
}
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 TaskResult `json:"result"`
Stage string `json:"stage"`
Status string `json:"status"`
UpdateTime uint64 `json:"updateTime"`
UpdatedTime uint64 `json:"updatedTime"`
}
type SshKeyConfig struct {
User string
SshKey string
UserShell string
}
// FromSDK
type K8SGroup struct {
Annotations []string `json:"annotations"`
CPU uint64 `json:"cpu"`
DetailedInfo DetailedInfoList `json:"detailedInfo"`
Disk uint64 `json:"disk"`
GUID string `json:"guid"`
ID uint64 `json:"id"`
Labels []string `json:"labels"`
Name string `json:"name"`
Num uint64 `json:"num"`
RAM uint64 `json:"ram"`
Taints []string `json:"taints"`
}
type K8SGroupList []K8SGroup
type DetailedInfo struct {
ID uint64 `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
}
type DetailedInfoList []DetailedInfo
type K8SRecord struct {
ACL ACLGroup `json:"ACL"`
AccountID uint64 `json:"accountId"`
AccountName string `json:"accountName"`
BServiceID uint64 `json:"bserviceId"`
CIID uint64 `json:"ciId"`
CreatedBy string `json:"createdBy"`
CreatedTime uint64 `json:"createdTime"`
DeletedBy string `json:"deletedBy"`
DeletedTime uint64 `json:"deletedTime"`
ID uint64 `json:"id"`
K8CIName string `json:"k8ciName"`
K8SGroups K8SGroups `json:"k8sGroups"`
LBID uint64 `json:"lbId"`
Name string `json:"name"`
RGID uint64 `json:"rgId"`
RGName string `json:"rgName"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
UpdatedBy string `json:"updatedBy"`
UpdatedTime uint64 `json:"updatedTime"`
}
type K8SRecordList []K8SRecord
type K8SGroups struct {
Masters MasterGroup `json:"masters"`
Workers K8SGroupList `json:"workers"`
}
type MasterGroup struct {
CPU uint64 `json:"cpu"`
DetailedInfo DetailedInfoList `json:"detailedInfo"`
Disk uint64 `json:"disk"`
ID uint64 `json:"id"`
Name string `json:"name"`
Num uint64 `json:"num"`
RAM uint64 `json:"ram"`
}
type ACLGroup struct {
AccountACL ACLList `json:"accountAcl"`
K8SACL ACLList `json:"k8sAcl"`
RGACL ACLList `json:"rgAcl"`
}
type ACL struct {
Explicit bool `json:"explicit"`
GUID string `json:"guid"`
Right string `json:"right"`
Status string `json:"status"`
Type string `json:"type"`
UserGroupID string `json:"userGroupId"`
}
type ACLList []ACL
type K8SItem struct {
AccountID uint64 `json:"accountId"`
AccountName string `json:"accountName"`
ACL []interface{} `json:"acl"`
BServiceID uint64 `json:"bserviceId"`
CIID uint64 `json:"ciId"`
Config interface{} `json:"config"`
CreatedBy string `json:"createdBy"`
CreatedTime uint64 `json:"createdTime"`
DeletedBy string `json:"deletedBy"`
DeletedTime uint64 `json:"deletedTime"`
Description string `json:"desc"`
ExtNetID uint64 `json:"extnetId"`
GID uint64 `json:"gid"`
GUID uint64 `json:"guid"`
ID uint64 `json:"id"`
LBID uint64 `json:"lbId"`
Milestones uint64 `json:"milestones"`
Name string `json:"name"`
RGID uint64 `json:"rgId"`
RGName string `json:"rgName"`
ServiceAccount ServiceAccount `json:"serviceAccount"`
Status string `json:"status"`
TechStatus string `json:"techStatus"`
UpdatedBy string `json:"updatedBy"`
UpdatedTime uint64 `json:"updatedTime"`
VINSID uint64 `json:"vinsId"`
WorkersGroup K8SGroupList `json:"workersGroups"`
}
type ServiceAccount struct {
GUID string `json:"guid"`
Password string `json:"password"`
Username string `json:"username"`
}
type K8SList []K8SItem

View File

@@ -0,0 +1,194 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/validators"
)
func nodeMasterDefault() K8sNodeRecord {
return K8sNodeRecord{
Num: 1,
Cpu: 2,
Ram: 2048,
Disk: 0,
}
}
func nodeWorkerDefault() K8sNodeRecord {
return K8sNodeRecord{
Num: 1,
Cpu: 1,
Ram: 1024,
Disk: 0,
}
}
func parseDefaultNode(nodeList []interface{}) K8sNodeRecord {
node := nodeList[0].(map[string]interface{})
return K8sNodeRecord{
Num: node["num"].(int),
Cpu: node["cpu"].(int),
Ram: node["ram"].(int),
Disk: node["disk"].(int),
SepID: node["sep_id"].(int),
SepPool: node["sep_pool"].(string),
}
}
func mastersSchemaMake() map[string]*schema.Schema {
masters := masterGroupSchemaMake()
masters["num"] = &schema.Schema{
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntInSlice([]int{1, 3, 5}),
Description: "Number of nodes to create. Can be either 1, 3 or 5",
}
masters["sep_id"] = &schema.Schema{
Type: schema.TypeInt,
Optional: true,
}
masters["sep_pool"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
}
masters["cpu"] = &schema.Schema{
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
Description: "Node CPU count.",
}
masters["ram"] = &schema.Schema{
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
ValidateFunc: validation.All(
validation.IntAtLeast(constants.MIN_RAM_PER_COMPUTE),
validators.DivisibleBy(constants.RAM_DIVISIBILITY),
),
Description: "Node RAM in MB.",
}
masters["disk"] = &schema.Schema{
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
Description: "Node boot disk size in GB.",
}
return masters
}
func workersSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"num": {
Type: schema.TypeInt,
Required: true,
},
"ram": {
Type: schema.TypeInt,
ValidateFunc: validation.All(
validation.IntAtLeast(constants.MIN_RAM_PER_COMPUTE),
validators.DivisibleBy(constants.RAM_DIVISIBILITY),
),
Required: true,
},
"cpu": {
Type: schema.TypeInt,
Required: true,
},
"disk": {
Type: schema.TypeInt,
Required: true,
},
"annotations": {
Type: schema.TypeList,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"detailed_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: detailedInfoSchemaMake(),
},
},
"chipset": {
Type: schema.TypeString,
Optional: true,
Description: "Type of the emulated system.",
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"id": {
Type: schema.TypeInt,
Computed: true,
},
"labels": {
Type: schema.TypeList,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"taints": {
Type: schema.TypeList,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"sep_id": {
Type: schema.TypeInt,
Optional: true,
},
"sep_pool": {
Type: schema.TypeString,
Optional: true,
},
}
}

View File

@@ -0,0 +1,396 @@
package k8s
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
func resourceK8sCPSchemaV1() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of the cluster.",
},
"rg_id": {
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
Description: "Resource group ID that this instance belongs to.",
},
"k8sci_id": {
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
Description: "ID of the k8s catalog item to base this instance on.",
},
"network_plugin": {
Type: schema.TypeString,
Required: true,
Description: "Network plugin to be used",
ValidateFunc: validation.StringInSlice([]string{"flannel", "weavenet", "calico"}, true),
},
"num": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validation.IntInSlice([]int{1, 3}),
Description: "Number of VMs to create. Can be either 1 or 3",
},
"cpu": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "Node CPU count.",
},
"ram": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "Node RAM in MB.",
},
"disk": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "Node boot disk size in GB.",
},
"sep_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "Storage Endpoint ID",
},
"sep_pool": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Storage Endpoint Pool",
},
"with_lb": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Create k8s with load balancer if true.",
},
"extnet_only": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Use only selected ExtNet for infrastructure connections",
},
// /4.4.0
"cloud_init": {
Type: schema.TypeString,
Optional: true,
Description: "Meta data for working group computes, format YAML 'user_data': 1111",
},
"join_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings for joining a node to a cluster. It includes parameters such as the cluster's control plane endpoint, token, and certificate key. insert a valid JSON string with all levels of nesting.",
},
"kube_proxy_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings of the Kube-proxy, which is responsible for network proxying and load balancing within the cluster. It includes parameters such as proxy mode, cluster IP ranges, and other Kube-proxy specific configurations. insert a valid JSON string with all levels of nesting.",
},
"kubelet_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings of the Kubelet, which is the primary node agent that runs on each node in the cluster. It includes parameters such as node IP address, resource allocation, pod eviction policies, and other Kubelet-specific configurations. insert a valid JSON string with all levels of nesting.",
},
"cluster_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to define global settings and configurations for the entire cluster. It includes parameters such as cluster name, DNS settings, authentication methods, and other cluster-wide configurations. insert a valid JSON string with all levels of nesting.",
},
"init_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to define settings and actions that should be performed before any other component in the cluster starts. It allows you to configure things like node registration, network setup, and other initialization tasks. insert a valid JSON string with all levels of nesting.",
},
"additional_sans": {
Type: schema.TypeList,
Optional: true,
Description: "Optional extra Subject Alternative Names (SANs) to use for the API Server serving certificate. Can be both IP addresses and DNS names",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"ha_mode": {
Type: schema.TypeBool,
Optional: true,
Description: "Use Highly Available schema for LB deploy",
},
"lb_sysctl_params": {
Type: schema.TypeString,
Optional: true,
Description: "Custom sysctl values for Load Balancer instance. Applied on boot.",
},
"oidc_cert": {
Type: schema.TypeString,
Optional: true,
Description: "insert ssl certificate in x509 pem format",
},
////
"extnet_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
//ForceNew: true,
Description: "ID of the external network to connect workers to. If omitted network will be chosen by the platfom.",
},
"desc": {
Type: schema.TypeString,
Optional: true,
Description: "Text description of this instance.",
},
"start": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Start k8s cluster.",
},
"detailed_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"compute_id": {
Type: schema.TypeInt,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"interfaces": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"def_gw": {
Type: schema.TypeString,
Computed: true,
},
"ip_address": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"natable_vins_ip": {
Type: schema.TypeString,
Computed: true,
},
"natable_vins_network": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"master_id": {
Type: schema.TypeInt,
Computed: true,
Description: "Master group ID.",
},
"master_name": {
Type: schema.TypeString,
Computed: true,
Description: "Master group name.",
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"explicit": {
Type: schema.TypeBool,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"right": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"user_group_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"k8s_acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"explicit": {
Type: schema.TypeBool,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"right": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"user_group_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"rg_acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"explicit": {
Type: schema.TypeBool,
Computed: true,
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
"right": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"user_group_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
},
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"bservice_id": {
Type: schema.TypeInt,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"k8s_ci_name": {
Type: schema.TypeString,
Computed: true,
},
"lb_id": {
Type: schema.TypeInt,
Computed: true,
},
"k8s_id": {
Type: schema.TypeInt,
Computed: true,
},
"lb_ip": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of default load balancer.",
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"kubeconfig": {
Type: schema.TypeString,
Computed: true,
Description: "Kubeconfig for cluster access.",
},
"vins_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "ID of default vins for this instace.",
},
},
}
}

View File

@@ -0,0 +1,69 @@
package k8s
import (
"context"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/extnet"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8ci"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/rg"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
)
func existK8sID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
c := m.(*controller.ControllerCfg)
k8sID := uint64(d.Get("k8s_id").(int))
req := k8s.ListRequest{}
k8sList, err := c.CloudAPI().K8S().List(ctx, req)
if err != nil {
return false, err
}
return len(k8sList.FilterByID(k8sID).Data) != 0, nil
}
func existK8sCIID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
c := m.(*controller.ControllerCfg)
k8sciID := uint64(d.Get("k8sci_id").(int))
req := k8ci.ListRequest{}
k8sciList, err := c.CloudAPI().K8CI().List(ctx, req)
if err != nil {
return false, err
}
return len(k8sciList.FilterByID(k8sciID).Data) != 0, nil
}
func existRGID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
c := m.(*controller.ControllerCfg)
rgID := uint64(d.Get("rg_id").(int))
req := rg.ListRequest{}
rgList, err := c.CloudAPI().RG().List(ctx, req)
if err != nil {
return false, err
}
return len(rgList.FilterByID(rgID).Data) != 0, nil
}
func existExtNetID(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
extNetID := uint64(d.Get("extnet_id").(int))
if extNetID == 0 {
return true, nil
}
c := m.(*controller.ControllerCfg)
req := extnet.ListRequest{}
extNetList, err := c.CloudAPI().ExtNet().List(ctx, req)
if err != nil {
return false, err
}
return len(extNetList.FilterByID(extNetID).Data) != 0, nil
}

View File

@@ -0,0 +1,938 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"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"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/lb"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/tasks"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/status"
)
func resourceK8sCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sCreate: called with name %s, rg %d", d.Get("name").(string), d.Get("rg_id").(int))
haveRGID, err := existRGID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveRGID {
return diag.Errorf("resourceK8sCreate: can't create k8s cluster because RGID %d is not allowed or does not exist", d.Get("rg_id").(int))
}
haveK8sciID, err := existK8sCIID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveK8sciID {
return diag.Errorf("resourceK8sCreate: can't create k8s cluster because K8sCIID %d is not allowed or does not exist", d.Get("k8sci_id").(int))
}
if _, ok := d.GetOk("extnet_id"); ok {
haveExtNetID, err := existExtNetID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveExtNetID {
return diag.Errorf("resourceK8sCreate: can't create k8s cluster because ExtNetID %d is not allowed or does not exist", d.Get("extnet_id").(int))
}
}
c := m.(*controller.ControllerCfg)
createReq := k8s.CreateRequest{}
createReq.Name = d.Get("name").(string)
createReq.RGID = uint64(d.Get("rg_id").(int))
createReq.K8SCIID = uint64(d.Get("k8sci_id").(int))
createReq.WorkerGroupName = d.Get("wg_name").(string)
createReq.NetworkPlugin = d.Get("network_plugin").(string)
createReq.StoragePolicyID = uint64(d.Get("storage_policy_id").(int))
var masterNode K8sNodeRecord
if masters, ok := d.GetOk("masters"); ok {
masterNode = parseDefaultNode(masters.([]interface{}))
} else {
masterNode = nodeMasterDefault()
}
createReq.MasterNum = uint(masterNode.Num)
createReq.MasterCPU = uint(masterNode.Cpu)
createReq.MasterRAM = uint64(masterNode.Ram)
createReq.MasterDisk = uint(masterNode.Disk)
createReq.MasterSEPID = uint64(masterNode.SepID)
createReq.MasterSEPPool = masterNode.SepPool
var workerNode K8sNodeRecord
if workers, ok := d.GetOk("workers"); ok {
workerNode = parseDefaultNode(workers.([]interface{}))
} else {
workerNode = nodeWorkerDefault()
}
createReq.WorkerNum = uint(workerNode.Num)
createReq.WorkerCPU = uint(workerNode.Cpu)
createReq.WorkerRAM = uint64(workerNode.Ram)
createReq.WorkerDisk = uint(workerNode.Disk)
createReq.WorkerSEPID = uint64(workerNode.SepID)
createReq.WorkerSEPPool = workerNode.SepPool
if labels, ok := d.GetOk("labels"); ok {
labels := labels.([]interface{})
for _, label := range labels {
if !strings.HasPrefix(label.(string), "workersGroupName") {
createReq.Labels = append(createReq.Labels, label.(string))
}
}
}
if taints, ok := d.GetOk("taints"); ok {
taints := taints.([]interface{})
for _, taint := range taints {
createReq.Taints = append(createReq.Taints, taint.(string))
}
}
if annotations, ok := d.GetOk("annotations"); ok {
annotations := annotations.([]interface{})
for _, annotation := range annotations {
createReq.Annotations = append(createReq.Annotations, annotation.(string))
}
}
if zoneID, ok := d.GetOk("zone_id"); ok {
createReq.ZoneID = uint64(zoneID.(int))
}
createReq.WithLB = d.Get("with_lb").(bool)
///4.4.0
createReq.HighlyAvailable = d.Get("ha_mode").(bool)
if additionalSans, ok := d.GetOk("additional_sans"); ok {
addSans := additionalSans.([]interface{})
resSans := make([]string, 0)
for _, san := range addSans {
resSans = append(resSans, san.(string))
}
createReq.AdditionalSANs = resSans
}
if clusterConfig, ok := d.GetOk("cluster_config"); ok {
createReq.ClusterConfiguration = clusterConfig.(string)
}
if kubeletConfig, ok := d.GetOk("kubelet_config"); ok {
createReq.KubeletConfiguration = kubeletConfig.(string)
}
if kubeProxyConfig, ok := d.GetOk("kube_proxy_config"); ok {
createReq.KubeProxyConfiguration = kubeProxyConfig.(string)
}
if joinConfig, ok := d.GetOk("join_config"); ok {
createReq.JoinConfiguration = joinConfig.(string)
}
if cloudInit, ok := d.GetOk("cloud_init"); ok {
createReq.UserData = cloudInit.(string)
}
if initConfig, ok := d.GetOk("init_config"); ok {
createReq.InitConfiguration = initConfig.(string)
}
if lbSysctlParams, ok := d.GetOk("lb_sysctl_params"); ok {
syscrlSliceMaps := lbSysctlParams.([]interface{})
res := make([]map[string]interface{}, 0, len(syscrlSliceMaps))
for _, syscrlMap := range syscrlSliceMaps {
tempMap := make(map[string]interface{})
for k, v := range syscrlMap.(map[string]interface{}) {
if intVal, err := strconv.Atoi(v.(string)); err == nil {
tempMap[k] = intVal
continue
}
tempMap[k] = v.(string)
}
res = append(res, tempMap)
}
createReq.LbSysctlParams = res
}
if oidcCertificate, ok := d.GetOk("oidc_cert"); ok {
createReq.OidcCertificate = oidcCertificate.(string)
}
///
if chipset, ok := d.GetOk("chipset"); ok {
createReq.Chipset = chipset.(string)
}
createReq.ExtNetOnly = d.Get("extnet_only").(bool)
if extNet, ok := d.GetOk("extnet_id"); ok {
createReq.ExtNetID = uint64(extNet.(int))
} else {
createReq.ExtNetID = 0
}
if vins, ok := d.GetOk("vins_id"); ok {
createReq.VinsId = uint64(vins.(int))
} else {
createReq.VinsId = 0
}
if desc, ok := d.GetOk("desc"); ok {
createReq.Description = desc.(string)
}
resp, err := c.CloudAPI().K8S().Create(ctx, createReq)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
taskReq := tasks.GetRequest{
AuditID: strings.Trim(resp, `"`),
}
for {
task, err := c.CloudAPI().Tasks().Get(ctx, taskReq)
if err != nil {
return diag.FromErr(err)
}
log.Debugf("resourceK8sCreate: instance creating - %s", task.Stage)
if task.Completed {
if task.Error != "" {
return diag.FromErr(fmt.Errorf("cannot create k8s instance: %v", task.Error))
}
id, err := task.Result.ID()
if err != nil {
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(id))
break
}
time.Sleep(time.Second * 20)
}
return resourceK8sRead(ctx, d, m)
}
func resourceK8sRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
cluster, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
c := m.(*controller.ControllerCfg)
hasChanged := false
switch cluster.Status {
case status.Modeled:
return diag.Errorf("The k8s cluster is in status: %s, please, contact support for more information", cluster.Status)
case status.Creating:
case status.Created:
case status.Deleting:
case status.Deleted:
// id, _ := strconv.ParseUint(d.Id(), 10, 64)
// restoreReq := k8s.RestoreRequest{
// K8SID: id,
// }
// _, err := c.CloudAPI().K8S().Restore(ctx, restoreReq)
// if err != nil {
// return diag.FromErr(err)
// }
// enableReq := k8s.DisableEnableRequest{
// K8SID: id,
// }
// _, err = c.CloudAPI().K8S().Enable(ctx, enableReq)
// if err != nil {
// return diag.FromErr(err)
// }
// hasChanged = true
case status.Destroying:
return diag.Errorf("The k8s cluster is in progress with status: %s", cluster.Status)
case status.Destroyed:
d.SetId("")
return diag.Errorf("The resource cannot be updated because it has been destroyed")
// return resourceK8sCreate(ctx, d, m)
case status.Enabling:
case status.Enabled:
case status.Disabling:
case status.Disabled:
log.Debugf("The k8s cluster is in status: %s, troubles may occur with update. Please, enable compute first.", cluster.Status)
case status.Restoring:
}
if hasChanged {
cluster, err = utilityK8sCheckPresence(ctx, d, m)
if cluster == nil {
d.SetId("")
if err != nil {
return diag.FromErr(err)
}
return nil
}
}
if d.Get("start").(bool) {
if cluster.TechStatus == "STOPPED" {
req := k8s.StartRequest{
K8SID: cluster.ID,
}
_, err := c.CloudAPI().K8S().Start(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
}
k8sList, err := utilityK8sListForResourceCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
curK8s := k8s.ItemK8SCluster{}
for _, k8sCluster := range k8sList.Data {
if k8sCluster.ID == cluster.ID {
curK8s = k8sCluster
}
}
if curK8s.ID == 0 {
return diag.Errorf("Cluster with id %d not found", cluster.ID)
}
d.Set("vins_id", curK8s.VINSID)
masterComputeList := make([]compute.RecordCompute, 0, len(cluster.K8SGroups.Masters.DetailedInfo))
workersComputeList := make([]compute.RecordCompute, 0, len(cluster.K8SGroups.Workers))
for _, masterNode := range cluster.K8SGroups.Masters.DetailedInfo {
compute, err := utilityComputeCheckPresence(ctx, d, m, masterNode.ID)
if err != nil {
return diag.FromErr(err)
}
masterComputeList = append(masterComputeList, *compute)
}
for _, worker := range cluster.K8SGroups.Workers {
for _, info := range worker.DetailedInfo {
compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID)
if err != nil {
return diag.FromErr(err)
}
workersComputeList = append(workersComputeList, *compute)
}
}
flattenResourceK8s(d, *cluster, masterComputeList, workersComputeList)
if d.Get("with_lb").(bool) || cluster.LBID != 0 {
lbGetReq := lb.GetRequest{
LBID: cluster.LBID,
}
lb, err := c.CloudAPI().LB().Get(ctx, lbGetReq)
if err != nil {
return diag.FromErr(err)
}
d.Set("extnet_id", lb.ExtNetID)
d.Set("lb_ip", lb.PrimaryNode.FrontendIP)
}
kubeconfigReq := k8s.GetConfigRequest{
K8SID: cluster.ID,
}
kubeconfig, err := c.CloudAPI().K8S().GetConfig(ctx, kubeconfigReq)
if err != nil {
log.Warnf("could not get kubeconfig: %v", err)
}
d.Set("kubeconfig", kubeconfig)
return nil
}
func resourceK8sUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sUpdate: called with id %s, rg %d", d.Id(), d.Get("rg_id").(int))
c := m.(*controller.ControllerCfg)
haveRGID, err := existRGID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveRGID {
return diag.Errorf("resourceK8sUpdate: can't update k8s cluster because RGID %d is not allowed or does not exist", d.Get("rg_id").(int))
}
haveK8sciID, err := existK8sCIID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveK8sciID {
return diag.Errorf("resourceK8sUpdate: can't update k8s cluster because K8sCIID %d is not allowed or does not exist", d.Get("k8sci_id").(int))
}
cluster, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
hasChanged := false
switch cluster.Status {
case status.Modeled:
return diag.Errorf("The k8s cluster is in status: %s, please, contact support for more information", cluster.Status)
case status.Creating:
case status.Created:
case status.Deleting:
case status.Deleted:
id, _ := strconv.ParseUint(d.Id(), 10, 64)
restoreReq := k8s.RestoreRequest{
K8SID: id,
}
_, err := c.CloudAPI().K8S().Restore(ctx, restoreReq)
if err != nil {
return diag.FromErr(err)
}
enableReq := k8s.DisableEnableRequest{
K8SID: id,
}
_, err = c.CloudAPI().K8S().Enable(ctx, enableReq)
if err != nil {
return diag.FromErr(err)
}
hasChanged = true
case status.Destroying:
return diag.Errorf("The k8s cluster is in progress with status: %s", cluster.Status)
case status.Destroyed:
d.SetId("")
return diag.Errorf("The resource cannot be updated because it has been destroyed")
// return resourceK8sCreate(ctx, d, m)
case status.Enabling:
case status.Enabled:
case status.Disabling:
case status.Disabled:
log.Debugf("The k8s cluster is in status: %s, troubles may occur with update. Please, enable compute first.", cluster.Status)
case status.Restoring:
}
if hasChanged {
cluster, err = utilityK8sCheckPresence(ctx, d, m)
if cluster == nil {
d.SetId("")
if err != nil {
return diag.FromErr(err)
}
return nil
}
}
updateReq := k8s.UpdateRequest{K8SID: cluster.ID}
doBasicUpdate := false
if d.HasChange("name") {
updateReq.Name = d.Get("name").(string)
doBasicUpdate = true
}
if d.HasChange("desc") {
updateReq.Description = d.Get("desc").(string)
doBasicUpdate = true
}
if doBasicUpdate {
_, err := c.CloudAPI().K8S().Update(ctx, updateReq)
if err != nil {
return diag.FromErr(err)
}
}
if d.HasChange("workers") {
err := handleWorkersChange(ctx, d, c, cluster)
if err != nil {
return diag.FromErr(err)
}
}
if d.HasChange("zone_id") {
id, _ := strconv.ParseUint(d.Id(), 10, 64)
zoneID := uint64(d.Get("zone_id").(int))
start := d.Get("start").(bool)
if start {
stopReq := k8s.StopRequest{
K8SID: id,
}
_, err := c.CloudAPI().K8S().Stop(ctx, stopReq)
if err != nil {
return diag.FromErr(err)
}
}
req := k8s.MigrateToZoneRequest{
K8SID: id,
ZoneID: zoneID,
}
_, err := c.CloudAPI().K8S().MigrateToZone(ctx, req)
if err != nil {
return diag.FromErr(err)
}
if start {
startReq := k8s.StartRequest{
K8SID: id,
}
_, err = c.CloudAPI().K8S().Start(ctx, startReq)
if err != nil {
return diag.FromErr(err)
}
}
}
if d.HasChange("start") {
if d.Get("start").(bool) {
if cluster.TechStatus == "STOPPED" {
req := k8s.StartRequest{
K8SID: cluster.ID,
}
_, err := c.CloudAPI().K8S().Start(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
} else {
if cluster.TechStatus == "STARTED" {
req := k8s.StopRequest{
K8SID: cluster.ID,
}
_, err := c.CloudAPI().K8S().Stop(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
}
}
if d.HasChange("lb_sysctl_params") && d.Get("with_lb").(bool) {
lbSysctlParams := d.Get("lb_sysctl_params").([]interface{})
res := make([]map[string]interface{}, 0, len(lbSysctlParams))
for _, syscrlMap := range lbSysctlParams {
tempMap := make(map[string]interface{})
for k, v := range syscrlMap.(map[string]interface{}) {
if intVal, err := strconv.Atoi(v.(string)); err == nil {
tempMap[k] = intVal
continue
}
tempMap[k] = v.(string)
}
res = append(res, tempMap)
}
if len(res) > 0 {
req := lb.UpdateSysctParamsRequest{
LBID: cluster.LBID,
SysctlParams: res,
}
_, err := c.CloudAPI().LB().UpdateSysctlParams(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
}
return resourceK8sRead(ctx, d, m)
}
func resourceK8sDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sDelete: called with id %s, rg %d", d.Id(), d.Get("rg_id").(int))
cluster, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
req := k8s.DeleteRequest{K8SID: cluster.ID}
if val, ok := d.GetOk("permanently"); ok {
req.Permanently = val.(bool)
}
c := m.(*controller.ControllerCfg)
_, err = c.CloudAPI().K8S().Delete(ctx, req)
if err != nil {
return diag.FromErr(err)
}
return nil
}
func resourceK8sSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of the cluster.",
},
"rg_id": {
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
Description: "Resource group ID that this instance belongs to.",
},
"k8sci_id": {
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
Description: "ID of the k8s catalog item to base this instance on.",
},
"wg_name": {
Type: schema.TypeString,
Required: true,
Description: "Name for first worker group created with cluster.",
},
"network_plugin": {
Type: schema.TypeString,
Required: true,
Description: "Network plugin to be used",
ValidateFunc: validation.StringInSlice([]string{"flannel", "weavenet", "calico"}, true),
},
"storage_policy_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the storage policy",
},
"zone_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "ID of the zone to put the cluster into.",
},
"labels": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"taints": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"annotations": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"masters": {
Type: schema.TypeList,
Optional: true,
Computed: true,
//ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: mastersSchemaMake(),
},
Description: "Master node(s) configuration.",
},
"workers": {
Type: schema.TypeList,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: workersSchemaMake(),
},
Description: "Worker node(s) configuration.",
},
"with_lb": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Create k8s with load balancer if true.",
},
"extnet_only": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Use only selected ExtNet for infrastructure connections",
},
"extnet_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
//ForceNew: true,
Description: "ID of the external network to connect workers to. If omitted network will be chosen by the platfom.",
},
"permanently": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Determines if cluster should be destroyed",
},
///4.4.0
"cloud_init": {
Type: schema.TypeString,
Optional: true,
Description: "Meta data for working group computes, format YAML 'user_data': 1111",
},
"join_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings for joining a node to a cluster. It includes parameters such as the cluster's control plane endpoint, token, and certificate key. insert a valid JSON string with all levels of nesting.",
},
"kube_proxy_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings of the Kube-proxy, which is responsible for network proxying and load balancing within the cluster. It includes parameters such as proxy mode, cluster IP ranges, and other Kube-proxy specific configurations. insert a valid JSON string with all levels of nesting.",
},
"kubelet_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings of the Kubelet, which is the primary node agent that runs on each node in the cluster. It includes parameters such as node IP address, resource allocation, pod eviction policies, and other Kubelet-specific configurations. insert a valid JSON string with all levels of nesting.",
},
"cluster_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to define global settings and configurations for the entire cluster. It includes parameters such as cluster name, DNS settings, authentication methods, and other cluster-wide configurations. insert a valid JSON string with all levels of nesting.",
},
"init_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to define settings and actions that should be performed before any other component in the cluster starts. It allows you to configure things like node registration, network setup, and other initialization tasks. insert a valid JSON string with all levels of nesting.",
},
"additional_sans": {
Type: schema.TypeList,
Optional: true,
Description: "Optional extra Subject Alternative Names (SANs) to use for the API Server serving certificate. Can be both IP addresses and DNS names",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"ha_mode": {
Type: schema.TypeBool,
Optional: true,
Description: "Use Highly Available schema for LB deploy",
},
"lb_sysctl_params": {
Type: schema.TypeList,
Optional: true,
Description: "Custom sysctl values for Load Balancer instance. Applied on boot.",
Elem: &schema.Schema{
Type: schema.TypeMap,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
"oidc_cert": {
Type: schema.TypeString,
Optional: true,
Description: "insert ssl certificate in x509 pem format",
},
"chipset": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"Q35", "i440fx"}, false),
Default: "Q35",
Description: "Type of the emulated system. Possible values: i440fx, Q35. Default: Q35",
},
"desc": {
Type: schema.TypeString,
Optional: true,
Description: "Text description of this instance.",
},
"start": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Start k8s cluster",
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: aclGroupSchemaMake(),
},
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"bservice_id": {
Type: schema.TypeInt,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"k8s_ci_name": {
Type: schema.TypeString,
Computed: true,
},
"lb_id": {
Type: schema.TypeInt,
Computed: true,
},
"lb_ip": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of default load balancer.",
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"default_wg_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of default workers group for this instace.",
},
"kubeconfig": {
Type: schema.TypeString,
Computed: true,
Description: "Kubeconfig for cluster access.",
},
"vins_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "ID of default vins for this instace.",
},
}
}
func ResourceK8s() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceK8sCreate,
ReadContext: resourceK8sRead,
UpdateContext: resourceK8sUpdate,
DeleteContext: resourceK8sDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout30m,
Read: &constants.Timeout600s,
Update: &constants.Timeout600s,
Delete: &constants.Timeout600s,
Default: &constants.Timeout600s,
},
Schema: resourceK8sSchemaMake(),
}
}

View File

@@ -0,0 +1,966 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
Tim Tkachev, <tvtkachev@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"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"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/lb"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/tasks"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/dc"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/status"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/validators"
)
func resourceK8sCPCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sControlPlaneCreate: called with name %s, rg %d", d.Get("name").(string), d.Get("rg_id").(int))
haveRGID, err := existRGID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveRGID {
return diag.Errorf("resourceK8sCreate: can't create k8s cluster because RGID %d is not allowed or does not exist", d.Get("rg_id").(int))
}
haveK8sciID, err := existK8sCIID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveK8sciID {
return diag.Errorf("resourceK8sCreate: can't create k8s cluster because K8sCIID %d is not allowed or does not exist", d.Get("k8sci_id").(int))
}
if _, ok := d.GetOk("extnet_id"); ok {
haveExtNetID, err := existExtNetID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveExtNetID {
return diag.Errorf("resourceK8sCreate: can't create k8s cluster because ExtNetID %d is not allowed or does not exist", d.Get("extnet_id").(int))
}
}
c := m.(*controller.ControllerCfg)
createReq := k8s.CreateRequest{}
createReq.Name = d.Get("name").(string)
createReq.RGID = uint64(d.Get("rg_id").(int))
createReq.K8SCIID = uint64(d.Get("k8sci_id").(int))
createReq.WorkerGroupName = "temp"
createReq.NetworkPlugin = d.Get("network_plugin").(string)
createReq.StoragePolicyID = uint64(d.Get("storage_policy_id").(int))
if zoneID, ok := d.GetOk("zone_id"); ok {
createReq.ZoneID = uint64(zoneID.(int))
}
if num, ok := d.GetOk("num"); ok {
createReq.MasterNum = uint(num.(int))
} else {
createReq.MasterNum = 1
}
if cpu, ok := d.GetOk("cpu"); ok {
createReq.MasterCPU = uint(cpu.(int))
} else {
createReq.MasterCPU = 2
}
if ram, ok := d.GetOk("ram"); ok {
createReq.MasterRAM = uint64(ram.(int))
} else {
createReq.MasterRAM = 2048
}
if disk, ok := d.GetOk("disk"); ok {
createReq.MasterDisk = uint(disk.(int))
} else {
createReq.MasterDisk = 0
}
if sepId, ok := d.GetOk("sep_id"); ok {
createReq.MasterSEPID = uint64(sepId.(int))
}
if chipset, ok := d.GetOk("chipset"); ok {
createReq.Chipset = chipset.(string)
}
if sepPool, ok := d.GetOk("sep_pool"); ok {
createReq.MasterSEPPool = sepPool.(string)
}
createReq.WithLB = d.Get("with_lb").(bool)
///4.4.0
createReq.HighlyAvailable = d.Get("ha_mode").(bool)
if additionalSans, ok := d.GetOk("additional_sans"); ok {
addSans := additionalSans.([]interface{})
resSans := make([]string, 0)
for _, san := range addSans {
resSans = append(resSans, san.(string))
}
createReq.AdditionalSANs = resSans
}
if clusterConfig, ok := d.GetOk("cluster_config"); ok {
createReq.ClusterConfiguration = clusterConfig.(string)
}
if kubeletConfig, ok := d.GetOk("kubelet_config"); ok {
createReq.KubeletConfiguration = kubeletConfig.(string)
}
if kubeProxyConfig, ok := d.GetOk("kube_proxy_config"); ok {
createReq.KubeProxyConfiguration = kubeProxyConfig.(string)
}
if joinConfig, ok := d.GetOk("join_config"); ok {
createReq.JoinConfiguration = joinConfig.(string)
}
// if cloudInit, ok := d.GetOk("cloud_init"); ok {
// createReq.UserData = cloudInit.(string)
// }
if initConfig, ok := d.GetOk("init_config"); ok {
createReq.InitConfiguration = initConfig.(string)
}
if lbSysctlParams, ok := d.GetOk("lb_sysctl_params"); ok {
syscrlSliceMaps := lbSysctlParams.([]interface{})
res := make([]map[string]interface{}, 0, len(syscrlSliceMaps))
for _, syscrlMap := range syscrlSliceMaps {
tempMap := make(map[string]interface{})
for k, v := range syscrlMap.(map[string]interface{}) {
if intVal, err := strconv.Atoi(v.(string)); err == nil {
tempMap[k] = intVal
continue
}
tempMap[k] = v.(string)
}
res = append(res, tempMap)
}
createReq.LbSysctlParams = res
}
if oidcCertificate, ok := d.GetOk("oidc_cert"); ok {
createReq.OidcCertificate = oidcCertificate.(string)
log.Debug(createReq.OidcCertificate)
}
///
createReq.ExtNetOnly = d.Get("extnet_only").(bool)
if extNet, ok := d.GetOk("extnet_id"); ok {
createReq.ExtNetID = uint64(extNet.(int))
} else {
createReq.ExtNetID = 0
}
if vins, ok := d.GetOk("vins_id"); ok {
createReq.VinsId = uint64(vins.(int))
} else {
createReq.VinsId = 0
}
if desc, ok := d.GetOk("desc"); ok {
createReq.Description = desc.(string)
}
resp, err := c.CloudAPI().K8S().Create(ctx, createReq)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
taskReq := tasks.GetRequest{
AuditID: strings.Trim(resp, `"`),
}
for {
task, err := c.CloudAPI().Tasks().Get(ctx, taskReq)
if err != nil {
return diag.FromErr(err)
}
log.Debugf("resourceK8sControlPlaneCreate: instance creating - %s", task.Stage)
if task.Completed {
if task.Error != "" {
return diag.FromErr(fmt.Errorf("cannot create k8s instance: %v", task.Error))
}
id, err := task.Result.ID()
if err != nil {
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(id))
break
}
time.Sleep(time.Second * 20)
}
cluster, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
delWGReq := k8s.WorkersGroupDeleteRequest{
K8SID: cluster.ID,
WorkersGroupID: cluster.K8SGroups.Workers[0].ID,
}
_, err = c.CloudAPI().K8S().WorkersGroupDelete(ctx, delWGReq)
if err != nil {
return diag.FromErr(err)
}
return resourceK8sCPRead(ctx, d, m)
}
func resourceK8sCPRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
cluster, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
c := m.(*controller.ControllerCfg)
hasChanged := false
switch cluster.Status {
case status.Modeled:
return diag.Errorf("The k8s cluster is in status: %s, please, contact support for more information", cluster.Status)
case status.Creating:
case status.Created:
case status.Deleting:
case status.Deleted:
// id, _ := strconv.ParseUint(d.Id(), 10, 64)
// restoreReq := k8s.RestoreRequest{
// K8SID: id,
// }
// _, err := c.CloudAPI().K8S().Restore(ctx, restoreReq)
// if err != nil {
// return diag.FromErr(err)
// }
// enableReq := k8s.DisableEnableRequest{
// K8SID: id,
// }
// _, err = c.CloudAPI().K8S().Enable(ctx, enableReq)
// if err != nil {
// return diag.FromErr(err)
// }
// hasChanged = true
case status.Destroying:
return diag.Errorf("The k8s cluster is in progress with status: %s", cluster.Status)
case status.Destroyed:
d.SetId("")
return diag.Errorf("The resource cannot be updated because it has been destroyed")
// return resourceK8sCreate(ctx, d, m)
case status.Enabling:
case status.Enabled:
case status.Disabling:
case status.Disabled:
log.Debugf("The k8s cluster is in status: %s, troubles may occur with update. Please, enable compute first.", cluster.Status)
case status.Restoring:
}
if hasChanged {
cluster, err = utilityK8sCheckPresence(ctx, d, m)
if cluster == nil {
d.SetId("")
if err != nil {
return diag.FromErr(err)
}
return nil
}
}
if d.Get("start").(bool) {
if cluster.TechStatus == "STOPPED" {
req := k8s.StartRequest{
K8SID: cluster.ID,
}
_, err := c.CloudAPI().K8S().Start(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
}
k8sList, err := utilityK8sListForResourceCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
curK8s := k8s.ItemK8SCluster{}
for _, k8sCluster := range k8sList.Data {
if k8sCluster.ID == cluster.ID {
curK8s = k8sCluster
}
}
if curK8s.ID == 0 {
return diag.Errorf("Cluster with id %d not found", cluster.ID)
}
d.Set("vins_id", curK8s.VINSID)
masterComputeList := make([]compute.RecordCompute, 0, len(cluster.K8SGroups.Masters.DetailedInfo))
for _, masterNode := range cluster.K8SGroups.Masters.DetailedInfo {
compute, err := utilityComputeCheckPresence(ctx, d, m, masterNode.ID)
if err != nil {
return diag.FromErr(err)
}
masterComputeList = append(masterComputeList, *compute)
}
var warnings dc.Warnings
if _, ok := d.GetOk("k8s_id"); !ok {
for _, worker := range cluster.K8SGroups.Workers {
err := fmt.Errorf("found worker-group with ID %d. Make sure to import it to decort_k8s_wg resource if you wish to manage it", worker.ID)
warnings.Add(err)
}
}
flattenResourceK8sCP(d, *cluster, masterComputeList)
lbGetReq := lb.GetRequest{
LBID: cluster.LBID,
}
if d.Get("with_lb").(bool) || cluster.LBID != 0 {
lb, err := c.CloudAPI().LB().Get(ctx, lbGetReq)
if err != nil {
return diag.FromErr(err)
}
d.Set("extnet_id", lb.ExtNetID)
d.Set("lb_ip", lb.PrimaryNode.FrontendIP)
}
kubeconfigReq := k8s.GetConfigRequest{
K8SID: cluster.ID,
}
kubeconfig, err := c.CloudAPI().K8S().GetConfig(ctx, kubeconfigReq)
if err != nil {
log.Warnf("could not get kubeconfig: %v", err)
}
d.Set("kubeconfig", kubeconfig)
return warnings.Get()
}
func resourceK8sCPUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sControlPlaneUpdate: called with id %s, rg %d", d.Id(), d.Get("rg_id").(int))
c := m.(*controller.ControllerCfg)
haveRGID, err := existRGID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveRGID {
return diag.Errorf("resourceK8sUpdate: can't update k8s cluster because RGID %d is not allowed or does not exist", d.Get("rg_id").(int))
}
haveK8sciID, err := existK8sCIID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveK8sciID {
return diag.Errorf("resourceK8sUpdate: can't update k8s cluster because K8sCIID %d is not allowed or does not exist", d.Get("k8sci_id").(int))
}
cluster, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
hasChanged := false
switch cluster.Status {
case status.Modeled:
return diag.Errorf("The k8s cluster is in status: %s, please, contact support for more information", cluster.Status)
case status.Creating:
case status.Created:
case status.Deleting:
case status.Deleted:
id, _ := strconv.ParseUint(d.Id(), 10, 64)
restoreReq := k8s.RestoreRequest{
K8SID: id,
}
_, err := c.CloudAPI().K8S().Restore(ctx, restoreReq)
if err != nil {
return diag.FromErr(err)
}
enableReq := k8s.DisableEnableRequest{
K8SID: id,
}
_, err = c.CloudAPI().K8S().Enable(ctx, enableReq)
if err != nil {
return diag.FromErr(err)
}
hasChanged = true
case status.Destroying:
return diag.Errorf("The k8s cluster is in progress with status: %s", cluster.Status)
case status.Destroyed:
d.SetId("")
return diag.Errorf("The resource cannot be updated because it has been destroyed")
// return resourceK8sCreate(ctx, d, m)
case status.Enabling:
case status.Enabled:
case status.Disabling:
case status.Disabled:
log.Debugf("The k8s cluster is in status: %s, troubles may occur with update. Please, enable compute first.", cluster.Status)
case status.Restoring:
}
if hasChanged {
cluster, err = utilityK8sCheckPresence(ctx, d, m)
if cluster == nil {
d.SetId("")
if err != nil {
return diag.FromErr(err)
}
return nil
}
}
updateReq := k8s.UpdateRequest{K8SID: cluster.ID}
doBasicUpdate := false
if d.HasChange("name") {
updateReq.Name = d.Get("name").(string)
doBasicUpdate = true
}
if d.HasChange("desc") {
updateReq.Description = d.Get("desc").(string)
doBasicUpdate = true
}
if doBasicUpdate {
_, err := c.CloudAPI().K8S().Update(ctx, updateReq)
if err != nil {
return diag.FromErr(err)
}
}
if d.HasChange("start") {
if d.Get("start").(bool) {
if cluster.TechStatus == "STOPPED" {
req := k8s.StartRequest{
K8SID: cluster.ID,
}
_, err := c.CloudAPI().K8S().Start(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
} else {
if cluster.TechStatus == "STARTED" {
req := k8s.StopRequest{
K8SID: cluster.ID,
}
_, err := c.CloudAPI().K8S().Stop(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
}
}
if d.HasChange("zone_id") {
id, _ := strconv.ParseUint(d.Id(), 10, 64)
zoneID := uint64(d.Get("zone_id").(int))
start := d.Get("start").(bool)
if start {
stopReq := k8s.StopRequest{
K8SID: id,
}
_, err := c.CloudAPI().K8S().Stop(ctx, stopReq)
if err != nil {
return diag.FromErr(err)
}
}
req := k8s.MigrateToZoneRequest{
K8SID: id,
ZoneID: zoneID,
}
_, err := c.CloudAPI().K8S().MigrateToZone(ctx, req)
if err != nil {
return diag.FromErr(err)
}
if start {
startReq := k8s.StartRequest{
K8SID: id,
}
_, err := c.CloudAPI().K8S().Start(ctx, startReq)
if err != nil {
return diag.FromErr(err)
}
}
}
if d.HasChange("num") {
oldVal, newVal := d.GetChange("num")
if oldVal.(int) > newVal.(int) {
ids := make([]uint64, 0)
for i := oldVal.(int) - 1; i >= newVal.(int); i-- {
id := cluster.K8SGroups.Masters.DetailedInfo[i].ID
ids = append(ids, id)
}
req := k8s.DeleteMasterFromGroupRequest{
K8SID: cluster.ID,
MasterGroupID: cluster.K8SGroups.Masters.ID,
MasterIDs: ids,
}
_, err := c.CloudAPI().K8S().DeleteMasterFromGroup(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
}
if d.HasChange("lb_sysctl_params") && d.Get("with_lb").(bool) {
lbSysctlParams := d.Get("lb_sysctl_params").([]interface{})
res := make([]map[string]interface{}, 0, len(lbSysctlParams))
for _, syscrlMap := range lbSysctlParams {
tempMap := make(map[string]interface{})
for k, v := range syscrlMap.(map[string]interface{}) {
if intVal, err := strconv.Atoi(v.(string)); err == nil {
tempMap[k] = intVal
continue
}
tempMap[k] = v.(string)
}
res = append(res, tempMap)
}
if len(res) > 0 {
req := lb.UpdateSysctParamsRequest{
LBID: cluster.LBID,
SysctlParams: res,
}
_, err := c.CloudAPI().LB().UpdateSysctlParams(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
}
return resourceK8sCPRead(ctx, d, m)
}
func resourceK8sCPDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sControlPlaneDelete: called with id %s, rg %d", d.Id(), d.Get("rg_id").(int))
cluster, err := utilityK8sCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
req := k8s.DeleteRequest{K8SID: cluster.ID}
if val, ok := d.GetOk("permanently"); ok {
req.Permanently = val.(bool)
}
c := m.(*controller.ControllerCfg)
_, err = c.CloudAPI().K8S().Delete(ctx, req)
if err != nil {
return diag.FromErr(err)
}
d.SetId("")
return nil
}
func resourceK8sCPSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Name of the cluster.",
},
"rg_id": {
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
Description: "Resource group ID that this instance belongs to.",
},
"k8sci_id": {
Type: schema.TypeInt,
Required: true,
//ForceNew: true,
Description: "ID of the k8s catalog item to base this instance on.",
},
"network_plugin": {
Type: schema.TypeString,
Required: true,
Description: "Network plugin to be used",
ValidateFunc: validation.StringInSlice([]string{"flannel", "weavenet", "calico"}, true),
},
"storage_policy_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the storage policy",
},
"zone_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "ID of the zone to put the cluster into.",
},
"num": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validation.IntInSlice([]int{1, 3, 5}),
Description: "Number of VMs to create. Can be either 1, 3 or 5",
},
"cpu": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "Node CPU count.",
},
"ram": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validation.All(
validation.IntAtLeast(constants.MIN_RAM_PER_COMPUTE),
validators.DivisibleBy(constants.RAM_DIVISIBILITY),
),
Description: "Node RAM in MB.",
},
"chipset": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"Q35", "i440fx"}, false),
Default: "Q35",
Description: "Type of the emulated system. Possible values: i440fx, Q35. Default: Q35",
},
"disk": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "Node boot disk size in GB.",
},
"sep_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "Storage Endpoint ID",
},
"sep_pool": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Storage Endpoint Pool",
},
"with_lb": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Create k8s with load balancer if true.",
},
"extnet_only": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Use only selected ExtNet for infrastructure connections",
},
"join_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings for joining a node to a cluster. It includes parameters such as the cluster's control plane endpoint, token, and certificate key. insert a valid JSON string with all levels of nesting.",
},
"kube_proxy_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings of the Kube-proxy, which is responsible for network proxying and load balancing within the cluster. It includes parameters such as proxy mode, cluster IP ranges, and other Kube-proxy specific configurations. insert a valid JSON string with all levels of nesting.",
},
"kubelet_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to configure the behavior and settings of the Kubelet, which is the primary node agent that runs on each node in the cluster. It includes parameters such as node IP address, resource allocation, pod eviction policies, and other Kubelet-specific configurations. insert a valid JSON string with all levels of nesting.",
},
"cluster_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to define global settings and configurations for the entire cluster. It includes parameters such as cluster name, DNS settings, authentication methods, and other cluster-wide configurations. insert a valid JSON string with all levels of nesting.",
},
"init_config": {
Type: schema.TypeString,
Optional: true,
Description: "is used to define settings and actions that should be performed before any other component in the cluster starts. It allows you to configure things like node registration, network setup, and other initialization tasks. insert a valid JSON string with all levels of nesting.",
},
"additional_sans": {
Type: schema.TypeList,
Optional: true,
Description: "Optional extra Subject Alternative Names (SANs) to use for the API Server serving certificate. Can be both IP addresses and DNS names",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"ha_mode": {
Type: schema.TypeBool,
Optional: true,
Description: "Use Highly Available schema for LB deploy",
},
"lb_sysctl_params": {
Type: schema.TypeList,
Optional: true,
Description: "Custom sysctl values for Load Balancer instance. Applied on boot.",
Elem: &schema.Schema{
Type: schema.TypeMap,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
"oidc_cert": {
Type: schema.TypeString,
Optional: true,
Description: "insert ssl certificate in x509 pem format",
},
"extnet_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
//ForceNew: true,
Description: "ID of the external network to connect workers to. If omitted network will be chosen by the platfom.",
},
"desc": {
Type: schema.TypeString,
Optional: true,
Description: "Text description of this instance.",
},
"start": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Start k8s cluster.",
},
"detailed_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: detailedInfoSchemaMake(),
},
},
"master_id": {
Type: schema.TypeInt,
Computed: true,
Description: "Master group ID.",
},
"master_name": {
Type: schema.TypeString,
Computed: true,
Description: "Master group name.",
},
"permanently": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Determines if cluster should be destroyed",
},
"acl": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: aclGroupSchemaMake(),
},
},
"account_id": {
Type: schema.TypeInt,
Computed: true,
},
"account_name": {
Type: schema.TypeString,
Computed: true,
},
"bservice_id": {
Type: schema.TypeInt,
Computed: true,
},
"created_by": {
Type: schema.TypeString,
Computed: true,
},
"created_time": {
Type: schema.TypeInt,
Computed: true,
},
"deleted_by": {
Type: schema.TypeString,
Computed: true,
},
"deleted_time": {
Type: schema.TypeInt,
Computed: true,
},
"k8s_ci_name": {
Type: schema.TypeString,
Computed: true,
},
"lb_id": {
Type: schema.TypeInt,
Computed: true,
},
"k8s_id": {
Type: schema.TypeInt,
Computed: true,
},
"lb_ip": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of default load balancer.",
},
"rg_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"tech_status": {
Type: schema.TypeString,
Computed: true,
},
"updated_by": {
Type: schema.TypeString,
Computed: true,
},
"updated_time": {
Type: schema.TypeInt,
Computed: true,
},
"kubeconfig": {
Type: schema.TypeString,
Computed: true,
Description: "Kubeconfig for cluster access.",
},
"vins_id": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "ID of default vins for this instace.",
},
}
}
func ResourceK8sCP() *schema.Resource {
return &schema.Resource{
SchemaVersion: 2,
CreateContext: resourceK8sCPCreate,
ReadContext: resourceK8sCPRead,
UpdateContext: resourceK8sCPUpdate,
DeleteContext: resourceK8sCPDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout30m,
Read: &constants.Timeout600s,
Update: &constants.Timeout600s,
Delete: &constants.Timeout600s,
Default: &constants.Timeout600s,
},
Schema: resourceK8sCPSchemaMake(),
StateUpgraders: []schema.StateUpgrader{
{
Type: resourceK8sCPSchemaV1().CoreConfigSchema().ImpliedType(),
Upgrade: resourceK8sCPStateUpgradeV1,
Version: 1,
},
},
}
}

View File

@@ -0,0 +1,406 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"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"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/tasks"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/validators"
)
func resourceK8sWgCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sWgCreate: called with k8s id %d", d.Get("k8s_id").(int))
haveK8sID, err := existK8sID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveK8sID {
return diag.Errorf("resourceK8sWgCreate: can't create k8s cluster because K8sID %d is not allowed or does not exist", d.Get("k8s_id").(int))
}
c := m.(*controller.ControllerCfg)
req := k8s.WorkersGroupAddRequest{
K8SID: uint64(d.Get("k8s_id").(int)),
Name: d.Get("name").(string),
WorkerNum: uint64(d.Get("num").(int)),
WorkerCPU: uint64(d.Get("cpu").(int)),
WorkerRAM: uint64(d.Get("ram").(int)),
WorkerSEPID: uint64(d.Get("worker_sep_id").(int)),
WorkerSEPPool: d.Get("worker_sep_pool").(string),
Chipset: d.Get("chipset").(string),
StoragePolicyID: uint64(d.Get("storage_policy_id").(int)),
}
labels, _ := d.Get("labels").([]interface{})
for _, label := range labels {
if !strings.HasPrefix(label.(string), "workersGroupName") {
req.Labels = append(req.Labels, label.(string))
}
}
annotations, _ := d.Get("annotations").([]interface{})
for _, annotation := range annotations {
req.Annotations = append(req.Annotations, annotation.(string))
}
taints, _ := d.Get("taints").([]interface{})
for _, taint := range taints {
req.Taints = append(req.Taints, taint.(string))
}
if d.Get("disk") == nil {
req.WorkerDisk = 0
} else {
req.WorkerDisk = uint64(d.Get("disk").(int))
}
if cloudInit, ok := d.GetOk("cloud_init"); ok {
req.UserData = cloudInit.(string)
}
resp, err := c.CloudAPI().K8S().WorkersGroupAdd(ctx, req)
if err != nil {
return diag.FromErr(err)
}
taskReq := tasks.GetRequest{
AuditID: strings.Trim(resp, `"`),
}
for {
task, err := c.CloudAPI().Tasks().Get(ctx, taskReq)
if err != nil {
return diag.FromErr(err)
}
log.Debugf("resourceK8sWgCreate: instance creating - %s", task.Stage)
if task.Completed {
if task.Error != "" {
return diag.FromErr(fmt.Errorf("cannot create k8s wg instance: %v", task.Error))
}
id, err := task.Result.ID()
if err != nil {
return diag.FromErr(err)
}
d.SetId(fmt.Sprint(d.Get("k8s_id").(int), "#", strconv.Itoa(id)))
break
}
time.Sleep(time.Second * 20)
}
return resourceK8sWgRead(ctx, d, m)
}
func resourceK8sWgRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sWgRead: called with %v", d.Id())
wg, err := utilityK8sWgCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
workersComputeList := make([]compute.RecordCompute, 0)
for _, info := range wg.DetailedInfo {
compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID)
if err != nil {
return diag.FromErr(err)
}
workersComputeList = append(workersComputeList, *compute)
}
d.Set("wg_id", wg.ID)
if strings.Contains(d.Id(), "#") {
k8sId, err := strconv.Atoi(strings.Split(d.Id(), "#")[0])
if err != nil {
return diag.FromErr(err)
}
d.Set("k8s_id", k8sId)
} else {
d.Set("k8s_id", d.Get("k8s_id"))
}
d.SetId(fmt.Sprintf("%d#%d", d.Get("k8s_id").(int), wg.ID))
flattenWg(d, *wg, workersComputeList)
return nil
}
func resourceK8sWgUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sWgUpdate: called with k8s id %d", d.Get("k8s_id").(int))
c := m.(*controller.ControllerCfg)
haveK8sID, err := existK8sID(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if !haveK8sID {
return diag.Errorf("resourceK8sUpdate: can't update k8s cluster because K8sID %d is not allowed or does not exist", d.Get("k8s_id").(int))
}
wg, err := utilityK8sWgCheckPresence(ctx, d, m)
if err != nil {
return diag.FromErr(err)
}
if newNum := d.Get("num").(int); uint64(newNum) > wg.Num {
req := k8s.WorkerAddRequest{
K8SID: uint64(d.Get("k8s_id").(int)),
WorkersGroupID: wg.ID,
Num: uint64(newNum) - wg.Num,
Chipset: d.Get("chipset").(string),
}
_, err := c.CloudAPI().K8S().WorkerAdd(ctx, req)
if err != nil {
return diag.FromErr(err)
}
} else {
for i := int(wg.Num) - 1; i >= newNum; i-- {
req := k8s.DeleteWorkerFromGroupRequest{
K8SID: uint64(d.Get("k8s_id").(int)),
WorkersGroupID: wg.ID,
WorkerID: wg.DetailedInfo[i].ID,
}
_, err := c.CloudAPI().K8S().DeleteWorkerFromGroup(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
}
if d.HasChange("cloud_init") {
req := k8s.UpdateWorkerNodesMetaDataRequest{
K8SID: uint64(d.Get("k8s_id").(int)),
WorkersGroupID: wg.ID,
UserData: d.Get("cloud_init").(string),
}
_, err := c.CloudAPI().K8S().UpdateWorkerNodesMetaData(ctx, req)
if err != nil {
return diag.FromErr(err)
}
}
return resourceK8sWgRead(ctx, d, m)
}
func resourceK8sWgDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
log.Debugf("resourceK8sWgDelete: called with k8s id %d", d.Get("k8s_id").(int))
wg, err := utilityK8sWgCheckPresence(ctx, d, m)
if err != nil {
d.SetId("")
return diag.FromErr(err)
}
c := m.(*controller.ControllerCfg)
req := k8s.WorkersGroupDeleteRequest{
K8SID: uint64(d.Get("k8s_id").(int)),
WorkersGroupID: wg.ID,
}
_, err = c.CloudAPI().K8S().WorkersGroupDelete(ctx, req)
if err != nil {
return diag.FromErr(err)
}
d.SetId("")
return 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.",
},
"name": {
Type: schema.TypeString,
Required: true,
//ForceNew: true,
Description: "Name of the worker group.",
},
"storage_policy_id": {
Type: schema.TypeInt,
Required: true,
Description: "ID of the storage policy",
},
"num": {
Type: schema.TypeInt,
Optional: true,
Default: 1,
Description: "Number of worker nodes to create.",
},
"chipset": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"Q35", "i440fx"}, false),
Default: "Q35",
Description: "Type of the emulated system. Possible values: i440fx, Q35. Default: Q35",
},
"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,
ValidateFunc: validation.All(
validation.IntAtLeast(constants.MIN_RAM_PER_COMPUTE),
validators.DivisibleBy(constants.RAM_DIVISIBILITY),
),
Description: "Worker node RAM in MB.",
},
"disk": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "Worker node boot disk size. If unspecified or 0, size is defined by OS image size.",
},
"labels": {
Type: schema.TypeList,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"annotations": {
Type: schema.TypeList,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"taints": {
Type: schema.TypeList,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"worker_sep_id": {
Type: schema.TypeInt,
Optional: true,
},
"worker_sep_pool": {
Type: schema.TypeString,
Optional: true,
},
"cloud_init": {
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: cloudInitDiffSupperss,
},
"wg_id": {
Type: schema.TypeInt,
Computed: true,
Description: "ID of k8s worker Group.",
},
"detailed_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: detailedInfoSchemaMake(),
},
},
"guid": {
Type: schema.TypeString,
Computed: true,
},
}
}
func ResourceK8sWg() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
CreateContext: resourceK8sWgCreate,
ReadContext: resourceK8sWgRead,
UpdateContext: resourceK8sWgUpdate,
DeleteContext: resourceK8sWgDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Timeouts: &schema.ResourceTimeout{
Create: &constants.Timeout20m,
Read: &constants.Timeout20m,
Update: &constants.Timeout20m,
Delete: &constants.Timeout20m,
Default: &constants.Timeout20m,
},
Schema: resourceK8sWgSchemaMake(),
}
}

View File

@@ -0,0 +1,15 @@
package k8s
import (
"context"
log "github.com/sirupsen/logrus"
)
func resourceK8sCPStateUpgradeV1(ctx context.Context, rawState map[string]interface{}, meta any) (map[string]interface{}, error) {
log.Debug("resourceK8sCPStateUpgradeV1: upgrading state")
delete(rawState, "cloud_init")
return rawState, nil
}

View File

@@ -0,0 +1,80 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
Tim Tkachev, <tvtkachev@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8ci"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
)
func utilityK8CIListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*k8ci.ListK8CI, error) {
c := m.(*controller.ControllerCfg)
req := k8ci.ListRequest{}
if by_id, ok := d.GetOk("by_id"); ok {
req.ByID = uint64(by_id.(int))
}
if name, ok := d.GetOk("name"); ok {
req.Name = name.(string)
}
if status, ok := d.GetOk("status"); ok {
req.Status = status.(string)
}
if network_plugin, ok := d.GetOk("network_plugin"); ok {
req.NetworkPlugins = network_plugin.(string)
}
if include_disabled, ok := d.GetOk("include_disabled"); ok {
req.IncludeDisabled = include_disabled.(bool)
}
if sortBy, ok := d.GetOk("sort_by"); ok {
req.SortBy = sortBy.(string)
}
if page, ok := d.GetOk("page"); ok {
req.Page = uint64(page.(int))
}
if size, ok := d.GetOk("size"); ok {
req.Size = uint64(size.(int))
}
resList, err := c.CloudAPI().K8CI().List(ctx, req)
if err != nil {
return nil, err
}
return resList, nil
}

View File

@@ -0,0 +1,385 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
)
func handleWorkersChange(ctx context.Context, d *schema.ResourceData, c *controller.ControllerCfg, cluster *k8s.RecordK8S) error {
o, n := d.GetChange("workers")
old_set, _ := o.([]interface{})
new_set, _ := n.([]interface{})
old_len := len(old_set)
new_len := len(new_set)
if old_len > new_len {
deleted := workersDifference(old_set, new_set)
if err := deleteWGs(ctx, c, cluster, deleted); err != nil {
return err
}
} else if old_len < new_len {
added := workersDifference(old_set, new_set)
if err := addWGs(ctx, c, cluster, added); err != nil {
return err
}
}
if err := updateNum(ctx, c, cluster, old_set, new_set); err != nil {
return err
}
return nil
}
func updateNum(ctx context.Context, c *controller.ControllerCfg, cluster *k8s.RecordK8S, old_set []interface{}, new_set []interface{}) error {
for _, valOld := range old_set {
wgOld, _ := valOld.(map[string]interface{})
for _, valNew := range new_set {
wgNew, _ := valNew.(map[string]interface{})
if wgOld["id"] == wgNew["id"] {
oldNum := wgOld["num"].(int)
newNum := wgNew["num"].(int)
if oldNum < newNum {
req := k8s.WorkerAddRequest{
K8SID: cluster.ID,
WorkersGroupID: uint64(wgNew["id"].(int)),
Num: uint64(newNum - oldNum),
Chipset: wgNew["chipset"].(string),
}
_, err := c.CloudAPI().K8S().WorkerAdd(ctx, req)
if err != nil {
return err
}
} else if oldNum > newNum {
for i := oldNum - 1; i >= newNum; i-- {
detailedInfo := wgOld["detailed_info"].([]interface{})
if len(detailedInfo) == 0 {
return nil
}
req := k8s.DeleteWorkerFromGroupRequest{
K8SID: cluster.ID,
WorkersGroupID: uint64(wgNew["id"].(int)),
WorkerID: uint64(detailedInfo[i].(map[string]interface{})["compute_id"].(int)),
}
_, err := c.CloudAPI().K8S().DeleteWorkerFromGroup(ctx, req)
if err != nil {
return err
}
}
}
}
}
}
return nil
}
func deleteWGs(ctx context.Context, c *controller.ControllerCfg, cluster *k8s.RecordK8S, deleted []interface{}) error {
for _, elem := range deleted {
found_wg := elem.(map[string]interface{})
req := k8s.WorkersGroupDeleteRequest{
K8SID: cluster.ID,
WorkersGroupID: uint64(found_wg["id"].(int)),
}
_, err := c.CloudAPI().K8S().WorkersGroupDelete(ctx, req)
if err != nil {
return err
}
}
return nil
}
func addWGs(ctx context.Context, c *controller.ControllerCfg, cluster *k8s.RecordK8S, added []interface{}) error {
for _, elem := range added {
found_wg := elem.(map[string]interface{})
req := k8s.WorkersGroupAddRequest{
K8SID: cluster.ID,
Name: found_wg["name"].(string),
WorkerSEPID: uint64(found_wg["sep_id"].(int)),
WorkerSEPPool: found_wg["sep_pool"].(string),
WorkerNum: uint64(found_wg["num"].(int)),
WorkerCPU: uint64(found_wg["cpu"].(int)),
WorkerRAM: uint64(found_wg["ram"].(int)),
WorkerDisk: uint64(found_wg["disk"].(int)),
Chipset: found_wg["chipset"].(string),
}
labels, _ := found_wg["labels"].([]interface{})
for _, label := range labels {
if !strings.HasPrefix(label.(string), "workersGroupName") {
req.Labels = append(req.Labels, label.(string))
}
}
annotations, _ := found_wg["annotations"].([]interface{})
for _, annotation := range annotations {
req.Annotations = append(req.Annotations, annotation.(string))
}
taints, _ := found_wg["taints"].([]interface{})
for _, taint := range taints {
req.Taints = append(req.Taints, taint.(string))
}
_, err := c.CloudAPI().K8S().WorkersGroupAdd(ctx, req)
if err != nil {
return err
}
}
return nil
}
func utilityK8sCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*k8s.RecordK8S, error) {
c := m.(*controller.ControllerCfg)
var k8sID uint64
if d.Id() != "" {
k8sID, _ = strconv.ParseUint(d.Id(), 10, 64)
} else {
k8sID = uint64(d.Get("k8s_id").(int))
}
req := k8s.GetRequest{
K8SID: k8sID,
}
k8s, err := c.CloudAPI().K8S().Get(ctx, req)
if err != nil {
return nil, err
}
return k8s, nil
}
func utilityComputeCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}, computeID uint64) (*compute.RecordCompute, error) {
c := m.(*controller.ControllerCfg)
req := compute.GetRequest{
ComputeID: computeID,
}
compute, err := c.CloudAPI().Compute().Get(ctx, req)
if err != nil {
return nil, err
}
return compute, nil
}
func utilityDataK8sCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*k8s.RecordK8S, error) {
c := m.(*controller.ControllerCfg)
req := k8s.GetRequest{
K8SID: uint64(d.Get("k8s_id").(int)),
}
k8s, err := c.CloudAPI().K8S().Get(ctx, req)
if err != nil {
return nil, err
}
return k8s, nil
}
func utilityK8sListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*k8s.ListK8SClusters, error) {
c := m.(*controller.ControllerCfg)
req := k8s.ListRequest{
IncludeDeleted: false,
}
if by_id, ok := d.GetOk("by_id"); ok {
req.ByID = uint64(by_id.(int))
}
if name, ok := d.GetOk("name"); ok {
req.Name = name.(string)
}
if ip_address, ok := d.GetOk("ip_address"); ok {
req.IPAddress = ip_address.(string)
}
if rg_id, ok := d.GetOk("rg_id"); ok {
req.RGID = uint64(rg_id.(int))
}
if lb_id, ok := d.GetOk("lb_id"); ok {
req.LBID = uint64(lb_id.(int))
}
if bservice_id, ok := d.GetOk("bservice_id"); ok {
req.BasicServiceID = uint64(bservice_id.(int))
}
if status, ok := d.GetOk("status"); ok {
req.Status = status.(string)
}
if tech_status, ok := d.GetOk("tech_status"); ok {
req.TechStatus = tech_status.(string)
}
if includedeleted, ok := d.GetOk("includedeleted"); ok {
req.IncludeDeleted = includedeleted.(bool)
}
if sortBy, ok := d.GetOk("sort_by"); ok {
req.SortBy = sortBy.(string)
}
if page, ok := d.GetOk("page"); ok {
req.Page = uint64(page.(int))
}
if size, ok := d.GetOk("size"); ok {
req.Size = uint64(size.(int))
}
if zoneID, ok := d.GetOk("zone_id"); ok {
req.ZoneID = uint64(zoneID.(int))
}
k8sList, err := c.CloudAPI().K8S().List(ctx, req)
if err != nil {
return nil, err
}
return k8sList, nil
}
func utilityK8sListForResourceCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*k8s.ListK8SClusters, error) {
c := m.(*controller.ControllerCfg)
req := k8s.ListRequest{
IncludeDeleted: false,
}
if name, ok := d.GetOk("name"); ok {
req.Name = name.(string)
}
k8sList, err := c.CloudAPI().K8S().List(ctx, req)
if err != nil {
return nil, err
}
return k8sList, nil
}
func utilityK8sListDeletedCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*k8s.ListK8SClusters, error) {
c := m.(*controller.ControllerCfg)
req := k8s.ListDeletedRequest{}
if by_id, ok := d.GetOk("by_id"); ok {
req.ByID = uint64(by_id.(int))
}
if name, ok := d.GetOk("name"); ok {
req.Name = name.(string)
}
if ip_address, ok := d.GetOk("ip_address"); ok {
req.IPAddress = ip_address.(string)
}
if rg_id, ok := d.GetOk("rg_id"); ok {
req.RGID = uint64(rg_id.(int))
}
if lb_id, ok := d.GetOk("lb_id"); ok {
req.LBID = uint64(lb_id.(int))
}
if bservice_id, ok := d.GetOk("bservice_id"); ok {
req.BasicServiceID = uint64(bservice_id.(int))
}
if tech_status, ok := d.GetOk("tech_status"); ok {
req.TechStatus = tech_status.(string)
}
if sortBy, ok := d.GetOk("sort_by"); ok {
req.SortBy = sortBy.(string)
}
if page, ok := d.GetOk("page"); ok {
req.Page = uint64(page.(int))
}
if size, ok := d.GetOk("size"); ok {
req.Size = uint64(size.(int))
}
k8sList, err := c.CloudAPI().K8S().ListDeleted(ctx, req)
if err != nil {
return nil, err
}
return k8sList, nil
}
func workersDifference(slice1 []interface{}, slice2 []interface{}) []interface{} {
var diff []interface{}
for i := 0; i < 2; i++ {
for _, s1 := range slice1 {
found := false
for _, s2 := range slice2 {
if s1.(map[string]interface{})["id"] == s2.(map[string]interface{})["id"] {
found = true
break
}
}
if !found {
diff = append(diff, s1)
}
}
if i == 0 {
slice1, slice2 = slice2, slice1
}
}
return diff
}

View File

@@ -0,0 +1,164 @@
/*
Copyright (c) 2019-2022 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
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.
*/
/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.
Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort
Please see README.md to learn where to place source code so that it
builds seamlessly.
Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/
package k8s
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
log "github.com/sirupsen/logrus"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
)
func utilityDataK8sWgCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*k8s.ItemK8SGroup, []compute.RecordCompute, error) {
c := m.(*controller.ControllerCfg)
k8sId := uint64(d.Get("k8s_id").(int))
wgId := uint64(d.Get("wg_id").(int))
k8sGetReq := k8s.GetRequest{
K8SID: k8sId,
}
cluster, err := c.CloudAPI().K8S().Get(ctx, k8sGetReq)
if err != nil {
return nil, nil, err
}
curWg := k8s.ItemK8SGroup{}
for _, wg := range cluster.K8SGroups.Workers {
if wg.ID == wgId {
curWg = wg
break
}
}
if curWg.ID == 0 {
return nil, nil, fmt.Errorf("WG with id %v in k8s cluster %v not found", wgId, k8sId)
}
workersComputeList := make([]compute.RecordCompute, 0)
for _, info := range curWg.DetailedInfo {
compute, err := utilityComputeCheckPresence(ctx, d, m, info.ID)
if err != nil {
return nil, nil, err
}
workersComputeList = append(workersComputeList, *compute)
}
return &curWg, workersComputeList, nil
}
func utilityK8sWgCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (*k8s.ItemK8SGroup, error) {
c := m.(*controller.ControllerCfg)
var wgId int
var k8sId int
var err error
if strings.Contains(d.Id(), "#") {
wgId, err = strconv.Atoi(strings.Split(d.Id(), "#")[1])
if err != nil {
return nil, err
}
k8sId, err = strconv.Atoi(strings.Split(d.Id(), "#")[0])
if err != nil {
return nil, err
}
} else {
wgId, err = strconv.Atoi(d.Id())
if err != nil {
return nil, err
}
k8sId = d.Get("k8s_id").(int)
}
req := k8s.GetRequest{
K8SID: uint64(k8sId),
}
cluster, err := c.CloudAPI().K8S().Get(ctx, req)
if err != nil {
return nil, err
}
for _, wg := range cluster.K8SGroups.Workers {
if wg.ID == uint64(wgId) {
return &wg, nil
}
}
return nil, fmt.Errorf("not found wg with id: %v in k8s cluster: %v", wgId, cluster.ID)
}
func utilityK8sWgListCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (k8s.ListK8SGroups, error) {
c := m.(*controller.ControllerCfg)
req := k8s.GetRequest{
K8SID: uint64(d.Get("k8s_id").(int)),
}
cluster, err := c.CloudAPI().K8S().Get(ctx, req)
if err != nil {
return nil, err
}
return cluster.K8SGroups.Workers, nil
}
func utilityK8sWgCloudInitCheckPresence(ctx context.Context, d *schema.ResourceData, m interface{}) (string, error) {
c := m.(*controller.ControllerCfg)
req := k8s.GetWorkerNodesMetaDataRequest{
K8SID: uint64(d.Get("k8s_id").(int)),
WorkersGroupID: uint64(d.Get("wg_id").(int)),
}
cloudInit, err := c.CloudAPI().K8S().GetWorkerNodesMetaData(ctx, req)
if err != nil {
return "", err
}
return cloudInit, nil
}
func cloudInitDiffSupperss(key, oldVal, newVal string, d *schema.ResourceData) bool {
if newVal != "" && newVal != oldVal {
log.Debugf("networkSubresIPAddreDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=FALSE", key, oldVal, newVal)
return false
}
log.Debugf("networkSubresIPAddreDiffSupperss: key=%s, oldVal=%q, newVal=%q -> suppress=TRUE", key, oldVal, newVal)
return true // suppress difference
}