From 5382579a5fe27552cd6cef4bb882482a318c60ab Mon Sep 17 00:00:00 2001 From: asteam Date: Thu, 17 Apr 2025 12:31:12 +0300 Subject: [PATCH] 1.2.2 --- CHANGELOG.md | 14 +- Makefile | 2 +- README.md | 12 + docs/resources/bservice.md | 7 +- docs/resources/vins.md | 5 +- go.mod | 2 +- go.sum | 4 +- .../flattens/flatten_resource_bservice.go | 46 +- .../flatten_resource_bservice_group.go | 24 +- .../service/cloudapi/bservice/input_check.go | 69 ++ .../models/model_resource_bservice.go | 2 +- .../cloudapi/bservice/resource_bservice.go | 27 +- .../bservice/resource_bservice_group.go | 59 +- .../schemas/schema_resource_bservice.go | 4 +- .../utilities/utility_resource_bservice.go | 150 +-- .../utility_resource_bservice_group.go | 123 ++- internal/service/cloudapi/ic/input_checks.go | 38 + .../vins/schemas/schema_resource_vins.go | 11 +- .../bservice/resource_bservice/main.tf | 10 +- .../bservice/resource_bservice_group/main.tf | 25 +- samples/cloudapi/image/resource_image/main.tf | 9 +- wiki/.gitignore | 3 + wiki/1.2.2/01.-Введение.md | 7 + wiki/1.2.2/02.-Пример-работы.md | 92 ++ ...лачной-платформы-DYNAMIX.md | 32 + ...erraform-провайдером-DYNAMIX.md | 6 + ...-Terraform-провайдера-DYNAMIX.md | 147 +++ ...-Terraform-провайдера-DYNAMIX.md | 64 ++ ...ние-между-группами-API.md | 38 + ...4-Получение-gid-или-grid_id.md | 17 + ...form-провайдера-в-образ.md | 43 + wiki/1.2.2/05.-Работа-с-terraform.md | 4 + ...ортирование-ресурсов.md | 75 ++ ...02-Работа-с-таймаутами.md | 100 ++ ...становление-ресурсов.md | 29 + ...урсов.-Мета-аргументы.md | 448 +++++++++ ...05.05-Удаление-ресурсов.md | 262 +++++ .../05.06-Установка-Terraform.md | 46 + ...струкция-по-миграции.md | 698 +++++++++++++ ...-Terraform-провайдера-DYNAMIX.md | 1 + ...овательская-группа-API.md | 1 + wiki/1.2.2/06.01.01-Data_dynamix_kvmvm.md | 288 ++++++ wiki/1.2.2/06.01.02-Data_dynamix_resgroup.md | 115 +++ wiki/1.2.2/06.01.03-Data_dynamix_disk.md | 110 ++ wiki/1.2.2/06.01.04-Data_dynamix_vins.md | 292 ++++++ wiki/1.2.2/06.01.05-Data_dynamix_account.md | 113 +++ .../1.2.2/06.01.06-Data_dynamix_image_list.md | 164 +++ wiki/1.2.2/06.01.07-Data_dynamix_image.md | 82 ++ .../06.01.08-Data_dynamix_locations_list.md | 76 ++ .../06.01.09-Data_dynamix_location_url.md | 23 + .../06.01.11-Data_dynamix_snapshot_list.md | 34 + wiki/1.2.2/06.01.12-Data_dynamix_rg_list.md | 122 +++ wiki/1.2.2/06.01.13-Data_dynamix_disk_list.md | 186 ++++ wiki/1.2.2/06.01.14-Data_dynamix_vins_list.md | 108 ++ .../06.01.15-Data_dynamix_extnet_list.md | 88 ++ .../06.01.16-Data_dynamix_extnet_default.md | 24 + ...01.17-Data_dynamix_extnet_computes_list.md | 76 ++ wiki/1.2.2/06.01.18-Data_dynamix_extnet.md | 79 ++ ....01.19-Data_dynamix_account_audits_list.md | 36 + ...1.20-Data_dynamix_account_computes_list.md | 118 +++ ....21-Data_dynamix_account_consumed_units.md | 38 + ..._dynamix_account_consumed_units_by_type.md | 44 + ...6.01.23-Data_dynamix_account_disks_list.md | 79 ++ ...24-Data_dynamix_account_flipgroups_list.md | 104 ++ ....25-Data_dynamix_account_reserved_units.md | 38 + .../06.01.26-Data_dynamix_account_rg_list.md | 93 ++ ....27-Data_dynamix_account_templates_list.md | 84 ++ ...06.01.28-Data_dynamix_account_vins_list.md | 94 ++ .../06.01.29-Data_dynamix_account_list.md | 76 ++ ...01.30-Data_dynamix_account_deleted_list.md | 70 ++ .../06.01.31-Data_dynamix_bservice_list.md | 116 +++ ...1.32-Data_dynamix_bservice_deleted_list.md | 81 ++ ....33-Data_dynamix_bservice_snapshot_list.md | 33 + wiki/1.2.2/06.01.34-Data_dynamix_bservice.md | 92 ++ .../06.01.35-Data_dynamix_bservice_group.md | 68 ++ wiki/1.2.2/06.01.36-Data_dynamix_lb.md | 109 ++ wiki/1.2.2/06.01.37-Data_dynamix_lb_list.md | 181 ++++ .../06.01.38-Data_dynamix_lb_list_deleted.md | 167 ++++ .../06.01.39-Data_dynamix_disk_list_types.md | 45 + ...0-Data_dynamix_disk_list_types_detailed.md | 52 + ...06.01.41-Data_dynamix_disk_list_deleted.md | 129 +++ ...01.42-Data_dynamix_disk_list_unattached.md | 160 +++ ...6.01.43-Data_dynamix_disk_snapshot_list.md | 32 + .../06.01.44-Data_dynamix_disk_snapshot.md | 38 + wiki/1.2.2/06.01.45-Data_dynamix_k8s.md | 131 +++ wiki/1.2.2/06.01.46-Data_dynamix_k8s_list.md | 171 ++++ .../06.01.47-Data_dynamix_k8s_list_deleted.md | 156 +++ wiki/1.2.2/06.01.48-Data_dynamix_k8s_wg.md | 66 ++ .../06.01.49-Data_dynamix_k8s_wg_list.md | 61 ++ .../06.01.50-Data_dynamix_vins_audits.md | 37 + ...06.01.51-Data_dynamix_vins_ext_net_list.md | 37 + .../06.01.52-Data_dynamix_vins_ip_list.md | 38 + ...06.01.53-Data_dynamix_vins_list_deleted.md | 96 ++ ...6.01.54-Data_dynamix_vins_nat_rule_list.md | 38 + .../06.01.55-Data_dynamix_kvmvm_audits.md | 33 + .../06.01.56-Data_dynamix_kvmvm_get_audits.md | 30 + ...1.57-Data_dynamix_kvmvm_get_console_url.md | 28 + .../06.01.58-Data_dynamix_kvmvm_get_log.md | 34 + .../1.2.2/06.01.59-Data_dynamix_kvmvm_list.md | 259 +++++ .../06.01.60-Data_dynamix_kvmvm_pfw_list.md | 35 + .../06.01.61-Data_dynamix_kvmvm_user_list.md | 41 + wiki/1.2.2/06.01.62-Data_dynamix_rg_list.md | 155 +++ ...Data_dynamix_rg_affinity_group_computes.md | 50 + ...64-Data_dynamix_rg_affinity_groups_list.md | 54 + ....65-Data_dynamix_rg_affinity_groups_get.md | 44 + wiki/1.2.2/06.01.66-Data_dynamix_rg_audits.md | 42 + .../06.01.67-Data_dynamix_rg_list_computes.md | 139 +++ .../06.01.68-Data_dynamix_rg_list_deleted.md | 143 +++ .../1.2.2/06.01.69-Data_dynamix_rg_list_lb.md | 194 ++++ .../06.01.70-Data_dynamix_rg_list_pfw.md | 45 + .../06.01.71-Data_dynamix_rg_list_vins.md | 100 ++ wiki/1.2.2/06.01.72-Data_dynamix_rg_usage.md | 51 + ...01.73-Data_dynamix_kvmvm_snapshot_usage.md | 45 + .../06.01.74-Data_dynamix_k8s_computes.md | 47 + wiki/1.2.2/06.01.75-Data_dynamix_flipgroup.md | 49 + .../06.01.75-Data_dynamix_flipgroup_list.md | 125 +++ wiki/1.2.2/06.01.76-Data_dynamix_k8ci_list.md | 88 ++ ...06.01.77-Data_dynamix_vins_static_route.md | 39 + ....78-Data_dynamix_vins_static_route_list.md | 34 + ...ynamix_account_resource_consumption_get.md | 68 ++ ...namix_account_resource_consumption_list.md | 49 + ...06.01.81-Data_dynamix_k8s_wg_cloud_init.md | 39 + ...6.01.82-Data_dynamix_kvmvm_list_deleted.md | 245 +++++ ...1.83-Data_dynamix_kvmvm_pci_device_list.md | 84 ++ .../06.01.84-Data_dynamix_kvmvm_vgpu_list.md | 92 ++ ...ata_dynamix_rg_resource_consumption_get.md | 68 ++ ...ta_dynamix_rg_resource_consumption_list.md | 61 ++ .../06.01.87-Data_dynamix_disk_replication.md | 118 +++ wiki/1.2.2/06.01.87-Data_dynamix_vfpool.md | 63 ++ .../06.01.88-Data_dynamix_vfpool_list.md | 122 +++ wiki/1.2.2/06.01.89-Data_dynamix_stack.md | 42 + .../1.2.2/06.01.90-Data_dynamix_stack_list.md | 76 ++ wiki/1.2.2/06.01.91-Data_dynamix_audit.md | 46 + wiki/1.2.2/06.01.92-Data_dynamix_dpdknet.md | 46 + .../06.01.93-Data_dynamix_dpdknet_list.md | 95 ++ ...94-Data_dynamix_extnet_reserved_ip_list.md | 52 + ...-Terraform-провайдера-dynamix.md | 2 + ...овательская-группа-API.md | 1 + wiki/1.2.2/07.01.01-Resource_dynamix_kvmvm.md | 941 ++++++++++++++++++ .../07.01.02-Resource_dynamix_resgroup.md | 265 +++++ wiki/1.2.2/07.01.03-Resource_dynamix_disk.md | 167 ++++ wiki/1.2.2/07.01.04-Resource_dynamix_vins.md | 474 +++++++++ .../07.01.05-Resource_dynamix_snapshot.md | 48 + wiki/1.2.2/07.01.06-Resource_dynamix_k8s.md | 571 +++++++++++ .../1.2.2/07.01.07-Resource_dynamix_k8s_wg.md | 190 ++++ ...07.01.08-Resource_dynamix_image_virtual.md | 80 ++ wiki/1.2.2/07.01.09-Resource_dynamix_image.md | 157 +++ wiki/1.2.2/07.01.10-Resource_dynamix_pfw.md | 59 ++ .../07.01.11-Resource_dynamix_account.md | 219 ++++ .../07.01.12-Resource_dynamix_bservice.md | 148 +++ ...7.01.13-Resource_dynamix_bservice_group.md | 244 +++++ wiki/1.2.2/07.01.14-Resource_dynamix_lb.md | 203 ++++ .../07.01.15-Resource_dynamix_lb_frontend.md | 73 ++ ...01.16-Resource_dynamix_lb_frontend_bind.md | 71 ++ .../07.01.17-Resource_dynamix_lb_backend.md | 151 +++ ...1.18-Resource_dynamix_lb_backend_server.md | 154 +++ ...07.01.19-Resource_dynamix_disk_snapshot.md | 51 + .../07.01.20-Resource_dynamix_flipgroup.md | 96 ++ .../1.2.2/07.01.21-Resource_dynamix_k8s_cp.md | 371 +++++++ ...1.22-Resource_dynamix_vins_static_route.md | 54 + ...source_dynamix_image_from_blank_compute.md | 145 +++ ...source_dynamix_image_from_platform_disk.md | 168 ++++ ...01.25-Resource_dynamix_disk_replication.md | 161 +++ .../08.-Полезные-советы.md | 52 + wiki/1.2.2/Home.md | 297 ++++++ wiki/Home.md | 3 +- 166 files changed, 16411 insertions(+), 219 deletions(-) create mode 100644 internal/service/cloudapi/bservice/input_check.go create mode 100644 wiki/.gitignore create mode 100644 wiki/1.2.2/01.-Введение.md create mode 100644 wiki/1.2.2/02.-Пример-работы.md create mode 100644 wiki/1.2.2/03.-Обзор-облачной-платформы-DYNAMIX.md create mode 100644 wiki/1.2.2/04.-Начало-работы-с-terraform-провайдером-DYNAMIX.md create mode 100644 wiki/1.2.2/04.01-Установка-Terraform-провайдера-DYNAMIX.md create mode 100644 wiki/1.2.2/04.02-Инициализация-Terraform-провайдера-DYNAMIX.md create mode 100644 wiki/1.2.2/04.03-Переключение-между-группами-API.md create mode 100644 wiki/1.2.2/04.04-Получение-gid-или-grid_id.md create mode 100644 wiki/1.2.2/04.05-Сборка-terraform-провайдера-в-образ.md create mode 100644 wiki/1.2.2/05.-Работа-с-terraform.md create mode 100644 wiki/1.2.2/05.01-Импортирование-ресурсов.md create mode 100644 wiki/1.2.2/05.02-Работа-с-таймаутами.md create mode 100644 wiki/1.2.2/05.03-Восстановление-ресурсов.md create mode 100644 wiki/1.2.2/05.04-Массовое-создание-ресурсов.-Мета-аргументы.md create mode 100644 wiki/1.2.2/05.05-Удаление-ресурсов.md create mode 100644 wiki/1.2.2/05.06-Установка-Terraform.md create mode 100644 wiki/1.2.2/05.07-Инструкция-по-миграции.md create mode 100644 wiki/1.2.2/06.-Data-source-функции-Terraform-провайдера-DYNAMIX.md create mode 100644 wiki/1.2.2/06.01-Пользовательская-группа-API.md create mode 100644 wiki/1.2.2/06.01.01-Data_dynamix_kvmvm.md create mode 100644 wiki/1.2.2/06.01.02-Data_dynamix_resgroup.md create mode 100644 wiki/1.2.2/06.01.03-Data_dynamix_disk.md create mode 100644 wiki/1.2.2/06.01.04-Data_dynamix_vins.md create mode 100644 wiki/1.2.2/06.01.05-Data_dynamix_account.md create mode 100644 wiki/1.2.2/06.01.06-Data_dynamix_image_list.md create mode 100644 wiki/1.2.2/06.01.07-Data_dynamix_image.md create mode 100644 wiki/1.2.2/06.01.08-Data_dynamix_locations_list.md create mode 100644 wiki/1.2.2/06.01.09-Data_dynamix_location_url.md create mode 100644 wiki/1.2.2/06.01.11-Data_dynamix_snapshot_list.md create mode 100644 wiki/1.2.2/06.01.12-Data_dynamix_rg_list.md create mode 100644 wiki/1.2.2/06.01.13-Data_dynamix_disk_list.md create mode 100644 wiki/1.2.2/06.01.14-Data_dynamix_vins_list.md create mode 100644 wiki/1.2.2/06.01.15-Data_dynamix_extnet_list.md create mode 100644 wiki/1.2.2/06.01.16-Data_dynamix_extnet_default.md create mode 100644 wiki/1.2.2/06.01.17-Data_dynamix_extnet_computes_list.md create mode 100644 wiki/1.2.2/06.01.18-Data_dynamix_extnet.md create mode 100644 wiki/1.2.2/06.01.19-Data_dynamix_account_audits_list.md create mode 100644 wiki/1.2.2/06.01.20-Data_dynamix_account_computes_list.md create mode 100644 wiki/1.2.2/06.01.21-Data_dynamix_account_consumed_units.md create mode 100644 wiki/1.2.2/06.01.22-Data_dynamix_account_consumed_units_by_type.md create mode 100644 wiki/1.2.2/06.01.23-Data_dynamix_account_disks_list.md create mode 100644 wiki/1.2.2/06.01.24-Data_dynamix_account_flipgroups_list.md create mode 100644 wiki/1.2.2/06.01.25-Data_dynamix_account_reserved_units.md create mode 100644 wiki/1.2.2/06.01.26-Data_dynamix_account_rg_list.md create mode 100644 wiki/1.2.2/06.01.27-Data_dynamix_account_templates_list.md create mode 100644 wiki/1.2.2/06.01.28-Data_dynamix_account_vins_list.md create mode 100644 wiki/1.2.2/06.01.29-Data_dynamix_account_list.md create mode 100644 wiki/1.2.2/06.01.30-Data_dynamix_account_deleted_list.md create mode 100644 wiki/1.2.2/06.01.31-Data_dynamix_bservice_list.md create mode 100644 wiki/1.2.2/06.01.32-Data_dynamix_bservice_deleted_list.md create mode 100644 wiki/1.2.2/06.01.33-Data_dynamix_bservice_snapshot_list.md create mode 100644 wiki/1.2.2/06.01.34-Data_dynamix_bservice.md create mode 100644 wiki/1.2.2/06.01.35-Data_dynamix_bservice_group.md create mode 100644 wiki/1.2.2/06.01.36-Data_dynamix_lb.md create mode 100644 wiki/1.2.2/06.01.37-Data_dynamix_lb_list.md create mode 100644 wiki/1.2.2/06.01.38-Data_dynamix_lb_list_deleted.md create mode 100644 wiki/1.2.2/06.01.39-Data_dynamix_disk_list_types.md create mode 100644 wiki/1.2.2/06.01.40-Data_dynamix_disk_list_types_detailed.md create mode 100644 wiki/1.2.2/06.01.41-Data_dynamix_disk_list_deleted.md create mode 100644 wiki/1.2.2/06.01.42-Data_dynamix_disk_list_unattached.md create mode 100644 wiki/1.2.2/06.01.43-Data_dynamix_disk_snapshot_list.md create mode 100644 wiki/1.2.2/06.01.44-Data_dynamix_disk_snapshot.md create mode 100644 wiki/1.2.2/06.01.45-Data_dynamix_k8s.md create mode 100644 wiki/1.2.2/06.01.46-Data_dynamix_k8s_list.md create mode 100644 wiki/1.2.2/06.01.47-Data_dynamix_k8s_list_deleted.md create mode 100644 wiki/1.2.2/06.01.48-Data_dynamix_k8s_wg.md create mode 100644 wiki/1.2.2/06.01.49-Data_dynamix_k8s_wg_list.md create mode 100644 wiki/1.2.2/06.01.50-Data_dynamix_vins_audits.md create mode 100644 wiki/1.2.2/06.01.51-Data_dynamix_vins_ext_net_list.md create mode 100644 wiki/1.2.2/06.01.52-Data_dynamix_vins_ip_list.md create mode 100644 wiki/1.2.2/06.01.53-Data_dynamix_vins_list_deleted.md create mode 100644 wiki/1.2.2/06.01.54-Data_dynamix_vins_nat_rule_list.md create mode 100644 wiki/1.2.2/06.01.55-Data_dynamix_kvmvm_audits.md create mode 100644 wiki/1.2.2/06.01.56-Data_dynamix_kvmvm_get_audits.md create mode 100644 wiki/1.2.2/06.01.57-Data_dynamix_kvmvm_get_console_url.md create mode 100644 wiki/1.2.2/06.01.58-Data_dynamix_kvmvm_get_log.md create mode 100644 wiki/1.2.2/06.01.59-Data_dynamix_kvmvm_list.md create mode 100644 wiki/1.2.2/06.01.60-Data_dynamix_kvmvm_pfw_list.md create mode 100644 wiki/1.2.2/06.01.61-Data_dynamix_kvmvm_user_list.md create mode 100644 wiki/1.2.2/06.01.62-Data_dynamix_rg_list.md create mode 100644 wiki/1.2.2/06.01.63-Data_dynamix_rg_affinity_group_computes.md create mode 100644 wiki/1.2.2/06.01.64-Data_dynamix_rg_affinity_groups_list.md create mode 100644 wiki/1.2.2/06.01.65-Data_dynamix_rg_affinity_groups_get.md create mode 100644 wiki/1.2.2/06.01.66-Data_dynamix_rg_audits.md create mode 100644 wiki/1.2.2/06.01.67-Data_dynamix_rg_list_computes.md create mode 100644 wiki/1.2.2/06.01.68-Data_dynamix_rg_list_deleted.md create mode 100644 wiki/1.2.2/06.01.69-Data_dynamix_rg_list_lb.md create mode 100644 wiki/1.2.2/06.01.70-Data_dynamix_rg_list_pfw.md create mode 100644 wiki/1.2.2/06.01.71-Data_dynamix_rg_list_vins.md create mode 100644 wiki/1.2.2/06.01.72-Data_dynamix_rg_usage.md create mode 100644 wiki/1.2.2/06.01.73-Data_dynamix_kvmvm_snapshot_usage.md create mode 100644 wiki/1.2.2/06.01.74-Data_dynamix_k8s_computes.md create mode 100644 wiki/1.2.2/06.01.75-Data_dynamix_flipgroup.md create mode 100644 wiki/1.2.2/06.01.75-Data_dynamix_flipgroup_list.md create mode 100644 wiki/1.2.2/06.01.76-Data_dynamix_k8ci_list.md create mode 100644 wiki/1.2.2/06.01.77-Data_dynamix_vins_static_route.md create mode 100644 wiki/1.2.2/06.01.78-Data_dynamix_vins_static_route_list.md create mode 100644 wiki/1.2.2/06.01.79-Data_dynamix_account_resource_consumption_get.md create mode 100644 wiki/1.2.2/06.01.80-Data_dynamix_account_resource_consumption_list.md create mode 100644 wiki/1.2.2/06.01.81-Data_dynamix_k8s_wg_cloud_init.md create mode 100644 wiki/1.2.2/06.01.82-Data_dynamix_kvmvm_list_deleted.md create mode 100644 wiki/1.2.2/06.01.83-Data_dynamix_kvmvm_pci_device_list.md create mode 100644 wiki/1.2.2/06.01.84-Data_dynamix_kvmvm_vgpu_list.md create mode 100644 wiki/1.2.2/06.01.85-Data_dynamix_rg_resource_consumption_get.md create mode 100644 wiki/1.2.2/06.01.86-Data_dynamix_rg_resource_consumption_list.md create mode 100644 wiki/1.2.2/06.01.87-Data_dynamix_disk_replication.md create mode 100644 wiki/1.2.2/06.01.87-Data_dynamix_vfpool.md create mode 100644 wiki/1.2.2/06.01.88-Data_dynamix_vfpool_list.md create mode 100644 wiki/1.2.2/06.01.89-Data_dynamix_stack.md create mode 100644 wiki/1.2.2/06.01.90-Data_dynamix_stack_list.md create mode 100644 wiki/1.2.2/06.01.91-Data_dynamix_audit.md create mode 100644 wiki/1.2.2/06.01.92-Data_dynamix_dpdknet.md create mode 100644 wiki/1.2.2/06.01.93-Data_dynamix_dpdknet_list.md create mode 100644 wiki/1.2.2/06.01.94-Data_dynamix_extnet_reserved_ip_list.md create mode 100644 wiki/1.2.2/07.-Resource-функции-Terraform-провайдера-dynamix.md create mode 100644 wiki/1.2.2/07.01-Пользовательская-группа-API.md create mode 100644 wiki/1.2.2/07.01.01-Resource_dynamix_kvmvm.md create mode 100644 wiki/1.2.2/07.01.02-Resource_dynamix_resgroup.md create mode 100644 wiki/1.2.2/07.01.03-Resource_dynamix_disk.md create mode 100644 wiki/1.2.2/07.01.04-Resource_dynamix_vins.md create mode 100644 wiki/1.2.2/07.01.05-Resource_dynamix_snapshot.md create mode 100644 wiki/1.2.2/07.01.06-Resource_dynamix_k8s.md create mode 100644 wiki/1.2.2/07.01.07-Resource_dynamix_k8s_wg.md create mode 100644 wiki/1.2.2/07.01.08-Resource_dynamix_image_virtual.md create mode 100644 wiki/1.2.2/07.01.09-Resource_dynamix_image.md create mode 100644 wiki/1.2.2/07.01.10-Resource_dynamix_pfw.md create mode 100644 wiki/1.2.2/07.01.11-Resource_dynamix_account.md create mode 100644 wiki/1.2.2/07.01.12-Resource_dynamix_bservice.md create mode 100644 wiki/1.2.2/07.01.13-Resource_dynamix_bservice_group.md create mode 100644 wiki/1.2.2/07.01.14-Resource_dynamix_lb.md create mode 100644 wiki/1.2.2/07.01.15-Resource_dynamix_lb_frontend.md create mode 100644 wiki/1.2.2/07.01.16-Resource_dynamix_lb_frontend_bind.md create mode 100644 wiki/1.2.2/07.01.17-Resource_dynamix_lb_backend.md create mode 100644 wiki/1.2.2/07.01.18-Resource_dynamix_lb_backend_server.md create mode 100644 wiki/1.2.2/07.01.19-Resource_dynamix_disk_snapshot.md create mode 100644 wiki/1.2.2/07.01.20-Resource_dynamix_flipgroup.md create mode 100644 wiki/1.2.2/07.01.21-Resource_dynamix_k8s_cp.md create mode 100644 wiki/1.2.2/07.01.22-Resource_dynamix_vins_static_route.md create mode 100644 wiki/1.2.2/07.01.23-Resource_dynamix_image_from_blank_compute.md create mode 100644 wiki/1.2.2/07.01.24-Resource_dynamix_image_from_platform_disk.md create mode 100644 wiki/1.2.2/07.01.25-Resource_dynamix_disk_replication.md create mode 100644 wiki/1.2.2/08.-Полезные-советы.md create mode 100644 wiki/1.2.2/Home.md diff --git a/CHANGELOG.md b/CHANGELOG.md index cd21eb0..7a6dd7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ -## Version 1.2.1 +## Version 1.2.2 -### Добавлено +### Исправлено -#### kvmvm +#### bservice | Идентификатор
задачи | Описание | | --- | --- | -| BATF-692 | Опциональное поле `preferred_cpu`в resource `dynamix_kvmvm` в cloudapi/kvmvm | -| BATF-692 | Вычисляемое поле `preferred_cpu`в data_sources `dynamix_kvmvm, dynamix_kvmvm_list, dynamix_kvmvm_list_deleted` в cloudapi/kvmvm | +| BATF-879 | Исправлена работа resources `dynamix_bservice` и `dynamix_bservice_group` в cloudapi/bservice | + +#### vins +| Идентификатор
задачи | Описание | +| --- | --- | +| BATF-877 | Исправлена ошибка записи state в resource `dynamix_vins` в cloudapi/vins | \ No newline at end of file diff --git a/Makefile b/Makefile index 51dcc4d..71549bb 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ ZIPDIR = ./zip BINARY=${NAME} WORKPATH= ./examples/terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${SECONDNAMESPACE}/${VERSION}/${OS_ARCH} MAINPATH = ./cmd/dynamix/ -VERSION=1.2.1 +VERSION=1.2.2 OS_ARCH=$(shell go env GOHOSTOS)_$(shell go env GOHOSTARCH) FILES = ${BINARY}_${VERSION}_darwin_amd64\ diff --git a/README.md b/README.md index 6f7799b..436a472 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,18 @@ | resource dynamix_disk_replication | iotune, replication | | resource dynamix_disk | iotune | +#### Ресурсная группа bservice + +Удалено поле `service_id` для импорта `resource dynamix_bservice` , теперь необходимо передавать `service_id`, при импорте например `terraform import dynamix_bservice.bs 5` + +Удалено поле `compgroup_id` для импорта `resource dynamix_bservice` , теперь необходимо использовать связку `bservice_id#compgroup_id`, например `terraform import dynamix_bservice_group.bsg 5#10` + +Следующие поля в terraform-provider-decort имели тип списка (List), а в terraform-provider-dynamix имеют тип Set: + +| Название ресурса | Поля схемы | +|--------------------------|-------------------| +| resource dynamix_bservice | snapshots | + #### Ресурсная группа disks | Название ресурса | Поля схемы | Изменение по сравнению с terraform-provider-decort | Комментарий | diff --git a/docs/resources/bservice.md b/docs/resources/bservice.md index 148d9aa..3ff909a 100644 --- a/docs/resources/bservice.md +++ b/docs/resources/bservice.md @@ -26,7 +26,7 @@ description: |- - `permanently` (Boolean) - `restore` (Boolean) - `service_id` (Number) -- `snapshots` (Attributes List) (see [below for nested schema](#nestedatt--snapshots)) +- `snapshots` (Attributes Set) (see [below for nested schema](#nestedatt--snapshots)) - `ssh_key` (String) - `ssh_user` (String) - `start` (Boolean) @@ -62,6 +62,10 @@ description: |- ### Nested Schema for `snapshots` +Required: + +- `label` (String) + Optional: - `rollback` (Boolean) @@ -69,7 +73,6 @@ Optional: Read-Only: - `guid` (String) -- `label` (String) - `timestamp` (Number) - `valid` (Boolean) diff --git a/docs/resources/vins.md b/docs/resources/vins.md index 0e5e687..7f2236d 100644 --- a/docs/resources/vins.md +++ b/docs/resources/vins.md @@ -305,10 +305,7 @@ Read-Only: Read-Only: -- `client_type` (String) -- `desc` (String) -- `domainname` (String) -- `hostname` (String) +- `account_id` (Number) - `ip` (String) - `mac` (String) - `type` (String) diff --git a/go.mod b/go.mod index 98ed7fc..62dd15d 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/sirupsen/logrus v1.9.3 - repository.basistech.ru/BASIS/decort-golang-sdk v1.10.1 + repository.basistech.ru/BASIS/decort-golang-sdk v1.10.2 ) require ( diff --git a/go.sum b/go.sum index 098a28c..c91c204 100644 --- a/go.sum +++ b/go.sum @@ -100,5 +100,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -repository.basistech.ru/BASIS/decort-golang-sdk v1.10.1 h1:Z6drv1seHY6nkyEdjGp2LoNKbne1ihrNRs32q93UhcM= -repository.basistech.ru/BASIS/decort-golang-sdk v1.10.1/go.mod h1:OaUynHHuSjWMzpfyoL4au6oLcUogqUkPPBKA15pbHWo= +repository.basistech.ru/BASIS/decort-golang-sdk v1.10.2 h1:sA/ZngL4xvkyz8lVGkqbi2RBi4CrHJjho2WV21KX918= +repository.basistech.ru/BASIS/decort-golang-sdk v1.10.2/go.mod h1:OaUynHHuSjWMzpfyoL4au6oLcUogqUkPPBKA15pbHWo= diff --git a/internal/service/cloudapi/bservice/flattens/flatten_resource_bservice.go b/internal/service/cloudapi/bservice/flattens/flatten_resource_bservice.go index 5a2ad1a..af16baa 100644 --- a/internal/service/cloudapi/bservice/flattens/flatten_resource_bservice.go +++ b/internal/service/cloudapi/bservice/flattens/flatten_resource_bservice.go @@ -40,14 +40,14 @@ func BServiceResource(ctx context.Context, state *models.RecordBasicServiceResou tflog.Info(ctx, "flattens.BServiceResource: before flatten", map[string]any{"service_id": serviceId, "recordBService": recordBService}) *state = models.RecordBasicServiceResourceModel{ - Name: state.Name, - RGID: state.RGID, + Name: types.StringValue(recordBService.Name), + RGID: types.Int64Value(int64(recordBService.RGID)), Permanently: state.Permanently, Enable: state.Enable, Restore: state.Restore, Start: state.Start, - Snapshots: state.Snapshots, Timeouts: state.Timeouts, + Snapshots: flattenSnapshot(ctx, state.Snapshots, recordBService.Snapshots), SSHKey: types.StringValue(recordBService.SSHKey), SSHUser: types.StringValue(recordBService.SSHUser), ServiceId: types.Int64Value(int64(recordBService.ID)), @@ -142,3 +142,43 @@ func flattenGroups(ctx context.Context, items bservice.ListGroups) types.List { tflog.Info(ctx, "End flattenGroups") return res } + +func flattenSnapshot(ctx context.Context, snapshots types.Set, items bservice.ListSnapshots) types.Set { + tflog.Info(ctx, "Start flattenSnapshot") + tempSlice := make([]types.Object, 0, len(items)) + for _, v := range items { + temp := models.ItemSnapshotResourceModel{ + GUID: types.StringValue(v.GUID), + Label: types.StringValue(v.Label), + Rollback: flattenSnapshotRollback(ctx, snapshots, v), + Timestamp: types.Int64Value(int64(v.Timestamp)), + Valid: types.BoolValue(v.Valid), + } + obj, diags := types.ObjectValueFrom(ctx, models.ItemSnapshotResource, temp) + if diags != nil { + tflog.Error(ctx, fmt.Sprint("Error flattenSnapshot struct to obj", diags)) + } + tempSlice = append(tempSlice, obj) + } + + res, diags := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemSnapshotResource}, tempSlice) + if diags != nil { + tflog.Error(ctx, fmt.Sprint("Error flattenSnapshot", diags)) + } + + tflog.Info(ctx, "End flattenSnapshot") + return res +} + +func flattenSnapshotRollback(ctx context.Context, snapshots types.Set, item bservice.ItemSnapshot) types.Bool { + tflog.Info(ctx, "Start flattenSnapshotRollback") + snapshotsList := snapshots.Elements() + for _, snapshot := range snapshotsList { + snapshotMap := snapshot.(types.Object).Attributes() + if snapshotMap["label"].(types.String).ValueString() == item.Label { + return types.BoolValue(snapshotMap["rollback"].(types.Bool).ValueBool()) + } + } + tflog.Info(ctx, "End flattenSnapshotRollback") + return types.BoolValue(false) +} diff --git a/internal/service/cloudapi/bservice/flattens/flatten_resource_bservice_group.go b/internal/service/cloudapi/bservice/flattens/flatten_resource_bservice_group.go index 5d837d3..b7c1934 100644 --- a/internal/service/cloudapi/bservice/flattens/flatten_resource_bservice_group.go +++ b/internal/service/cloudapi/bservice/flattens/flatten_resource_bservice_group.go @@ -50,14 +50,14 @@ func BServiceGroupResource(ctx context.Context, plan *models.ResourceRecordGroup } *plan = models.ResourceRecordGroupModel{ - ServiceID: plan.ServiceID, + ServiceID: types.Int64Value(int64(recordResourceGroup.ServiceID)), CompCount: plan.CompCount, - Name: plan.Name, - CPU: plan.CPU, - RAM: plan.RAM, - Disk: plan.Disk, - ImageID: plan.ImageID, - Driver: plan.Driver, + Name: types.StringValue(recordResourceGroup.Name), + CPU: types.Int64Value(int64(recordResourceGroup.CPU)), + RAM: types.Int64Value(int64(recordResourceGroup.RAM)), + Disk: types.Int64Value(int64(recordResourceGroup.Disk)), + ImageID: types.Int64Value(int64(recordResourceGroup.ImageID)), + Driver: types.StringValue(recordResourceGroup.Driver), SEPID: types.Int64Value(int64(recordResourceGroup.SEPID)), SepPool: types.StringValue(recordResourceGroup.PoolName), CloudInit: plan.CloudInit, @@ -71,9 +71,9 @@ func BServiceGroupResource(ctx context.Context, plan *models.ResourceRecordGroup ForceUpdate: plan.ForceUpdate, Parents: flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordResourceGroup.Parents), RemoveComputes: plan.RemoveComputes, - CompgroupID: plan.CompgroupID, - ID: types.StringValue(strconv.Itoa(int(plan.CompgroupID.ValueInt64()))), - SID: types.StringValue(strconv.Itoa(int(plan.ServiceID.ValueInt64()))), + CompgroupID: types.Int64Value(int64(recordResourceGroup.ID)), + ID: types.StringValue(strconv.Itoa(int(recordResourceGroup.ID))), + SID: types.StringValue(strconv.Itoa(int(recordResourceGroup.ServiceID))), Timeouts: plan.Timeouts, AccountID: types.Int64Value(int64(recordResourceGroup.AccountID)), AccountName: types.StringValue(recordResourceGroup.AccountName), @@ -95,6 +95,10 @@ func BServiceGroupResource(ctx context.Context, plan *models.ResourceRecordGroup UpdatedTime: types.Int64Value(int64(recordResourceGroup.UpdatedTime)), } + if plan.CompCount == types.Int64Null() { + plan.CompCount = types.Int64Value(int64(len(recordResourceGroup.Computes))) + } + tflog.Info(ctx, "End BServiceGroupResource", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) return nil } diff --git a/internal/service/cloudapi/bservice/input_check.go b/internal/service/cloudapi/bservice/input_check.go new file mode 100644 index 0000000..627d7a5 --- /dev/null +++ b/internal/service/cloudapi/bservice/input_check.go @@ -0,0 +1,69 @@ +package bservice + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client" + "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/bservice/models" + "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/ic" +) + +// resourceBServiceGroupInputCheck checks if input parameters are valid. +func resourceBServiceGroupInputCheck(ctx context.Context, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { + diags := diag.Diagnostics{} + + serviceID := uint64(plan.ServiceID.ValueInt64()) + serviceIDValid := true + tflog.Info(ctx, "resourceBServiceGroupInputCheck: exist resource bservice", map[string]any{"service_id": serviceID}) + err := ic.ExistBservise(ctx, serviceID, c) + if err != nil { + serviceIDValid = false + diags.AddError(fmt.Sprintf("Cannot get info about bservice with ID %v", serviceID), err.Error()) + } + + imageID := uint64(plan.ImageID.ValueInt64()) + tflog.Info(ctx, "resourceBServiceGroupInputCheck: exist resource image", map[string]any{"image_id": imageID}) + err = ic.ExistImage(ctx, imageID, c) + if err != nil { + diags.AddError(fmt.Sprintf("Cannot get info about image with ID %v", imageID), err.Error()) + } + + if !plan.ExtNets.IsNull() { + extnetList := plan.ExtNets.Elements() + for _, elem := range extnetList { + extNetID := elem.(types.Int64).ValueInt64() + err = ic.ExistExtNet(ctx, uint64(extNetID), c) + if err != nil { + diags.AddError(fmt.Sprintf("Cannot get info about extnet with ID %v", extNetID), err.Error()) + } + } + } + + if !plan.VINSes.IsNull() { + vinsList := plan.VINSes.Elements() + for _, elem := range vinsList { + vinsID := elem.(types.Int64).ValueInt64() + err = ic.ExistVins(ctx, uint64(vinsID), c) + if err != nil { + diags.AddError(fmt.Sprintf("Cannot get info about VINS with ID %v", vinsList), err.Error()) + } + } + } + + if serviceIDValid && !plan.Parents.IsNull() { + parentsList := plan.Parents.Elements() + for _, elem := range parentsList { + parentID := elem.(types.Int64).ValueInt64() + err = ic.ExistBserviseGroup(ctx, serviceID, uint64(parentID), c) + if err != nil { + diags.AddError(fmt.Sprintf("Cannot get info about parent group with ID %v", parentID), err.Error()) + } + } + } + + return diags +} diff --git a/internal/service/cloudapi/bservice/models/model_resource_bservice.go b/internal/service/cloudapi/bservice/models/model_resource_bservice.go index b9ab5ab..719c3e0 100644 --- a/internal/service/cloudapi/bservice/models/model_resource_bservice.go +++ b/internal/service/cloudapi/bservice/models/model_resource_bservice.go @@ -19,7 +19,7 @@ type RecordBasicServiceResourceModel struct { Restore types.Bool `tfsdk:"restore"` Start types.Bool `tfsdk:"start"` ServiceId types.Int64 `tfsdk:"service_id"` - Snapshots types.List `tfsdk:"snapshots"` + Snapshots types.Set `tfsdk:"snapshots"` Timeouts timeouts.Value `tfsdk:"timeouts"` //computed fields diff --git a/internal/service/cloudapi/bservice/resource_bservice.go b/internal/service/cloudapi/bservice/resource_bservice.go index 153469e..a1c0574 100644 --- a/internal/service/cloudapi/bservice/resource_bservice.go +++ b/internal/service/cloudapi/bservice/resource_bservice.go @@ -67,6 +67,11 @@ func (r *resourceBService) Create(ctx context.Context, req resource.CreateReques ctx, cancel := context.WithTimeout(ctx, createTimeout) defer cancel() + if len(plan.Snapshots.Elements()) > 0 { + resp.Diagnostics.AddError("BServiceResourceCreate: snapshot field must be empty when resource is creating", "") + return + } + // Make create request and get response id, diags := utilities.BServiceResourceCreate(ctx, &plan, r.client) resp.Diagnostics.Append(diags...) @@ -74,15 +79,9 @@ func (r *resourceBService) Create(ctx context.Context, req resource.CreateReques return } plan.ID = types.StringValue(strconv.Itoa(int(*id))) - tflog.Info(ctx, "BServiceResourceCreatee: BService created", map[string]any{"service_id": id}) - - tflog.Info(ctx, "BServiceResourceCreatee: resource creation is completed", map[string]any{"service_id": id}) + tflog.Info(ctx, "BServiceResourceCreate: BService created", map[string]any{"service_id": id}) - currentSnapshots, diags := types.ListValue(plan.Snapshots.Type(ctx), plan.Snapshots.Elements()) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } + tflog.Info(ctx, "BServiceResourceCreate: resource creation is completed", map[string]any{"service_id": id}) // Map response body to schema and populate Computed attribute values resp.Diagnostics.Append(flattens.BServiceResource(ctx, &plan, r.client)...) @@ -90,15 +89,6 @@ func (r *resourceBService) Create(ctx context.Context, req resource.CreateReques return } - if !plan.Snapshots.Equal(currentSnapshots) && !currentSnapshots.IsNull() { - resp.Diagnostics.Append(utilities.SnapshotsBService(ctx, currentSnapshots, plan.Snapshots, uint64(plan.ServiceId.ValueInt64()), r.client)...) - - if resp.Diagnostics.HasError() { - tflog.Warn(ctx, "Create resourceBService: Error snapshosts bservice") - return - } - } - // Set state to fully populated data resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) if resp.Diagnostics.HasError() { @@ -217,8 +207,7 @@ func (r *resourceBService) Update(ctx context.Context, req resource.UpdateReques // SnapshotsBService bservice if !plan.Snapshots.Equal(state.Snapshots) && !plan.Snapshots.IsNull() { - resp.Diagnostics.Append(utilities.SnapshotsBService(ctx, state.Snapshots, plan.Snapshots, uint64(plan.ServiceId.ValueInt64()), r.client)...) - + resp.Diagnostics.Append(utilities.SnapshotsBService(ctx, state.Snapshots, plan.Snapshots, uint64(state.ServiceId.ValueInt64()), r.client)...) if resp.Diagnostics.HasError() { tflog.Error(ctx, "Update resourceBService: Error snapshosts bservice") return diff --git a/internal/service/cloudapi/bservice/resource_bservice_group.go b/internal/service/cloudapi/bservice/resource_bservice_group.go index d1350f3..f11e1b8 100644 --- a/internal/service/cloudapi/bservice/resource_bservice_group.go +++ b/internal/service/cloudapi/bservice/resource_bservice_group.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -69,21 +70,35 @@ func (r *resourceBServiceGroup) Create(ctx context.Context, req resource.CreateR ctx, cancel := context.WithTimeout(ctx, createTimeout) defer cancel() - // Make create request and get response - diags = utilities.BServiceGroupResourceCreate(ctx, &plan, r.client) - resp.Diagnostics.Append(diags...) + // Check if input values are valid in the platform + tflog.Info(ctx, "Create resourceBServiceGroup: starting input checks", map[string]any{"name": plan.Name.ValueString()}) + resp.Diagnostics.Append(resourceBServiceGroupInputCheck(ctx, &plan, r.client)...) if resp.Diagnostics.HasError() { + tflog.Error(ctx, "Create resourceBServiceGroup: Error input checks") return } - tflog.Info(ctx, "BServiceResourceCreatee: BService group created", map[string]any{"service_id": plan.ServiceID.ValueInt64()}) + tflog.Info(ctx, "Create resourceBServiceGroup: input checks successful", map[string]any{"name": plan.Name.ValueString()}) - tflog.Info(ctx, "BServiceResourceCreatee: resource creation is completed", map[string]any{"service_id": plan.ServiceID.ValueInt64()}) - - currentParents, diags := types.ListValue(plan.Parents.Type(ctx), plan.Parents.Elements()) + // Make create request and get response + diags = utilities.BServiceGroupResourceCreate(ctx, &plan, r.client) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } + tflog.Info(ctx, "BServiceResourceCreate: BService group created", map[string]any{"service_id": plan.ServiceID.ValueInt64()}) + + if !plan.Parents.IsNull() { + oldParents := types.ListValueMust( + types.Int64Type, + []attr.Value{}, + ) + resp.Diagnostics.Append(utilities.BServiceGroupParents(ctx, plan.Parents, oldParents, &plan, r.client)...) + if resp.Diagnostics.HasError() { + return + } + } + + tflog.Info(ctx, "BServiceResourceCreate: resource creation is completed", map[string]any{"service_id": plan.ServiceID.ValueInt64()}) // Map response body to schema and populate Computed attribute values resp.Diagnostics.Append(flattens.BServiceGroupResource(ctx, &plan, r.client)...) @@ -91,13 +106,6 @@ func (r *resourceBServiceGroup) Create(ctx context.Context, req resource.CreateR return } - if !plan.Parents.Equal(currentParents) && !currentParents.IsNull() { - resp.Diagnostics.Append(utilities.BServiceGroupParents(ctx, plan.Parents, currentParents, &plan, r.client)...) - if resp.Diagnostics.HasError() { - return - } - } - // Set state to fully populated data resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) if resp.Diagnostics.HasError() { @@ -163,8 +171,7 @@ func (r *resourceBServiceGroup) Update(ctx context.Context, req resource.UpdateR return } - logMap := map[string]any{"service_id": plan.ID.ValueString()} - tflog.Info(ctx, "Update resourceBServiceGroup: got plan successfully", logMap) + tflog.Info(ctx, "Update resourceBServiceGroup: got plan successfully", map[string]any{"compgroup_name": plan.Name.ValueString()}) // Retrieve values from state var state models.ResourceRecordGroupModel @@ -173,6 +180,7 @@ func (r *resourceBServiceGroup) Update(ctx context.Context, req resource.UpdateR tflog.Error(ctx, "Update resourceBServiceGroup: Error receiving the state") return } + logMap := map[string]any{"service_id": state.ID.ValueString()} tflog.Info(ctx, "Update resourceBServiceGroup: got state successfully", logMap) // Set timeouts @@ -189,12 +197,23 @@ func (r *resourceBServiceGroup) Update(ctx context.Context, req resource.UpdateR ctx, cancel := context.WithTimeout(ctx, updateTimeout) defer cancel() + // Check if input values are valid in the platform + tflog.Info(ctx, "Create resourceBServiceGroup: starting input checks", map[string]any{"name": plan.Name.ValueString()}) + resp.Diagnostics.Append(resourceBServiceGroupInputCheck(ctx, &plan, r.client)...) + if resp.Diagnostics.HasError() { + tflog.Error(ctx, "Create resourceBServiceGroup: Error input checks") + return + } + tflog.Info(ctx, "Create resourceBServiceGroup: input checks successful", map[string]any{"name": plan.Name.ValueString()}) + _, err := strconv.Atoi(state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError("Update resourceBServiceGroup: Cannot parse ID from state", err.Error()) return } + plan.ID = state.ID + if !plan.CompCount.Equal(state.CompCount) && !plan.CompCount.IsNull() { resp.Diagnostics.Append(utilities.BServiceGroupResize(ctx, &plan, r.client)...) if resp.Diagnostics.HasError() { @@ -211,7 +230,7 @@ func (r *resourceBServiceGroup) Update(ctx context.Context, req resource.UpdateR } } - if !plan.Name.Equal(state.Name) || !plan.RAM.Equal(state.RAM) || !plan.CPU.Equal(state.CPU) || !plan.Disk.Equal(state.Disk) || !plan.Role.Equal(state.Role) { + if !plan.Name.Equal(state.Name) || !plan.RAM.Equal(state.RAM) || !plan.CPU.Equal(state.CPU) || !plan.Disk.Equal(state.Disk) || (!plan.Role.Equal(state.Role) && !plan.Role.IsUnknown()) { resp.Diagnostics.Append(utilities.BServiceGroupUpdate(ctx, &plan, r.client)...) if resp.Diagnostics.HasError() { tflog.Error(ctx, "Update resourceBServiceGroup: Error update bservice group") @@ -219,7 +238,7 @@ func (r *resourceBServiceGroup) Update(ctx context.Context, req resource.UpdateR } } - if !plan.ExtNets.Equal(state.ExtNets) && !plan.ExtNets.IsNull() { + if !plan.ExtNets.Equal(state.ExtNets) && !plan.ExtNets.IsUnknown() { resp.Diagnostics.Append(utilities.BServiceGroupExtNet(ctx, &plan, r.client)...) if resp.Diagnostics.HasError() { tflog.Error(ctx, "Update resourceBServiceGroup: Error update extnets bservice") @@ -227,7 +246,7 @@ func (r *resourceBServiceGroup) Update(ctx context.Context, req resource.UpdateR } } - if !plan.VINSes.Equal(state.VINSes) && !plan.VINSes.IsNull() { + if !plan.VINSes.Equal(state.VINSes) && !plan.VINSes.IsUnknown() { resp.Diagnostics.Append(utilities.BServiceGroupVinses(ctx, &plan, r.client)...) if resp.Diagnostics.HasError() { tflog.Error(ctx, "Update resourceBServiceGroup: Error update vinses bservice") @@ -331,7 +350,7 @@ func (r *resourceBServiceGroup) Configure(ctx context.Context, req resource.Conf } func (r *resourceBServiceGroup) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - idParts := strings.Split(req.ID, ",") + idParts := strings.Split(req.ID, "#") if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { resp.Diagnostics.AddError( diff --git a/internal/service/cloudapi/bservice/schemas/schema_resource_bservice.go b/internal/service/cloudapi/bservice/schemas/schema_resource_bservice.go index 103ce5b..24d67c4 100644 --- a/internal/service/cloudapi/bservice/schemas/schema_resource_bservice.go +++ b/internal/service/cloudapi/bservice/schemas/schema_resource_bservice.go @@ -38,7 +38,7 @@ func MakeSchemaResourceBService() map[string]schema.Attribute { Optional: true, Computed: true, }, - "snapshots": schema.ListNestedAttribute{ + "snapshots": schema.SetNestedAttribute{ Optional: true, Computed: true, NestedObject: schema.NestedAttributeObject{ @@ -47,7 +47,7 @@ func MakeSchemaResourceBService() map[string]schema.Attribute { Computed: true, }, "label": schema.StringAttribute{ - Computed: true, + Required: true, }, "rollback": schema.BoolAttribute{ Optional: true, diff --git a/internal/service/cloudapi/bservice/utilities/utility_resource_bservice.go b/internal/service/cloudapi/bservice/utilities/utility_resource_bservice.go index 05bd143..a9cd9a0 100644 --- a/internal/service/cloudapi/bservice/utilities/utility_resource_bservice.go +++ b/internal/service/cloudapi/bservice/utilities/utility_resource_bservice.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" @@ -131,7 +132,7 @@ func BServiceResourceCreate(ctx context.Context, plan *models.RecordBasicService plan.ID = types.StringValue(strconv.Itoa(int(serviceId))) enable := plan.Enable.ValueBool() - if enable && (plan.Status.ValueString() == status.Disabled || plan.Status.ValueString() == status.Created) { + if enable { tflog.Info(ctx, "resourceBasicServiceCreate: before calling CloudAPI().BService().Enable", map[string]any{"service_id": serviceId}) res, err := c.CloudAPI().BService().Enable(ctx, bservice.EnableRequest{ServiceID: serviceId}) if err != nil { @@ -142,7 +143,6 @@ func BServiceResourceCreate(ctx context.Context, plan *models.RecordBasicService return &serviceId, diags } tflog.Info(ctx, "resourceBasicServiceCreate: response from CloudAPI().BService().Enable", map[string]any{"service_id": serviceId, "response": res}) - return &serviceId, diags } if plan.Start.ValueBool() { @@ -153,6 +153,7 @@ func BServiceResourceCreate(ctx context.Context, plan *models.RecordBasicService ) return &serviceId, diags } + tflog.Info(ctx, "resourceBasicServiceCreate: before calling CloudAPI().BService().Start", map[string]any{"service_id": serviceId}) _, err := c.CloudAPI().BService().Start(ctx, bservice.StartRequest{ ServiceID: serviceId, }) @@ -250,115 +251,124 @@ func StartStopBService(ctx context.Context, plan *models.RecordBasicServiceResou return nil } -func SnapshotsBService(ctx context.Context, oldSnapshots basetypes.ListValue, newSnapshots basetypes.ListValue, serviceID uint64, c *client.Client) diag.Diagnostics { +func SnapshotsBService(ctx context.Context, oldSnapshots basetypes.SetValue, newSnapshots basetypes.SetValue, serviceID uint64, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} // Handle snapshot changes in the plan tflog.Info(ctx, "Start SnapshotsBService", map[string]any{"service_id": serviceID}) - deletedSnapshots := make([]models.ItemSnapshotResourceModel, 0) - addedSnapshots := make([]models.ItemSnapshotResourceModel, 0) - updatedSnapshots := make([]models.ItemSnapshotResourceModel, 0) + addMap, rollbackMap, deleteMap := differenceSnapshots(oldSnapshots, newSnapshots) - oldSnapshotsList := make([]models.ItemSnapshotResourceModel, 0, len(oldSnapshots.Elements())) - newSnapshotsList := make([]models.ItemSnapshotResourceModel, 0, len(newSnapshots.Elements())) + tflog.Debug(ctx, "SnapshotsBService: Snapshots to be deleted", map[string]any{"deleted_snapshots": deleteMap}) + tflog.Debug(ctx, "SnapshotsBService: Snapshots to be added", map[string]any{"added_snapshots": addMap}) + tflog.Debug(ctx, "SnapshotsBService: Snapshots to be updated", map[string]any{"updated_snapshots": rollbackMap}) - diags.Append(oldSnapshots.ElementsAs(ctx, &oldSnapshotsList, true)...) - if diags.HasError() { - tflog.Error(ctx, "SnapshotsBService: cannot populate SnapshotsBService with plan.Snapshots object element") - return diags - } - - diags.Append(newSnapshots.ElementsAs(ctx, &newSnapshotsList, true)...) - if diags.HasError() { - tflog.Error(ctx, "SnapshotsBService: cannot populate SnapshotsBService with plan.Snapshots object element") - return diags - } - - for _, el := range oldSnapshotsList { - if !isContainsSnapshot(newSnapshotsList, el) { - deletedSnapshots = append(deletedSnapshots, el) - } - } - - for _, el := range newSnapshotsList { - if !isContainsSnapshot(oldSnapshotsList, el) { - addedSnapshots = append(addedSnapshots, el) - } else if isRollback(oldSnapshotsList, el) { - updatedSnapshots = append(updatedSnapshots, el) - } - } - - tflog.Debug(ctx, "SnapshotsBService: Snapshots to be deleted", map[string]any{"deleted_snapshots": deletedSnapshots}) - tflog.Debug(ctx, "SnapshotsBService: Snapshots to be added", map[string]any{"added_snapshots": addedSnapshots}) - tflog.Debug(ctx, "SnapshotsBService: Snapshots to be updated", map[string]any{"updated_snapshots": updatedSnapshots}) - - if len(deletedSnapshots) > 0 { - for _, snapshot := range deletedSnapshots { + if len(deleteMap) > 0 { + for _, snapshot := range deleteMap { req := bservice.SnapshotDeleteRequest{ ServiceID: serviceID, - Label: snapshot.Label.ValueString(), + Label: snapshot["label"].(types.String).ValueString(), } - + tflog.Info(ctx, "SnapshotsBService: before calling CloudAPI().BService().SnapshotDelete", map[string]any{"service_id": serviceID, "req": req}) _, err := c.CloudAPI().BService().SnapshotDelete(ctx, req) if err != nil { tflog.Error(ctx, "SnapshotsBService: Failed to delete snapshot") + diags.AddError(fmt.Sprintf("SnapshotsBService: failed to delete snapshot %d from service %d", + snapshot["label"].(types.String).ValueString(), serviceID), err.Error()) return diags } - tflog.Info(ctx, "Deleted snapshot", map[string]any{"service_id": serviceID, "label": snapshot.Label}) + tflog.Info(ctx, "Deleted snapshot", map[string]any{"service_id": serviceID, "label": req.Label}) } } - if len(addedSnapshots) > 0 { - for _, snapshot := range addedSnapshots { + if len(addMap) > 0 { + for _, snapshot := range addMap { req := bservice.SnapshotCreateRequest{ ServiceID: serviceID, - Label: snapshot.Label.ValueString(), + Label: snapshot["label"].(types.String).ValueString(), } + tflog.Info(ctx, "SnapshotsBService: before calling CloudAPI().BService().SnapshotCreate", map[string]any{"service_id": serviceID, "req": req}) _, err := c.CloudAPI().BService().SnapshotCreate(ctx, req) if err != nil { tflog.Error(ctx, "SnapshotsBService: Failed to create snapshot") + diags.AddError(fmt.Sprintf("SnapshotsBService: failed to create snapshot %d for service %d", + snapshot["label"].(types.String).ValueString(), serviceID), err.Error()) return diags } - tflog.Info(ctx, "Created snapshot", map[string]any{"service_id": serviceID, "label": snapshot.Label}) + tflog.Info(ctx, "Created snapshot", map[string]any{"service_id": serviceID, "label": req.Label}) } } - if len(updatedSnapshots) > 0 { - for _, snapshot := range updatedSnapshots { - req := bservice.SnapshotRollbackRequest{ - ServiceID: serviceID, - Label: snapshot.Label.ValueString(), - } - - _, err := c.CloudAPI().BService().SnapshotRollback(ctx, req) - if err != nil { - tflog.Error(ctx, "SnapshotsBService: Failed to rollback snapshot") - return diags + if len(rollbackMap) > 0 { + for _, snapshot := range rollbackMap { + if snapshot["rollback"].(types.Bool).ValueBool() { + req := bservice.SnapshotRollbackRequest{ + ServiceID: serviceID, + Label: snapshot["label"].(types.String).ValueString(), + } + tflog.Info(ctx, "SnapshotsBService: before calling CloudAPI().BService().SnapshotRollback", map[string]any{"service_id": serviceID, "req": req}) + _, err := c.CloudAPI().BService().SnapshotRollback(ctx, req) + if err != nil { + tflog.Error(ctx, "SnapshotsBService: Failed to rollback snapshot") + diags.AddError(fmt.Sprintf("SnapshotsBService: failed to rollback snapshot %d from service %d", + snapshot["label"].(types.String).ValueString(), serviceID), err.Error()) + return diags + } + tflog.Info(ctx, "Rolled back snapshot", map[string]any{"service_id": serviceID, "label": req.Label}) } - tflog.Info(ctx, "Rolled back snapshot", map[string]any{"service_id": serviceID, "label": snapshot.Label}) } } return nil } -func isContainsSnapshot(els []models.ItemSnapshotResourceModel, el models.ItemSnapshotResourceModel) bool { - for _, elOld := range els { - if elOld.GUID == el.GUID { - return true +func differenceSnapshots(oldSet, newSet types.Set) (added, rollback, removed []map[string]attr.Value) { + oldSlice := oldSet.Elements() + newSlice := newSet.Elements() + + added = make([]map[string]attr.Value, 0) + rollback = make([]map[string]attr.Value, 0) + removed = make([]map[string]attr.Value, 0) + + for _, oldSnapshot := range oldSlice { + oldMap := oldSnapshot.(types.Object).Attributes() + found := false + for _, newSnapshot := range newSlice { + newMap := newSnapshot.(types.Object).Attributes() + if newMap["label"] == oldMap["label"] { + if newMap["rollback"] != oldMap["rollback"] { + rollback = append(rollback, newMap) + } + found = true + break + } + } + if found { + continue } + removed = append(removed, oldMap) } - return false -} -func isRollback(els []models.ItemSnapshotResourceModel, el models.ItemSnapshotResourceModel) bool { - for _, elOld := range els { - if elOld.GUID == el.GUID && elOld.Rollback != el.Rollback && el.Rollback.ValueBool() { - return true + for _, newSnapshot := range newSlice { + newMap := newSnapshot.(types.Object).Attributes() + found := false + for _, oldSnapshot := range oldSlice { + oldMap := oldSnapshot.(types.Object).Attributes() + if newMap["label"] == oldMap["label"] { + if newMap["rollback"] != oldMap["rollback"] { + rollback = append(rollback, newMap) + } + found = true + break + } + } + if found { + continue } + added = append(added, newMap) } - return false + + return } // restoreBservice performs BService Restore request. diff --git a/internal/service/cloudapi/bservice/utilities/utility_resource_bservice_group.go b/internal/service/cloudapi/bservice/utilities/utility_resource_bservice_group.go index d5887db..e64f714 100644 --- a/internal/service/cloudapi/bservice/utilities/utility_resource_bservice_group.go +++ b/internal/service/cloudapi/bservice/utilities/utility_resource_bservice_group.go @@ -91,7 +91,7 @@ func BServiceGroupResourceCreate(ctx context.Context, plan *models.ResourceRecor diags.AddError("Unable to add group", err.Error()) return diags } - plan.ID = types.StringValue(strconv.Itoa(int(plan.CompgroupID.ValueInt64()))) + plan.ID = types.StringValue(strconv.Itoa(int(compgroupId))) plan.SID = types.StringValue(strconv.Itoa(int(plan.ServiceID.ValueInt64()))) if plan.Start.ValueBool() { @@ -114,35 +114,48 @@ func BServiceGroupResourceCreate(ctx context.Context, plan *models.ResourceRecor } func BServiceGroupResize(ctx context.Context, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { - tflog.Info(ctx, "BServiceGroupResize: start.", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "BServiceGroupResize: start.", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.ID.ValueString()}) diags := diag.Diagnostics{} + compGroupID, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64) + if err != nil { + diags.AddError("BServiceGroupResize: cannot parsed ID compute from plan", err.Error()) + return diags + } + req := bservice.GroupResizeRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.CompgroupID.ValueInt64()), + CompGroupID: compGroupID, Count: plan.CompCount.ValueInt64(), Mode: plan.Mode.ValueString(), } - _, err := c.CloudAPI().BService().GroupResize(ctx, req) + tflog.Info(ctx, "BServiceGroupResize: before calling CloudAPI().BService().GroupResize", map[string]any{"req": req}) + _, err = c.CloudAPI().BService().GroupResize(ctx, req) if err != nil { - diags.AddError(fmt.Sprintf("Cannot resize group with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), plan.CompCount.ValueInt64()), err.Error()) + diags.AddError(fmt.Sprintf("Cannot resize group with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), compGroupID), err.Error()) return diags } - tflog.Info(ctx, "BServiceGroupResize: resize group successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "BServiceGroupResize: resize group successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": compGroupID}) return diags } func BServiceGroupUpdate(ctx context.Context, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { - tflog.Info(ctx, "BServiceGroupUpdate: start.", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "BServiceGroupUpdate: start.", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.ID.ValueString()}) diags := diag.Diagnostics{} + compGroupID, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64) + if err != nil { + diags.AddError("BServiceGroupResize: cannot parsed ID compute from plan", err.Error()) + return diags + } + req := bservice.GroupUpdateRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.CompgroupID.ValueInt64()), + CompGroupID: compGroupID, Name: plan.Name.ValueString(), Role: plan.Role.ValueString(), CPU: uint64(plan.CPU.ValueInt64()), @@ -151,18 +164,19 @@ func BServiceGroupUpdate(ctx context.Context, plan *models.ResourceRecordGroupMo Force: plan.ForceUpdate.ValueBool(), } - _, err := c.CloudAPI().BService().GroupUpdate(ctx, req) + tflog.Info(ctx, "BServiceGroupResize: before calling CloudAPI().BService().GroupUpdate", map[string]any{"req": req}) + _, err = c.CloudAPI().BService().GroupUpdate(ctx, req) if err != nil { - diags.AddError(fmt.Sprintf("Cannot update group with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), plan.CompgroupID.ValueInt64()), err.Error()) + diags.AddError(fmt.Sprintf("Cannot update group with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), compGroupID), err.Error()) return diags } - tflog.Info(ctx, "BServiceGroupUpdate: update group successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "BServiceGroupUpdate: update group successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": compGroupID}) return diags } func BServiceGroupReadStatus(ctx context.Context, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { - tflog.Info(ctx, "Read status BServiceGroupReadStatus with ID", map[string]any{"service_id": plan.ServiceID.ValueInt64()}) + tflog.Info(ctx, "Read status BServiceGroupReadStatus with ID", map[string]any{"service_id": plan.SID.ValueString()}) diags := diag.Diagnostics{} @@ -201,43 +215,56 @@ func BServiceGroupReadStatus(ctx context.Context, plan *models.ResourceRecordGro } func BServiceGroupStartStop(ctx context.Context, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { - tflog.Info(ctx, "Start/Stop bservice group", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "Start/Stop bservice group", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.ID.ValueString()}) diags := diag.Diagnostics{} + compGroupID, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64) + if err != nil { + diags.AddError("BServiceGroupStartStop: Cannot parse resource ID from state", err.Error()) + return diags + } + if plan.Start.ValueBool() { req := bservice.GroupStartRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.CompgroupID.ValueInt64()), + CompGroupID: compGroupID, } - + tflog.Info(ctx, "BServiceGroupStartStop: before calling CloudAPI().BService().GroupStart", map[string]any{"req": req}) _, err := c.CloudAPI().BService().GroupStart(ctx, req) if err != nil { - diags.AddError(fmt.Sprintf("Cannot start bservice group with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), plan.CompCount.ValueInt64()), err.Error()) + diags.AddError(fmt.Sprintf("Cannot start bservice group with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), compGroupID), err.Error()) return diags } } else { req := bservice.GroupStopRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.CompgroupID.ValueInt64()), + CompGroupID: compGroupID, Force: plan.ForceStop.ValueBool(), } + tflog.Info(ctx, "BServiceGroupStartStop: before calling CloudAPI().BService().GroupStop", map[string]any{"req": req}) _, err := c.CloudAPI().BService().GroupStop(ctx, req) if err != nil { - diags.AddError(fmt.Sprintf("Cannot stop bservice group with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), plan.CompCount.ValueInt64()), err.Error()) + diags.AddError(fmt.Sprintf("Cannot stop bservice group with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), compGroupID), err.Error()) return diags } } - tflog.Info(ctx, "Start/Stop bservice group successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "Start/Stop bservice group successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": compGroupID}) return diags } func BServiceGroupExtNet(ctx context.Context, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { - tflog.Info(ctx, "update ExtNets", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "update ExtNets", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.ID.ValueString()}) diags := diag.Diagnostics{} + compGroupID, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64) + if err != nil { + diags.AddError("BServiceGroupStartStop: Cannot parse resource ID from state", err.Error()) + return diags + } + extnetList := make([]uint64, 0, len(plan.ExtNets.Elements())) diags.Append(plan.ExtNets.ElementsAs(ctx, &extnetList, true)...) @@ -248,25 +275,32 @@ func BServiceGroupExtNet(ctx context.Context, plan *models.ResourceRecordGroupMo req := bservice.GroupUpdateExtNetRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.CompgroupID.ValueInt64()), + CompGroupID: compGroupID, ExtNets: extnetList, } - _, err := c.CloudAPI().BService().GroupUpdateExtNet(ctx, req) + tflog.Info(ctx, "BServiceGroupExtNet: before calling CloudAPI().BService().GroupUpdateExtNet", map[string]any{"req": req}) + _, err = c.CloudAPI().BService().GroupUpdateExtNet(ctx, req) if err != nil { - diags.AddError(fmt.Sprintf("Cannot update bservice group extnets with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), plan.CompCount.ValueInt64()), err.Error()) + diags.AddError(fmt.Sprintf("Cannot update bservice group extnets with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), compGroupID), err.Error()) return diags } - tflog.Info(ctx, "BServiceGroupExtNet: update successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "BServiceGroupExtNet: update successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": compGroupID}) return diags } func BServiceGroupVinses(ctx context.Context, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { - tflog.Info(ctx, "update Vinses", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "update Vinses", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.ID.ValueString()}) diags := diag.Diagnostics{} + compGroupID, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64) + if err != nil { + diags.AddError("BServiceGroupStartStop: Cannot parse resource ID from state", err.Error()) + return diags + } + vinsesList := make([]uint64, 0, len(plan.VINSes.Elements())) diags.Append(plan.VINSes.ElementsAs(ctx, &vinsesList, true)...) @@ -277,23 +311,30 @@ func BServiceGroupVinses(ctx context.Context, plan *models.ResourceRecordGroupMo req := bservice.GroupUpdateVINSRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.ServiceID.ValueInt64()), + CompGroupID: compGroupID, VINSes: vinsesList, } - _, err := c.CloudAPI().BService().GroupUpdateVINS(ctx, req) + tflog.Info(ctx, "BServiceGroupVinses: before calling CloudAPI().BService().GroupUpdateVINS", map[string]any{"req": req}) + _, err = c.CloudAPI().BService().GroupUpdateVINS(ctx, req) if err != nil { - diags.AddError(fmt.Sprintf("Cannot update bservice group vinses with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), plan.CompCount.ValueInt64()), err.Error()) + diags.AddError(fmt.Sprintf("Cannot update bservice group vinses with ServiceID - %d,CompgroupID - %d ", plan.ServiceID.ValueInt64(), compGroupID), err.Error()) return diags } - tflog.Info(ctx, "BServiceGroupVinses: update successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "BServiceGroupVinses: update successfully", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": compGroupID}) return diags } func BServiceGroupParents(ctx context.Context, newParents basetypes.ListValue, oldParents basetypes.ListValue, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} - tflog.Info(ctx, "Start BServiceGroupParents", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "Start BServiceGroupParents", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.ID.ValueString()}) + + compGroupID, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64) + if err != nil { + diags.AddError("BServiceGroupStartStop: Cannot parse resource ID from state", err.Error()) + return diags + } deletedParents := make([]uint64, 0) addedParents := make([]uint64, 0) @@ -330,7 +371,7 @@ func BServiceGroupParents(ctx context.Context, newParents basetypes.ListValue, o req := bservice.GroupParentRemoveRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.CompCount.ValueInt64()), + CompGroupID: compGroupID, ParentID: parent, } @@ -340,7 +381,7 @@ func BServiceGroupParents(ctx context.Context, newParents basetypes.ListValue, o return diags } } - tflog.Info(ctx, "Deleted parents", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "Deleted parents", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": compGroupID}) } if len(addedParents) > 0 { @@ -348,17 +389,17 @@ func BServiceGroupParents(ctx context.Context, newParents basetypes.ListValue, o req := bservice.GroupParentAddRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.CompCount.ValueInt64()), + CompGroupID: compGroupID, ParentID: parent, } - + tflog.Info(ctx, "BServiceGroupParents: before calling CloudAPI().BService().GroupParentAdd", map[string]any{"req": req}) _, err := c.CloudAPI().BService().GroupParentAdd(ctx, req) if err != nil { tflog.Error(ctx, "BServiceGroupParents: Failed to add parent") return diags } } - tflog.Info(ctx, "Added parents", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.CompgroupID.ValueInt64()}) + tflog.Info(ctx, "Added parents", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": compGroupID}) } return diags @@ -366,6 +407,14 @@ func BServiceGroupParents(ctx context.Context, newParents basetypes.ListValue, o func BServiceGroupRemoveComputes(ctx context.Context, plan *models.ResourceRecordGroupModel, c *client.Client) diag.Diagnostics { diags := diag.Diagnostics{} + tflog.Info(ctx, "Start BServiceGroupRemoveComputes", map[string]any{"service_id": plan.ServiceID.ValueInt64(), "compgroup_id": plan.ID.ValueString()}) + + compGroupID, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64) + if err != nil { + diags.AddError("BServiceGroupStartStop: Cannot parse resource ID from state", err.Error()) + return diags + } + rcs := plan.RemoveComputes rcsList := make([]uint64, 0, len(rcs.Elements())) @@ -379,10 +428,10 @@ func BServiceGroupRemoveComputes(ctx context.Context, plan *models.ResourceRecor for _, rc := range rcsList { req := bservice.GroupComputeRemoveRequest{ ServiceID: uint64(plan.ServiceID.ValueInt64()), - CompGroupID: uint64(plan.CompCount.ValueInt64()), + CompGroupID: compGroupID, ComputeID: rc, } - + tflog.Info(ctx, "BServiceGroupParents: before calling CloudAPI().BService().GroupComputeRemove", map[string]any{"req": req}) _, err := c.CloudAPI().BService().GroupComputeRemove(ctx, req) if err != nil { tflog.Error(ctx, "BServiceGroupRemoveComputes: Failed to remove compute") diff --git a/internal/service/cloudapi/ic/input_checks.go b/internal/service/cloudapi/ic/input_checks.go index 34738e0..f50d97c 100644 --- a/internal/service/cloudapi/ic/input_checks.go +++ b/internal/service/cloudapi/ic/input_checks.go @@ -6,6 +6,7 @@ import ( "fmt" account "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/account" + "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/bservice" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/disks" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/dpdknet" @@ -523,3 +524,40 @@ func ExistLBFrontend(ctx context.Context, lbId uint64, fName string, c *client.C return fmt.Errorf("frontend with name %v not found", fName) } + +func ExistBservise(ctx context.Context, serviceID uint64, c *client.Client) error { + req := bservice.ListRequest{ + ByID: serviceID, + } + + rgList, err := c.CloudAPI().BService().List(ctx, req) + if err != nil { + return err + } + + if len(rgList.Data) == 0 { + return fmt.Errorf("Bservice with id %v not found", serviceID) + } + + for _, v := range rgList.Data { + if v.Status != "ENABLED" { + return fmt.Errorf("Bservice with id %v should be enabled", serviceID) + } + } + + return nil +} + +func ExistBserviseGroup(ctx context.Context, serviseID uint64, compGroupID uint64, c *client.Client) error { + req := bservice.GroupGetRequest{ + ServiceID: serviseID, + CompGroupID: compGroupID, + } + + _, err := c.CloudAPI().BService().GroupGet(ctx, req) + if err != nil { + return err + } + + return nil +} diff --git a/internal/service/cloudapi/vins/schemas/schema_resource_vins.go b/internal/service/cloudapi/vins/schemas/schema_resource_vins.go index ba328b6..250aa65 100644 --- a/internal/service/cloudapi/vins/schemas/schema_resource_vins.go +++ b/internal/service/cloudapi/vins/schemas/schema_resource_vins.go @@ -523,16 +523,7 @@ func MakeSchemaResourceVINS() map[string]schema.Attribute { Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ - "client_type": schema.StringAttribute{ - Computed: true, - }, - "desc": schema.StringAttribute{ - Computed: true, - }, - "domainname": schema.StringAttribute{ - Computed: true, - }, - "hostname": schema.StringAttribute{ + "account_id": schema.Int64Attribute{ Computed: true, }, "ip": schema.StringAttribute{ diff --git a/samples/cloudapi/bservice/resource_bservice/main.tf b/samples/cloudapi/bservice/resource_bservice/main.tf index ccc63a5..f670efa 100644 --- a/samples/cloudapi/bservice/resource_bservice/main.tf +++ b/samples/cloudapi/bservice/resource_bservice/main.tf @@ -57,7 +57,7 @@ resource "dynamix_bservice" "b" { #используется при редактировании ресурса #может быть несколько в ресурсе /* - snapshots { + snapshots = [{ #имя снимка состояния #обязательный параметр #тип - строка @@ -69,7 +69,7 @@ resource "dynamix_bservice" "b" { #по-умолчанию - false #восстановление происходит только при переключении с false на true rollback = false - } + }] */ #старт сервиса @@ -93,12 +93,6 @@ resource "dynamix_bservice" "b" { #по-умолачанию - false #permanently = true - #id сервиса, позволяет сформировать .tfstate, если сервис есть в платформе - #необязательный параметр - #тип - булев - #используется при создании ресурса - #service_id = 11111 - } output "test" { diff --git a/samples/cloudapi/bservice/resource_bservice_group/main.tf b/samples/cloudapi/bservice/resource_bservice_group/main.tf index 5be0997..6c19c21 100644 --- a/samples/cloudapi/bservice/resource_bservice_group/main.tf +++ b/samples/cloudapi/bservice/resource_bservice_group/main.tf @@ -41,12 +41,6 @@ resource "dynamix_bservice_group" "bsg" { #тип - строка compgroup_name = "tf_group_rename" - #id группы - #необязательный параметр - #тип - целое число - #применяется при редактировании группы, либо при создании .tfstate - файла, если группа имеется в плафторме - compgroup_id = 33333 - #кол-во вычислительных ресурсов #обязательный параметр #тип - целое число @@ -84,46 +78,45 @@ resource "dynamix_bservice_group" "bsg" { #id Storage endpoint provider #необязательный параметр #тип - целое число - sep_id = 3 + #sep_id = 3 - #Наименование SEPPool используется если установлен sepId, также может быть пустым + #наименование SEPPool используется если установлен sepId, также может быть пустым #необязательный параметр #тип - строка - sep_pool = "name" + #sep_pool = "name" #тег группы #необязательный параметр #тип - строка #используется при создании и редактировании ресурса - role = "tf_test_changed" + #role = "tf_test_changed" #id сетей extnet #необязательный параметр #тип - массив целых чисел #должен быть использован vins или extnets - extnets = [1111, 2222] + #extnets = [1111, 2222] #id сетей vinses #необязательный параметр #тип - массив целых чисел #должен быть использован vins или extnets - vinses = [1111, 2222] + #vinses = [1111, 2222] #время таймуата перед стартом #необязательный параметр #тип - целое число #используется при создании ресурса - timeout_start = 0 + #timeout_start = 0 - #Перечень аргументов для cloud-init создаваемым группам узлов Worker + #перечень аргументов для cloud-init создаваемым группам узлов Worker #необязательный параметр #тип - файл в формате YAML - cloud_init = file("initconfig.tftpl") + #cloud_init = file("initconfig.tftpl") #id групп родителей #необязательный параметр #тип - массив целых чисел - #используется при редактировании ресурса #parents = [2222] #принудительное обновление параметров выч. мощностей (ram,disk,cpu) и имени группы diff --git a/samples/cloudapi/image/resource_image/main.tf b/samples/cloudapi/image/resource_image/main.tf index e144bff..8a9f471 100644 --- a/samples/cloudapi/image/resource_image/main.tf +++ b/samples/cloudapi/image/resource_image/main.tf @@ -28,11 +28,6 @@ resource "dynamix_image" "img" { #тип - строка image_name = "image_name" - #grid ID платформы - #обязательный параметр - #тип - целое число - gid = 2002 - #драйверы компьютов, подходящие для данного образа #обязательный параметр #тип - массив строк @@ -54,9 +49,9 @@ resource "dynamix_image" "img" { boot_type = "bios" #id аккаунта - #опциональный параметр + #обязательный параметр #тип - целое число - #account_id = 138 + account_id = 138 #поддержка hot resize #опциональный параметр diff --git a/wiki/.gitignore b/wiki/.gitignore new file mode 100644 index 0000000..b3b5f1f --- /dev/null +++ b/wiki/.gitignore @@ -0,0 +1,3 @@ +.idea/ +.vscode/ +.DS_Store \ No newline at end of file diff --git a/wiki/1.2.2/01.-Введение.md b/wiki/1.2.2/01.-Введение.md new file mode 100644 index 0000000..fc01643 --- /dev/null +++ b/wiki/1.2.2/01.-Введение.md @@ -0,0 +1,7 @@ +DYNAMIX Terraform Provider версии 1.2.x позволяет управлять облачными ресурсами на платформе DYNAMIX версии 4.2.x и выше посредством Terraform. + +С помощью данного провайдера можно организовать программное управление вычислительными ресурсами (_compute_), ресурсными группами, сетевыми и дисковыми ресурсами, образами дисков, кластером, а также другими параметрами облачной платформы DYNAMIX. + +Если вы хорошо знакомы с инструментом Terraform и хотите максимально быстро начать использовать платформу DYNAMIX в своих Terraform-проектах, то можете сразу перейти к разделу [Пример работы](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/02.-Пример-работы.md), где приведён подробно откомментированный пример работы с основными видами ресурсов платформы. Если у вас всё же возникнут вопросы по облачной платформе DYNAMIX и порядку авторизации в ней, то обратитесь к главе [«Обзор облачной платформы DYNAMIX»](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/03.-Обзор-облачной-платформы-DYNAMIX.md). Также может оказаться полезной глава [«Инициализация Terraform провайдера DYNAMIX»](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/04.02-Инициализация-Terraform-провайдера-DYNAMIX.md). + +Если вы только начинаете использовать инструмент Terraform и облачную платформу DYNAMIX, то рекомендуем вам начать с главы [«Обзор облачной платформы DYNAMIX»](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/03.-Обзор-облачной-платформы-DYNAMIX.md), после чего изучить главы [«_Data source_ функции Terraform провайдера DYNAMIX»](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/06.-Data-source-функции-Terraform-провайдера-DYNAMIX.md) и [«_Resource_ функции Terraform провайдера DYNAMIX»](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/07.-Resource-функции-Terraform-провайдера-dynamix.md). Примеры, приведенные в этих разделах, помогут вам быстро освоить базовые приёмы работы с инструментом Terraform и провайдером DYNAMIX. diff --git a/wiki/1.2.2/02.-Пример-работы.md b/wiki/1.2.2/02.-Пример-работы.md new file mode 100644 index 0000000..c427c13 --- /dev/null +++ b/wiki/1.2.2/02.-Пример-работы.md @@ -0,0 +1,92 @@ +Данный раздел предназначен для тех, кто хорошо знаком с инструментом Terraform, а также имеет представление об основных понятиях и способах авторизации в облачной платформе DYNAMIX. + +Ниже приведён подробно откомментированный пример, показывающий, как создать виртуальный сервер (aka _compute_ на базе системы виртуализации KVM x86) в облачной платформе DYNAMIX с помощью соответствующего Terraform провайдера. Сервер будет создан в новой ресурсной группе, к нему будет подключён один предварительно созданный диск, у сервера будет прямое сетевое подключение во внешнюю сеть. + +Идентификатор образа операционной системы, на базе которого должен быть создан виртуальный сервер, считывается из облачной платформы с помощью _data source_ функции `dynamix_image`. + +Далее мы с помощью _resource_ функции `dynamix_resgroup` создаём новую ресурсную группу, в которую будет помещён этот виртуальный сервер. В качестве альтернативы, для получения информации об уже имеющейся ресурсной группе можно использовать _data source_ функцию с таким же названием. + +Затем с помощью _resource_ функции `dynamix_disk` создаётся диск, который будет подключён к виртуальному серверу в качестве дополнительного. Помимо этого дополнительного диска у сервера будет также и загрузочный диск, на который в процессе создания сервера клонируется выбранный образ операционной системы. + +Виртуальный сервер - в данном примере на базе системы виртуализации KVM x86 - создаётся посредством _resource_ функции `dynamix_kvmvm`. + +Только авторизованные в контроллере облачной платформы пользователи могут управлять облачными ресурсами. Подробнее о способах авторизации см. [Обзор облачной платформы DYNAMIX](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/03.-Обзор-облачной-платформы-DYNAMIX.md). + +```terraform +# 1. Initialize DYNAMIX plugin and connection to DYNAMIX cloud controller +# NOTE: in this example credentials are expected to come from +# DYNAMIX_APP_ID and DYNAMIX_APP_SECRET environmental variables - set them +# in the shell before calling terraform. +# Alternatively you may define plugin parameters app_id and app_secret in +# the TF file, however, this may not be secure if you plan to share this TF +# file with others. + +provider "dynamix" { + authenticator = "decs3o" + controller_url = "<>" # specify correct DYNAMIX controller URL, e.g. "https://ds1.digitalenergy.online" + oauth2_url = "<>" # specify corresponding DYNAMIX OAUTH2 URL, e.g. "https://sso.digitalenergy.online" + app_id = "<>" # application secret to access DYNAMIX cloud API in 'decs3o' and 'bvs' authentication mode, e.g. "ewqfrvea7s890avw804389qwguf234h0otfi3w4eiu" + app_secret = "<>" # application ID to access DYNAMIX cloud API in 'decs3o' and 'bvs' authentication mode, e.g. "ewqfrvea7s890avw804389qwguf234h0otfi3w4eiu" + # allow_unverified_ssl = true +} + +# 2. Load account to use - new VM will belong to this account +data "dynamix_account" "my_account" { + account_id = # Specify account ID +} + +# 3. Load OS image to use for VM deployment +data "dynamix_image" "os_image" { + image_id = # Specify OS image id, e.g. 1234. You can get accessible image id from data source "dynamix_image_list" +} + +# 4. Create new Resource Group in the selected account, new VM will be created in this RG +resource "dynamix_resgroup" "my_rg" { + name = "NewRgByTF" + account_id = data.dynamix_account.my_account.account_id + gid = # Grid (platform) ID + # if you want to set resource quota on this Resource Group, uncomment + # the following code fragment + # quota { + # cpu = 8 # CPU limit + # ram = 8912 # RAM limit in MB + # disk = 96 # disk volume limit in GB + #} +} + +# 5. Create extra disk, which will be attached to the new VM. +# This step is optional - if you do not want extra disks on your VM, skip it +# and comment out extra_disks parameter when creating VM below. +resource "dynamix_disk" "extra_disk" { + disk_name = "extra-disk-for-vm" + account_id = data.dynamix_account.my_account.account_id + gid = # Grid (platform) ID + size_max = 5 # disk size in GB + type = "D" # disk type, always use "D" for extra disks + sep_id = data.dynamix_image.os_image.sep_id # use the same SEP ID as the OS image + pool = "<>" # consult your DYNAMIX platform admin for configured storage pool names +} + +# 6. Create virtual machine (a compute of type KVM VM x86 in this example) +# Now that we have all necessary components at hand, we may create a virtual machine. +# This VM will be based on the previsouly obtained OS image, located in the specified +# Resource Group, directly connected to an external network, have a boot disk of +# specified size and one extra disk attached. +resource "dynamix_kvmvm" "my_new_vm" { + name = "tf-managed-vm" + driver = "KVM_X86" # Compute virtualization driver + rg_id = dynamix_resgroup.my_rg.id + cpu = 1 # CPU count + ram = 1024 # RAM size in MB, must be even number, ideally a power of 2 + boot_disk_size = 10 # Boot disk size in GB + image_id = data.dynamix_image.os_image.image_id + description = "Test KVM VM Compute managed by Terraform" + extra_disks = [ dynamix_disk.extra_disk.id ] + + network { + net_type = "EXTNET" + net_id = <> # specify external network ID to use, consult your DYNAMIX platform admin for correct IDs + # ip_address = "<>" # you may optionally request a specific IP address + } +} +``` diff --git a/wiki/1.2.2/03.-Обзор-облачной-платформы-DYNAMIX.md b/wiki/1.2.2/03.-Обзор-облачной-платформы-DYNAMIX.md new file mode 100644 index 0000000..790ec7e --- /dev/null +++ b/wiki/1.2.2/03.-Обзор-облачной-платформы-DYNAMIX.md @@ -0,0 +1,32 @@ +## Основные понятия +Ниже перечислены основные понятия с указанием соответствующих им аргументов в Terraform провайдере DYNAMIX. +1. **Контроллер облачной платформы DYNAMIX** – управляющее приложение, которое обеспечивает авторизацию пользователей и оркестрацию облачных ресурсов. + - Адрес контроллера задается в обязательном аргументе `controller_url` на стадии инициализации Terraform провайдера DYNAMIX. Например, `controller_url= "https://ds1.digitalenergy.online"` +2. **Авторизационный провайдер** – приложение, работающее по протоколу Oauth2, предназначенное для выпуска и валидации токенов доступа к контроллеру облачной платформы в соответствующих режимах авторизации. Все действия в платформе должны выполняться авторизованными пользователями, и авторизационное приложение позволяет получить токен доступа, действующий некоторое ограниченное время, наличие которого подтверждает успешную авторизацию. + - Адрес авторизационного провайдера задается в аргументе`oauth2_url` на стадии инициализации Terraform провайдера DYNAMIX. Например `oauth2_url= "https://sso.digitalenergy.online"` +3. **Подписчик** (_account_) – сущность, которая используется для группирования облачных ресурсов по принадлежности к определенному клиенту для целей учета потребления и биллинга. + - Имя подписчика задается аргументом `account_name` при вызове _resource_ или _data_ функций провайдера. Альтернативной является задание численного идентификатора подписчика в аргументе `account_id`. +4. **Пользователь** (_user_) – пользователь облачной инфраструктуры, представленный учетной записью. Чтобы получить возможность управлять облачными ресурсами (например, создавать виртуальные серверы или дискт) пользователь должен быть ассоциирован с одним или несколькими подписчиками и иметь соответствующие права, определяемые ролевой моделью, принятой в облачной платформе DYNAMIX. Для доступа к платформе пользователь должен авторизоваться одним из способов, описанных ниже в разделе «Способы авторизации». +5. **Ресурсная группа** (_resource group_) – способ группирования вычислительных ресурсов (например, виртуальных серверов по функциональному признаку или принадлежности к одному и тому же проекту). Ресурсную группу можно рассматривать как небольшой персональный дата-центр, в котором размещаются один или несколько серверов и виртуальных сетевых сегментов. Ресурсная группа идентифицируется по комбинации параметров `account` и `name`. Обратите внимание, что имя имя ресурсной группы уникально только в рамках одного и того же `account`. +6. **Вычислительный ресурс** (_compute_) - универсальная абстракция пользовательского сервера в платформе DYNAMIX. Благодаря использованию такой абстракции можно, например, создать одну виртуальную машину на базе KVM Intel x86, а другую - на базе KVM IBM Power, а потом управлять ими - изменять количество CPU/RAM, подключать/отключать диски и т.п. - одинаковым образом, не задумываясь об их архитектурных различиях. В то же время, так как типизация ресурсов в Terraform не поддерживает наследование, различные типы вычислительных ресурсов, доступных на платформе DYNAMIX и абстрагируемых через понятие унифицированный _compute_, в Terraform представлены разными типами (напр., свой тип для виртуальных серверов на базе KVM и свой тип для перспективных x86-совместимых bare metal серверов). +7. **Ресурс хранения** (_disk_) - универсальная абстракция дискового ресурса в платформе DYNAMIX. Платформа поддерживает различные типы систем хранения данных, но при этом управление созданными на разных системах хранения дисками осуществляется посредством унифицированного набора действий, например, "подключить диск к _compute_", "увеличить размер диска", "сделать мгновенный снимок диска", "настроить параметры быстродействия диска". +8. **Виртуальный сервер** – экземпляр _compute_, в основе технической реализации которого лежит виртуальная машина, работающая в облаке и доступна по сети. Виртуальный сервер характеризуется количеством выделенных ему CPU (аргумент`cpu`), объемом ОЗУ (`ram`), размером загрузочного диска (`boot_disk size`). При создании виртуального сервера на загрузочный диск устанавливается образ операционной системы, заданный в аргументе `image_id`. Помимо загрузочного диска к виртуальному серверу можно подключить несколько дисков для хранения прикладных данных, список которых задается аргументами `extra_disks`. Виртуальный сервер идентифицируется по комбинации аргументов `name` (имя сервера) и `rgid` (идентификатор ресурсной группы). Обратите внимание, что имя виртуального сервера `name` уникально только в рамках одной и той же ресурсной группы. +9. **Виртуальный сетевой сегмент** (_Virtual Network Segment_ или _ViNS_) - сетевой сегмент и обеспечивающая его функционирование виртуальная инфраструктура, которые пользователь может создавать для своих нужд на уровне ресурсной группы или подписчика (_account_). ViNS можно создать полностью изолированным от внешних сетей (см. ниже _External Network_) или с подключением во внешнюю сеть. Внутри ViNS работает DHCP-сервис, обеспечивающий управление IP адресами экземпляров _compute_, подключённых в этот ViNS. +10. **Внешняя сеть** (_External Network_) - сетевой сегмент, через который платформа DYNAMIX взаимодействует с внешними по отношению к ней сетевыми ресурсами. Например, в случае с публичным облаком на базе DYNAMIX в качестве внешней сети выступает сеть Интернет. В отличие от ViNS платформа не управляет внешней сетью, а лишь пользуется её ресурсами. В платформе может быть настроено несколько внешних сетей с различными диапазонами IP адресов, и существует механизм управления доступом пользователей к внешним сетям. +11. Сетевой доступ к экземпляру _compute_ (виртуальному серверу) реализуется через его подключение к ViNS и/или прямое подключение во внешнюю сеть (External Network). Один и тот же экземпляр _compute_ может одновременно иметь несколько подключений в разные ViNS и/или различные внешние сети. + +## Способы авторизации +Облачная платформа DYNAMIX поддерживает три базовых типа авторизации: +1. С использованием авторизационного провайдера, работающего по протоколу _Oauth2_. Данный способ является предпочтительным, так как обеспечивает бОльшую гибкость и безопасность. Для авторизации в этом режиме при инициализации Terrafrom провайдера DYNAMIX необходимо указать параметры `oauth2_url` и `controller_url`, а также предоставить одно из нижеперечисленного: + - Комбинация Application ID & Application secret, соответствующих пользователю, от имени которого будет осуществляться управление облачными ресурсами в текущей сессии. В процессе проверки предоставленных Application ID & Application secret модуль получает от авторизационного провайдера токен (JSON Web Token, JWT), который затем используется для доступа к указанному контроллеру DYNAMIX. Для авторизации по данному варианту, при инициализации Terraform провайдера DYNAMIX следует установить аргумент `authenticator=decs3o` и задать аргументы `app_id` и `app_secret` (или определить соответствующие переменные окружения `DYNAMIX_APP_ID` и `DYNAMIX_APP_SECRET`). + - JSON Web Token – заранее полученный от авторизационного провайдера токен доступа, ассоциированный с определенным пользователем, от имени которого будет осуществляться управление облачными ресурсами в текущей сессии. Для авторизации по данному варианту, при инициализации Terraform провайдера DYNAMIX следует установить аргумент `authenticator=jwt` и задать аргумент `jwt` (или определить переменную окружения `DYNAMIX_JWT`). +2. С использованием комбинации _имя пользователя : пароль_. Данный режим не использует внешних авторизационных провайдеров и подразумевает, что пользователь с такой комбинацией зарегистрирован непосредственно на указанном в параметре `controller_url` контроллере облачной платформы DYNAMIX. + - Чтобы провайдер авторизовался по данному варианту, при его инициализации следует установить аргумент `authenticator=legacy` и задать аргументы `user` и `password` (или определить соответствующие переменные окружения `DYNAMIX_USER` и `DYNAMIX_PASSWORD`). +3. С использованием авторизационного провайдера, работающего по протоколу _Oauth2_oidc_. Для авторизации в этом режиме при инициализации Terrafrom провайдера DYNAMIX необходимо указать параметры `oauth2_url` и `controller_url`, а также Application ID & Application secret, _имя пользователя и пароль_, соответствующих пользователю, от имени которого будет осуществляться управление облачными ресурсами в текущей сессии, и _имя домена_. В процессе проверки предоставленных Application ID & Application secret и пары _имя пользователя-пароль_ модуль получает от авторизационного провайдера токен (JSON Web Token, JWT), который затем используется для доступа к указанному контроллеру DYNAMIX. Для авторизации по данному варианту, при инициализации Terraform провайдера DYNAMIX следует установить аргумент `authenticator=bvs`, задать аргументы `app_id` и `app_secret` (или определить соответствующие переменные окружения `DYNAMIX_APP_ID` и `DYNAMIX_APP_SECRET`), `bvs_user` и `bvs_password` (или определить соответствующие переменные окружения `DYNAMIX_BVS_USER` и `DYNAMIX_BVS_PASSWORD`), а также указать `domain` (или определить соответствующие переменные окружения `DYNAMIX_DOMAIN`). + +После успешной авторизации пользователь (или приложение-клиент) получает доступ к ресурсам, находящимся под управлением соответствующего DYNAMIX контроллера. Доступ предоставляется в рамках подписчиков (_account_), с которыми ассоциирован данный пользователь (_user_), и в соответствии с присвоенными ему ролями. + +## Пользовательская и административная группы API +Пользовательская группа API - группа API платформы DYNAMIX, которая позволяет выполнять операции с платформой с правами обычного пользователя. Покрывает большую часть задач. +Административная группа API - группа API платформы DYNAMIX, которая позволяет выполнять операции с платформой с расширенными правами. Данные права подразумевают расширенный перечень операций над ресурсами, расширенный перечень ресурсов, расширенную информацию. Требуются права администратора для взаимодействия с этой группой API. +В Terraform провайдере DYNAMIX версии 1.2.2 реализованы функции только пользовательской группа API. diff --git a/wiki/1.2.2/04.-Начало-работы-с-terraform-провайдером-DYNAMIX.md b/wiki/1.2.2/04.-Начало-работы-с-terraform-провайдером-DYNAMIX.md new file mode 100644 index 0000000..edaa664 --- /dev/null +++ b/wiki/1.2.2/04.-Начало-работы-с-terraform-провайдером-DYNAMIX.md @@ -0,0 +1,6 @@ +Данный раздел описывает: +- Системные требования +- Установку провайдера +- Инициализацию провайдера +- Переключение режима работы между разными группами API +- Получение gid/grid_id площадки diff --git a/wiki/1.2.2/04.01-Установка-Terraform-провайдера-DYNAMIX.md b/wiki/1.2.2/04.01-Установка-Terraform-провайдера-DYNAMIX.md new file mode 100644 index 0000000..3227f17 --- /dev/null +++ b/wiki/1.2.2/04.01-Установка-Terraform-провайдера-DYNAMIX.md @@ -0,0 +1,147 @@ +## Системные требования + +Для запуска провайдера вам потребуется машина, на которой установлен Terraform. + +Кроме того, в связи с тем, что начиная с версии 0.12 Terraform изменил алгоритм поиска и инициализации локальных провайдеров, настройка данного провайдера для работы с Terraform 0.12 или более новыми версиями потребует выполнения ряда дополнительных действий. Подробнее см. [8.3 Настройка локального провайдера для работы с новыми версиями Terraform](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/08.-Полезные-советы.md#8-3-настройка-локального-провайдера-для-работы-с-новыми-версиями-terraform). + +## Установка +В релизном архиве находятся скрипты-инсталляторы. +Чтобы выполнить установку, необходимо: +1. Перейти по адресу: https://repository.basistech.ru/BASIS/terraform-provider-dynamix/releases +2. Выбрать необходимую версию провайдера подходящую под операционную систему. +3. Скачать архив. +4. Распаковать архив. +5. Выполнить скрипт установщика, `install.sh` или `install.bat` для Windows.
+*Для запуска `install.sh` не забудьте изменить права доступа к файлу* +```bash +chmod u+x install.sh +``` +6. Дождаться сообщения об успешной установке. Установщик выведет актуальный блок конфигурации провайдера, скопируйте его +```bash +terraform { + required_providers { + dynamix = { + version = "1.2.2" + source = "basistech.ru/tf/dynamix" + } + } +} +``` +7. После этого, создайте файл `main.tf` в рабочей директории, которая может находится в любом удобном для пользователя месте. +В данном примере, рабочая директория с файлом main.tf находится по пути: +```bash +~/work/tfdir/main.tf +``` +8. Вставьте в `main.tf` блок конфигурации провайдера, который был выведен на экран установщиком: +```terraform +terraform { + required_providers { + dynamix = { + version = "1.2.2" + source = "basistech.ru/tf/dynamix" + } + } +} +``` +9. Добавьте в файл блок с инициализацией провайдера. +```terraform +provider "dynamix" { + authenticator = "decs3o" + controller_url = "https://mr4.digitalenergy.online" + oauth2_url = "https://sso.digitalenergy.online" + allow_unverified_ssl = true +} +``` + +10. В консоли выполните команду +```bash +terraform init +``` + +11. В случае успешной установки, Terraform инициализирует провайдер и будет готов к дальнейшей работе. + +## Установка из релизов +Terraform провайдер DYNAMIX имеет скомпилированные релизные версии, которые расположены по адресу: [Релизы](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/releases). +Чтобы выполнить установку из релиза, необходимо: +1. Перейти по адресу: https://repository.basistech.ru/BASIS/terraform-provider-dynamix/releases +2. Выбрать необходимую версию провайдера подходящую под операционную систему. +3. Скачать архив. +4. Распаковать архив. +5. Полученный файл (в директории `bin/`) необходимо поместить: +Linux: +```bash +~/.terraform.d/plugins/${host_name}/${namespace}/${type}/${version}/${target} +``` +Windows: +```powershell +%APPDATA%\terraform.d\plugins\${host_name}\${namespace}\${type}\${version}\${target} +``` +Где: +- host_name - имя хоста, держателя провайдера, например, basis +- namespace - пространство имен хоста, например decort +- type - тип провайдера, может совпадать с пространством имен, например, decort +- version - версия провайдера, например 4.3.0 +- target - архитектура операционной системы, например windows_amd64 + +В примере ниже используется путь до провайдера на машине с ОС Linux: + +```bash +~/.terraform.d/plugins/basistech.ru/tf/dynamix/1.2.2/linux_amd64/tf-provider + ^ ^ ^ ^ ^ ^ + host_name | | | | | | + | | | | | + namespace | | | | | + | | | | + type | | | | + | | | + version | | | + | | + target | | + | + исполняемый файл | +``` + +6. После этого, создайте файл `main.tf` в рабочей директории, которая может находится в любом удобном для пользователя месте. +В данном примере, рабочая директория с файлом main.tf находится по пути: +```bash +~/work/tfdir/main.tf +``` +7. Добавьте в `main.tf` следующий блок +```terraform +terraform { + required_providers { + dynamix = { + source = "basistech.ru/tf/dynamix" + version = "1.2.2" + } + } +} +``` +В поле `version` указывается версия провайдера. +
+**ВНИМАНИЕ: Версии в блоке и в пути к исполняемому файлу провайдера должны совпадать!** + +В поле `source` помещается путь до репозитория с версией вида: + +```bash +${host_name}/${namespace}/${type} +``` + +**ВНИМАНИЕ: Версии в блоке и в пути к исполняемому файлу провайдера должны совпадать!** + +8. Добавьте в файл блок с инициализацией провайдера. +```terraform +provider "dynamix" { + authenticator = "decs3o" + controller_url = "https://mr4.digitalenergy.online" + oauth2_url = "https://sso.digitalenergy.online" + allow_unverified_ssl = true +} +``` + +9. В консоли выполните команду +```bash +terraform init +``` + +10. В случае успешной установки, Terraform инициализирует провайдер и будет готов к дальнейшей работе. diff --git a/wiki/1.2.2/04.02-Инициализация-Terraform-провайдера-DYNAMIX.md b/wiki/1.2.2/04.02-Инициализация-Terraform-провайдера-DYNAMIX.md new file mode 100644 index 0000000..79605da --- /dev/null +++ b/wiki/1.2.2/04.02-Инициализация-Terraform-провайдера-DYNAMIX.md @@ -0,0 +1,64 @@ +## Список аргументов для инициализации +Перед началом использования любой Terraform провайдер должен быть инициализирован. + +В процессе инициализации Terraform провайдера DYNAMIX проверяется корректность переданных аргументов и выполняется авторизация в указанном контроллере облачной инфраструктуры. Подробнее о способах авторизации в платформе DYNAMIX смотри соответствующий [раздел](https://repository.basistech.ru/BASIS/terraform-provider-dynamix/src/branch/main/wiki/1.2.2/03.-Обзор-облачной-платформы-DYNAMIX.md#способы-авторизации). + +При инициализации Terraform провайдера DYNAMIX используются следующие аргументы: + +| Аргумент | Переменная окружения | Описание | +| --- | --- | --- | +| allow_unverified_ssl | - | Если данный аргумент явно установлен в `true`, то провайдер **не будет** проверять SSL сертификаты при взаимодействии с авторизационным сервисом OAuth2 и контроллером облачной платформы.
Отключение проверок может быть полезным при работе в доверенной среде, использующей самоподписанные SSL сертификаты. Однако, так как отключение проверок несёт потенциальные риски безопасности, данную настройку следует использовать с осторожностью.
Разрешённые значения: `false` (значение по умолчанию) и `true`. | +| app_id | DYNAMIX_APP_ID | Идентификатор приложения (клиента) для авторизации в контроллере облачной платформы в режиме `decs3o` или `bvs`.
Аргументы `app_id` и `app_secret` являются обязательными для режимов авторизации `authenticator=decs3o` и `authenticator=bvs`.
Если `app_id` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_APP_ID`. | +| app_secret | DYNAMIX_APP_SECRET | Секретный код приложения (клиента) для авторизации в контроллере облачной платформы в режиме `decs3o` или `bvs`.
Аргументы `app_id` и `app_secret` являются обязательными для режимов авторизации `authenticator=decs3o` и `authenticator=bvs`.
Если `app_secret` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_APP_SECRET`. | +| authenticator | - | Режим авторизации при подключении к контроллеру облачной платформы.
Доступные режимы: `decs3o`, `legacy`, `jwt` или `bvs`.
Данный аргумент является обязательным. | +| bvs_user | DYNAMIX_BVS_USER | Имя пользователя для авторизации в контроллере облачной платформы в режиме `bvs`.
Аргументы `bvs_password` и `bvs_user` являются обязательными для режима авторизации `authenticator=bvs`.
Если `bvs_user` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_BVS_USER`. | +| bvs_password | DYNAMIX_BVS_PASSWORD | Пароль пользователя для авторизации в контроллере облачной платформы в режиме `bvs`.
Аргументы `bvs_user` и `bvs_password` являются обязательными для режима авторизации `authenticator=bvs`.
Если `bvs_password` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_BVS_PASSWORD`. | +| domain | DYNAMIX_DOMAIN | Имя домена в контроллере облачной платформы в режиме `bvs`.
Данный аргумент является обязательным.
Если `domain` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_DOMAIN`. | +| controller_url | DYNAMIX_CONTROLLER_URL | URL контроллера облачной платформы, через который будет осуществляться управление облачными ресурсами.
Данный аргумент является обязательным. | +| jwt | DYNAMIX_JWT | JSON Web Token (JWT), который используется для авторизации в контроллере облачной платформы в режиме `jwt`.
Данный аргумент является обязательным для режима авторизации `authenticator=jwt`.
Если `jwt` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_JWT` | +| oauth2_url | DYNAMIX_OAUTH2_URL | URL авторизационного сервиса OAuth2, который используется для управления доступом пользователей (или программных клиентов) к контроллеру облачной платформы.
Данный аргумент является обязательным для режимов авторизации `authenticator=decs3o`, `authenticator=bvs` и `authenticator=jwt`.
Если `oauth2_url` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_OAUTH2_URL` | +| password | DYNAMIX_PASSWORD | Пароль для авторизации в контроллере облачной платформы в режиме `legacy`.
Аргументы `password` и `user` являются обязательными для режима авторизации `authenticator=legacy`.
Если `password` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_PASSWORD`. | +| user | DYNAMIX_USER | Имя пользователя для авторизации в контроллере облачной платформы в режиме `legacy`.
Аргументы `user` и `password` являются обязательными для режима авторизации `authenticator=legacy`.
Если `user` не задан в tf-файле, то провайдер будет использовать значение из переменной окружения `DYNAMIX_USER`. | + +## Пример инициализации в режиме авторизации `decs3o` +Пример инициализации Terraform провайдера DYNAMIX: +```terraform +provider "dynamix" { + authenticator = "decs3o" + controller_url = "https://ctrl.decort.online" + oauth2_url = "https://oauth2.decort.online:7777" +} +``` + +В данном примере используется режим авторизации `decs3o`. + +Как отмечено выше, в данном режиме требуется указать аргументы `app_id` и `app_secret`, идентифицирующие пользователя (или приложение-клиент), от лица которого будут выполняться дальнейшие действия. Однако, так как данная информация является конфиденциальной (по сути, она эквивалентна паре _имя пользователя : пароль_), то в общем случае заносить такого рода данные в tf-файл не следует. Рекомендуется определять в среде запуска Terraform переменные окружения `DYNAMIX_APP_ID` и `DYNAMIX_APP_SECRET`, из которых провайдер извлечёт нужные данные. Приведенный пример подразумевает, что нужная информация будет получена из этих переменных окружения. + +Пользователь, от лица которого Terrafrom будет выполнять действия в облачной платформе, должен заранее создать пару _Application ID_ и _Application Secret_ в авторизационном приложении OAuth2. Именно эти значения, а также URL авторизационного приложения Oauth2, должны присваиваться аргументам `app_id`, `app_secret` и `oauth2_url` соответственно для успешной инициализации провайдера. + +Также обратите внимание на формат задания аргументов `controller_url` и `oauth2_url`. В общем случае они должны содержать идентификатор протокола (_https://_) и сетевой порт, если он отличается от порта по умолчанию (в примере для авторизационного сервиса OAuth2 указан порт _7777_). Эту информацию вы можете узнать у администратора вашей облачной инфраструктуры. + +## Пример инициализации в режиме авторизации `bvs` +Пример инициализации Terraform провайдера DYNAMIX: +```terraform +provider "dynamix" { + authenticator = "bvs" + controller_url = "https://delta.qa.loc" + oauth2_url = "https://bvs-delta.qa.loc:8443" + app_id = "delta" + app_secret = "" + bvs_password = "" + bvs_user = "" + domain = "dynamix" +} +``` + +В данном примере используется режим авторизации `bvs`. + +Как отмечено выше, в данном режиме требуется указать аргументы `app_id` - идентификатор площадки - delta, alpha, poc, etc. Можно найти на странице администратора по следующему пути: вкладка безопасность - клиентские сервисы - наименование площадки. `app_secret` - пароль площадки. Можно найти на странице администратора по следующему пути: вкладка безопасность - клиентские сервисы - наименование площадки (символ i) - поле "Пароль". Однако, так как данная информация является конфиденциальной, то в общем случае заносить такого рода данные в tf-файл не следует. Рекомендуется определять в среде запуска Terraform переменные окружения `DYNAMIX_APP_ID` и `DYNAMIX_APP_SECRET`, из которых провайдер извлечёт нужные данные. Приведенный пример подразумевает, что нужная информация будет получена из этих переменных окружения. +Также обязательными аргументами являются: `bvs_user` - имя пользователя, `bvs_password` - пароль пользователя. Рекомендуется не заносить их в tf-файл, а определять в среде запуска Terraform переменные окружения `DYNAMIX_BVS_USER` и `DYNAMIX_BVS_PASSWORD`, из которых провайдер извлечёт нужные данные. Приведенный пример подразумевает, что нужная информация будет получена из этих переменных окружения. +Домен для подключения `domain` - указывается наименование площадки. Данный аргумент является обязательным. Рекомендуется не заносить его в tf-файл, а определять в среде запуска Terraform переменную окружения `DYNAMIX_DOMAIN`, из которой провайдер извлечёт нужные данные. Приведенный пример подразумевает, что нужная информация будет получена из этих переменных окружения. + +Пользователь, от лица которого Terrafrom будет выполнять действия в облачной платформе, должен заранее получить от администратора _Application ID_ и _Application Secret_, _bvs user_ и _bvs password_, а также _domain_. А также осуществить первичный вход на платформу посредством браузера. + +Также обратите внимание на формат задания аргументов `controller_url` и `oauth2_url`. В общем случае они должны содержать идентификатор протокола (_https://_) и сетевой порт, если он отличается от порта по умолчанию (в примере для авторизационного сервиса OAuth2 указан порт _8443_). Эту информацию вы можете узнать у администратора вашей облачной инфраструктуры. diff --git a/wiki/1.2.2/04.03-Переключение-между-группами-API.md b/wiki/1.2.2/04.03-Переключение-между-группами-API.md new file mode 100644 index 0000000..79557f2 --- /dev/null +++ b/wiki/1.2.2/04.03-Переключение-между-группами-API.md @@ -0,0 +1,38 @@ +Так как платформа DYNAMIX предоставляет для работы две группы API, то terraform провайдер позволяет свободно переключать режимы работы между этими группами. +По умолчанию стоит пользовательская группа API. Ее можно сменить на административную группу. +Если прав у пользователя будет достаточно, то запрос будет выполнен, если нет, то будет ошибка: +```bash +Permission denied +``` +Которая говорит о том, что прав недостаточно. Тогда для выполнения операции обратитесь к администратору платформы. +Установка режима взаимодействия с группами API осуществляется через установку переменной окружения _DECORT_ADMIN_MODE_. +Для более подробного описания возможностей каждой группы API см. соответствующий раздел. + +## Переключение режима работы в Windows +Используйте сл. команду: +```Powershell +$Env:DECORT_ADMIN_MODE=1 +``` +Для отключения: +```Powershell +$Env:DECORT_ADMIN_MODE=0 +``` +## Переключение режима работы в Linux +Используйте сл. команду: +```bash +DECORT_ADMIN_MODE=1 +``` +или +```bash +export DECORT_ADMIN_MODE=1 +``` +Для отключения: +```bash +DECORT_ADMIN_MODE=0 +``` +или +```bash +export DECORT_ADMIN_MODE=0 +``` +**ОБРАТИТЕ ВНИМАНИЕ** +Переменные окружения создаются для терминальной сессии. В сл. раз их придется задавать еще раз, если требуется режим, отличный от пользовательского. diff --git a/wiki/1.2.2/04.04-Получение-gid-или-grid_id.md b/wiki/1.2.2/04.04-Получение-gid-или-grid_id.md new file mode 100644 index 0000000..1161149 --- /dev/null +++ b/wiki/1.2.2/04.04-Получение-gid-или-grid_id.md @@ -0,0 +1,17 @@ +Платформа может располагаться на нескольких площадках(grid). +Такие площадки имеют свой id. +Для создания некоторых ресурсов требуется ввести grid_id или gid площадки. +Получение gid различается для пользовательского и административного API. + +## Получение gid для пользовательского API +Для получения gid с помощью пользовательского API, необходимо получить информацию из _data_source_ функции _dynamix_locations_list_, как указано ниже: +```terraform +data "dynamix_locations_list" "ll" { + +} + +output "test" { + value = data.dynamix_locations_list.ll +} +``` +В файл состояния будет сохранен результат, где можно посмотреть доступные для работы площадки. diff --git a/wiki/1.2.2/04.05-Сборка-terraform-провайдера-в-образ.md b/wiki/1.2.2/04.05-Сборка-terraform-провайдера-в-образ.md new file mode 100644 index 0000000..c0a96bf --- /dev/null +++ b/wiki/1.2.2/04.05-Сборка-terraform-провайдера-в-образ.md @@ -0,0 +1,43 @@ +Образ приложения - современный способ запуска приложений. Образ приложения представляет собой контейнер, в который входит ОС и необходимые для работы приложения пакеты. +Способов создать образ приложения довольно много, для этого существуют программы контейнеризации: +- Docker +- Podman +- и другие +Образ представляет собой "зафиксированную" версию приложения, что означает, что никакие изменения в приложения внесены быть не могут. Так же означает то, что приложение не может создавать побочные файлы при работе. +Контейнер - это запущенный экземпляр образа. То есть, один образ может порождать множество контейнеров, каждый из которых будет включать в себя отдельный экземпляр приложения. +Одно из преимуществ работы приложения в контейнере - кроссплатформенность. Это преимущество обуславливается тем, что образ приложения уже включает в себя все необходимое для успешной работы приложения, в том числе операционную систему. Поэтому, пользователю достаточно установить на вычислительной машине программу, которая обеспечивает работу с образами приложений. + +## Docker +Docker является одной из самых популярных программ для работы с образами. +Docker позволяет: +- Создавать образы +- Запускать контейнеры +- Управлять контейнерами +- Управлять образами +Скачать и установить Docker можно по ссылке https://docs.docker.com/get-docker/ + +## Сборка terraform провайдера +### Требования: +- Docker +- git +- Компилятор языка GO += make +### Установка необходимых программ +1. Компилятор языка GO можно скачать и установить по ссылке: https://go.dev/dl/ +2. Docker можно скачать и установить по ссылке выше. +3. Git можно скачать и установить по ссылке: https://git-scm.com/ +4. Программа make входит в пакет установленных программ для ОС Linux. Для Windows можно воспользоваться инструкцией со stack overflow: https://stackoverflow.com/questions/32127524/how-to-install-and-use-make-in-windows +### Порядок действий +1. Склонировать репозиторий с провайдером: +```bash +git clone https://repository.basistech.ru/BASIS/terraform-provider-dynamix +``` +2. Перейти в директорию со скачанным кодом: +```bash +cd terraform-provider-dynamix +``` +3. Выполнить команду: +```bash +make image +``` +В результате выполнения данной последовательности, будет создан docker образ, который содержит в себе приложение terraform, terraform провайдер. diff --git a/wiki/1.2.2/05.-Работа-с-terraform.md b/wiki/1.2.2/05.-Работа-с-terraform.md new file mode 100644 index 0000000..675f2ea --- /dev/null +++ b/wiki/1.2.2/05.-Работа-с-terraform.md @@ -0,0 +1,4 @@ +Раздел описывает некоторые практики работы с terraform, которые могут быть полезны пользователю. +Раздел включает в себя следующие статьи: +- Импортирование ресурсов +- Работа с таймаутами diff --git a/wiki/1.2.2/05.01-Импортирование-ресурсов.md b/wiki/1.2.2/05.01-Импортирование-ресурсов.md new file mode 100644 index 0000000..6983e22 --- /dev/null +++ b/wiki/1.2.2/05.01-Импортирование-ресурсов.md @@ -0,0 +1,75 @@ +Импортирование ресурсов в terraform позволяет привести в соответствие состояние terraform (.tfstate) к состоянию ресурса в платформе. +Необходимость такого приведения возникает в нескольких случаях: +- Ресурс был создан через портал платформы, работа продолжается через terraform провайдер, +- Ресурс был создан через terraform провайдер, однако был изменен через портал платформы, +- Ресурс был создан через terraform провайдер, однако был изменен другим пользователем через terraform провайдер, +- И так далее + +Такие расхождения в состоянии ресурсов нередки, путей их решения несколько: +- Использовать импортирование ресурсов, +- Использовать общие файлы состояний ресурсов, к которым будут иметь доступ все участники, занятые в работе с платформой. +В текущем разделе рассматривается первый вариант. + +## Импортирование ресурсов +Импортирование ресурсов позволяет совершить запрос к платформе, чтобы сформировать файл состояния. +Чтобы совершить импортирование ресурсов необходимо ввести сл. команду: +```bash +terraform import . +``` +## Пример +Предположим, что у нас ресурс, описывающий диск: +```terraform +resource "dynamix_disk" "disk" { + account_id = 121212 + gid = 3333 + disk_name = "mySuperDisk" + size_max = 100500 +} +``` +Если запустить команду: +```bash +terraform apply +``` +То у нас будет создан новый диск. +Но, такой диск уже есть на площадке и мы хотели бы сформировать .tfstate для этого ресурса. +Поэтому, для начала, необходимо получить список дисков: +```terraform +data "dynamix_disk_list" "dl"{ + +} +output "test" { + value = data.dynamix_disk_list.dl +} +``` +В полученных данных необходимо найти требуемый диск, получить его id - параметр disk_id. Пусть это будет - 777777 +Теперь можно выполнить импортирование: +```bash +terraform import dynamix_disk.disk 777777 +``` +Команда должна успешно завершиться, появиться файл состояний, который позволит манипулировать ресурсом. + +## Ошибки при импортировании +При импортировании ресурса может возникнуть сл. ошибка: +```bash +Error: : required field is not set +``` +Где - наименование поля. +Ошибка возникает в том случае, если в описании ресурса отсутствует обязательное поле. +Например: +```terraform +resource "dynamix_disk" "disk" { + account_id = 121212 + gid = 3333 + size_max = 100500 +} +``` +В приведенном выше описании отсутствует поле disk_name, поэтому, при попытке импортирования возникнет ошибка. +Для ее устранения, необходимо выполнить запрос на получение списка дисков, найти недостающее поле, после чего добавить его в описание ресурса. +После этого повторить попытку импортирования. + +## Общий алгоритм устранения ошибок +1. Выполнить запрос импортирования +2. В случае ошибки - внести недостающие поля. +3. Повторить п.1. + + diff --git a/wiki/1.2.2/05.02-Работа-с-таймаутами.md b/wiki/1.2.2/05.02-Работа-с-таймаутами.md new file mode 100644 index 0000000..ecffe69 --- /dev/null +++ b/wiki/1.2.2/05.02-Работа-с-таймаутами.md @@ -0,0 +1,100 @@ +Terraform провайдер DYNAMIX поддерживает тонкую настройку таймаутов выполнения запросов к платформе. Таймауты необходимы для определения максимального времени выполнения запроса. При превышении этого времени соединение рвется и запрос считается невыполненным. +Таймауты применяются при работе с _resource_ функциями провайдера. _Data source_ функции по-умолчанию имеют таймаут в 20 минут и изменяться не может. + +## Стандартные таймауты terraform +| Операция | Время | Описание | +| --- | --- | --- | +| create | 20 минут | Создание ресурса | +| read | 20 минут | Чтение ресурса | +| update | 20 минут | Обновление ресурса | +| delete | 20 минут | Удаление ресурса | +| default | 20 минут | Значение по умолчанию. Устанавливает значение для всех операций | + +## Стандартные таймауты провайдера DYNAMIX +В провайдере DYNAMIX таймауты переопределены для того, чтобы уменьшить нагрузку на платформу. +| Операция | Время | Описание | +| --- | --- | --- | +| create | 10 минут | Создание ресурса | +| read | 5 минут | Чтение ресурса | +| update | 5 минут | Обновление ресурса | +| delete | 5 минут | Удаление ресурса | +| default | 5 минут | Значение по умолчанию. Устанавливает значение для всех операций | + +## Установка таймаутов +Все таймауты можно установить самостоятельно для каждого ресурса. +Для этого используется блок _timeouts_, который имеется в каждом ресурсе провайдера. +Пример: +```terraform +resource "dynamix_res" "res_name" { + timeouts { + create = "10m" + update = "1m" + delete = "2m" + read = "7m" + #default = "15m" + } +} +``` +Где: +- create - операция создания ресурса +- read - операция чтения ресурса +- update - операция обновления ресурса +- delete - операция удаления ресурса +- default - установит заданное время для всех операций +## Формат установления времени +Как видно из примера выше, провайдер принимает на вход строку вида: +``` +"" +``` +Где: +- time-num - число +- time-val - сокращенная запись значения временного отрезка. + +Таблица с временными отрезками: + +| Отрезок | Значение | +| --- | --- | +| n | наносекунда | +| ms | миллисекунда | +| s | секунда | +| m | минута | +| h | час | + +Примеры: +``` +"10m" +"1s" +"1h10m" +``` +И так далее + +## Работа с таймером через .tf-файл +В .tf-файле, в блоке ресурса можно задавать таймауты для операций над ресурсом, однако, при работе с таймаутом, следует помнить о правиле: +__В случае изменения таймаутов в .tf-файле, операции с новыми таймаутами будут производиться только после apply/plan/destroy__ +То есть, если изменить таймауты и выполнить операцию, то она выполнится со старыми таймаутами, а сл. операция уже будет выполнена с новыми таймаутами. +Это объясняется тем, что значения таймаутов считываются из файла состояний .tfstate при выполнении операции, и новые значения таймаутов попадут туда только при успешно выполненной операции. + +## Ошибки при работе с таймаутом +### context deadline exceeded +Если время таймаута слишком короткое, то можно получить сл. ошибку: +``` +context deadline exceeded +``` +Которая говорит, что было выполнено прерывание работы программы из-за истечения времени на операцию. +Для исправления можно увеличить размер окна таймаута и выполнить успешный запрос (например, с помощью терминала), чтобы новое значения таймаутов было добавлено в .tfstate. В противном случае, файл состояния придется править в ручную, либо удалить его и импортировать ресурс для формирования .tfstate. + +### 504 ошибка +Данная ошибка говорит о том, что сервер принудительно разорвал соединения из-за истечения времени на ответ. +В случае получения данной ошибки, обратитесь в службу технической поддержки. + +## Работа с таймаутами через терминал +Сл. команда выполнит операцию terraform с заданным таймаутом: +```bash +timeout