From 6da49113a1cecc21bc6f24e8b741adacf5511c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Thu, 16 Jul 2026 18:02:41 +0400 Subject: [PATCH] chore: stop tracking generated/ (already in .gitignore) --- generated/dev/go/100_openwhisk_resource.go | 382 -------- .../dev/go/109_zones_v2_record_resource.go | 362 ------- generated/dev/go/109_zones_v2_resource.go | 484 --------- generated/dev/go/110_dnszone_resource.go | 495 ---------- generated/dev/go/111_dnsrecord_resource.go | 546 ----------- generated/dev/go/112_tenant_resource.go | 372 ------- generated/dev/go/113_vc_complex_resource.go | 563 ----------- .../dev/go/115_mariadb_database_resource.go | 343 ------- generated/dev/go/115_mariadb_resource.go | 447 --------- generated/dev/go/115_mariadb_user_resource.go | 367 ------- generated/dev/go/116_kafka_resource.go | 389 -------- generated/dev/go/116_kafka_topic_resource.go | 364 ------- generated/dev/go/116_kafka_user_resource.go | 373 ------- generated/dev/go/119_akhq_resource.go | 554 ----------- .../go/120_clickhouse_database_resource.go | 343 ------- generated/dev/go/120_clickhouse_resource.go | 487 --------- .../dev/go/120_clickhouse_user_resource.go | 359 ------- generated/dev/go/12_s3_resource.go | 421 -------- generated/dev/go/12_s3_sub_user_resource.go | 358 ------- generated/dev/go/13_s3bucket_resource.go | 478 --------- .../148_vc_mgmt_sthutrval_cluster_resource.go | 419 -------- generated/dev/go/149_valo_tenant_resource.go | 431 -------- .../go/150_k8s_sthutrval_cluster_resource.go | 429 -------- ...150_k8s_sthutrval_cluster_user_resource.go | 343 ------- .../dev/go/151_k8s_openbao_kv_resource.go | 343 ------- .../dev/go/151_k8s_openbao_policy_resource.go | 355 ------- generated/dev/go/151_k8s_openbao_resource.go | 468 --------- .../go/151_k8s_openbao_secrets_resource.go | 368 ------- generated/dev/go/153_nifi_resource.go | 474 --------- generated/dev/go/163_llm_ai_resource.go | 345 ------- .../dev/go/163_llm_ai_secrets_resource.go | 401 -------- generated/dev/go/163_llm_ai_team_resource.go | 365 ------- generated/dev/go/175_k8s_go_resource.go | 422 -------- generated/dev/go/19_vc_org_resource.go | 391 -------- generated/dev/go/19_vc_org_user_resource.go | 343 ------- generated/dev/go/1_dummy_resource.go | 582 ----------- generated/dev/go/20_vc_org_saas_resource.go | 348 ------- generated/dev/go/21_vc_vdc_resource.go | 573 ----------- generated/dev/go/22_vc_nsxt_resource.go | 614 ------------ generated/dev/go/23_vc_vm_resource.go | 581 ----------- generated/dev/go/24_vc_nat_resource.go | 322 ------ generated/dev/go/25_vcexternalip_resource.go | 604 ------------ generated/dev/go/26_vapp_resource.go | 571 ----------- generated/dev/go/27_vc_vm_v2_resource.go | 350 ------- generated/dev/go/28_vc_vm_v3_resource.go | 697 ------------- generated/dev/go/29_vc_vdc_group_resource.go | 449 --------- .../dev/go/29_vc_vdc_group_vdc_resource.go | 326 ------- generated/dev/go/2_template_resource.go | 397 -------- generated/dev/go/32_vmpostgre_resource.go | 514 ---------- .../dev/go/50_nextcloud_backup_resource.go | 344 ------- generated/dev/go/50_nextcloud_resource.go | 414 -------- generated/dev/go/81_superset_resource.go | 447 --------- generated/dev/go/82_harbor_resource.go | 482 --------- generated/dev/go/86_k8s_velero_resource.go | 401 -------- generated/dev/go/87_k8svalkey_resource.go | 423 -------- .../dev/go/87_k8svalkey_user_resource.go | 358 ------- .../dev/go/88_k8s_ziti_controller_resource.go | 431 -------- generated/dev/go/89_flask_resource.go | 449 --------- .../dev/go/90_postgres_backup_resource.go | 326 ------- .../dev/go/90_postgres_database_resource.go | 358 ------- generated/dev/go/90_postgres_resource.go | 499 ---------- generated/dev/go/90_postgres_user_resource.go | 367 ------- generated/dev/go/91_redis_resource.go | 393 -------- generated/dev/go/92_mongodb_resource.go | 389 -------- generated/dev/go/92_mongodb_user_resource.go | 364 ------- generated/dev/go/93_rabbitmq_resource.go | 474 --------- generated/dev/go/94_lucee_resource.go | 449 --------- generated/dev/go/95_nodejs_resource.go | 449 --------- generated/dev/go/96_pgadmin_resource.go | 515 ---------- generated/dev/go/97_nodered_resource.go | 431 -------- generated/dev/go/98_http_resource.go | 509 ---------- generated/dev/go/99_gitea_resource.go | 579 ----------- generated/dev/go/registry.go | 81 -- .../dev/resources_yaml/100_openwhisk.yaml | 54 - .../dev/resources_yaml/109_zones_v2.yaml | 281 ------ generated/dev/resources_yaml/110_dnszone.yaml | 97 -- .../dev/resources_yaml/111_dnsrecord.yaml | 105 -- generated/dev/resources_yaml/112_tenant.yaml | 60 -- .../dev/resources_yaml/113_vc_complex.yaml | 158 --- generated/dev/resources_yaml/115_mariadb.yaml | 458 --------- generated/dev/resources_yaml/116_kafka.yaml | 335 ------- generated/dev/resources_yaml/119_akhq.yaml | 107 -- .../dev/resources_yaml/120_clickhouse.yaml | 570 ----------- generated/dev/resources_yaml/12_s3.yaml | 130 --- generated/dev/resources_yaml/13_s3bucket.yaml | 90 -- .../148_vc_mgmt_sthutrval_cluster.yaml | 176 ---- .../dev/resources_yaml/149_valo_tenant.yaml | 249 ----- .../150_k8s_sthutrval_cluster.yaml | 371 ------- .../dev/resources_yaml/151_k8s_openbao.yaml | 389 -------- generated/dev/resources_yaml/153_nifi.yaml | 119 --- generated/dev/resources_yaml/163_llm_ai.yaml | 232 ----- generated/dev/resources_yaml/175_k8s_go.yaml | 225 ----- generated/dev/resources_yaml/19_vc_org.yaml | 140 --- generated/dev/resources_yaml/1_dummy.yaml | 339 ------- .../dev/resources_yaml/20_vc_org_saas.yaml | 60 -- generated/dev/resources_yaml/21_vc_vdc.yaml | 169 ---- generated/dev/resources_yaml/22_vc_nsxt.yaml | 139 --- generated/dev/resources_yaml/23_vc_vm.yaml | 153 --- generated/dev/resources_yaml/24_vc_nat.yaml | 28 - .../dev/resources_yaml/25_vcexternalip.yaml | 165 ---- generated/dev/resources_yaml/26_vapp.yaml | 79 -- generated/dev/resources_yaml/27_vc_vm_v2.yaml | 71 -- generated/dev/resources_yaml/28_vc_vm_v3.yaml | 278 ------ .../dev/resources_yaml/29_vc_vdc_group.yaml | 74 -- generated/dev/resources_yaml/2_template.yaml | 222 ----- .../dev/resources_yaml/32_vmpostgre.yaml | 71 -- .../dev/resources_yaml/50_nextcloud.yaml | 485 --------- generated/dev/resources_yaml/81_superset.yaml | 107 -- generated/dev/resources_yaml/82_harbor.yaml | 129 --- .../dev/resources_yaml/86_k8s_velero.yaml | 197 ---- .../dev/resources_yaml/87_k8svalkey.yaml | 275 ------ .../88_k8s_ziti_controller.yaml | 93 -- generated/dev/resources_yaml/89_flask.yaml | 240 ----- generated/dev/resources_yaml/90_postgres.yaml | 920 ------------------ generated/dev/resources_yaml/91_redis.yaml | 220 ----- generated/dev/resources_yaml/92_mongodb.yaml | 239 ----- generated/dev/resources_yaml/93_rabbitmq.yaml | 168 ---- generated/dev/resources_yaml/94_lucee.yaml | 248 ----- generated/dev/resources_yaml/95_nodejs.yaml | 248 ----- generated/dev/resources_yaml/96_pgadmin.yaml | 164 ---- generated/dev/resources_yaml/97_nodered.yaml | 95 -- generated/dev/resources_yaml/98_http.yaml | 166 ---- generated/dev/resources_yaml/99_gitea.yaml | 169 ---- generated/dev/resources_yaml/embed.go | 8 - generated/prod/go/100_openwhisk_resource.go | 355 ------- .../prod/go/109_zones_v2_record_resource.go | 366 ------- generated/prod/go/109_zones_v2_resource.go | 484 --------- generated/prod/go/110_dnszone_resource.go | 495 ---------- generated/prod/go/111_dnsrecord_resource.go | 546 ----------- generated/prod/go/112_tenant_resource.go | 372 ------- generated/prod/go/113_vc_complex_resource.go | 563 ----------- .../prod/go/114_gitea_complex_resource.go | 322 ------ .../prod/go/115_mariadb_database_resource.go | 343 ------- generated/prod/go/115_mariadb_resource.go | 447 --------- .../prod/go/115_mariadb_user_resource.go | 367 ------- generated/prod/go/116_kafka_resource.go | 389 -------- generated/prod/go/116_kafka_topic_resource.go | 364 ------- generated/prod/go/116_kafka_user_resource.go | 373 ------- generated/prod/go/117_nifi_resource.go | 533 ---------- generated/prod/go/119_akhq_resource.go | 544 ----------- .../go/120_clickhouse_database_resource.go | 343 ------- generated/prod/go/120_clickhouse_resource.go | 487 --------- .../prod/go/120_clickhouse_user_resource.go | 359 ------- generated/prod/go/12_s3_resource.go | 421 -------- generated/prod/go/12_s3_sub_user_resource.go | 358 ------- generated/prod/go/13_s3bucket_resource.go | 478 --------- .../148_vc_mgmt_sthutrval_cluster_resource.go | 419 -------- generated/prod/go/149_valo_tenant_resource.go | 431 -------- .../go/150_k8s_sthutrval_cluster_resource.go | 429 -------- ...150_k8s_sthutrval_cluster_user_resource.go | 343 ------- generated/prod/go/163_llm_ai_resource.go | 345 ------- .../prod/go/163_llm_ai_secrets_resource.go | 389 -------- generated/prod/go/163_llm_ai_team_resource.go | 365 ------- generated/prod/go/19_vc_org_resource.go | 391 -------- generated/prod/go/19_vc_org_user_resource.go | 343 ------- generated/prod/go/1_dummy_resource.go | 593 ----------- generated/prod/go/20_vc_org_saas_resource.go | 348 ------- generated/prod/go/21_vc_vdc_resource.go | 573 ----------- generated/prod/go/22_vc_nsxt_resource.go | 614 ------------ generated/prod/go/23_vc_vm_resource.go | 581 ----------- generated/prod/go/25_vcexternalip_resource.go | 604 ------------ generated/prod/go/26_vapp_resource.go | 571 ----------- generated/prod/go/27_vc_vm_v2_resource.go | 350 ------- generated/prod/go/28_vc_vm_v3_resource.go | 697 ------------- generated/prod/go/29_vc_vdc_group_resource.go | 449 --------- .../prod/go/29_vc_vdc_group_vdc_resource.go | 326 ------- generated/prod/go/2_template_resource.go | 397 -------- .../prod/go/50_nextcloud_backup_resource.go | 344 ------- generated/prod/go/50_nextcloud_resource.go | 414 -------- generated/prod/go/81_superset_resource.go | 436 --------- generated/prod/go/82_harbor_resource.go | 458 --------- generated/prod/go/86_k8s_velero_resource.go | 402 -------- .../go/88_k8s_ziti_controller_resource.go | 420 -------- generated/prod/go/89_flask_resource.go | 449 --------- .../prod/go/90_postgres_backup_resource.go | 326 ------- .../prod/go/90_postgres_database_resource.go | 358 ------- generated/prod/go/90_postgres_resource.go | 475 --------- .../prod/go/90_postgres_user_resource.go | 358 ------- generated/prod/go/91_redis_resource.go | 393 -------- generated/prod/go/92_mongodb_resource.go | 495 ---------- generated/prod/go/92_mongodb_user_resource.go | 364 ------- generated/prod/go/93_rabbitmq_resource.go | 452 --------- generated/prod/go/94_lucee_resource.go | 449 --------- generated/prod/go/95_nodejs_resource.go | 449 --------- generated/prod/go/96_pgadmin_resource.go | 504 ---------- generated/prod/go/97_nodered_resource.go | 420 -------- generated/prod/go/98_http_resource.go | 498 ---------- generated/prod/go/99_gitea_resource.go | 579 ----------- generated/prod/go/registry.go | 73 -- .../prod/resources_yaml/100_openwhisk.yaml | 48 - .../prod/resources_yaml/109_zones_v2.yaml | 264 ----- .../prod/resources_yaml/110_dnszone.yaml | 97 -- .../prod/resources_yaml/111_dnsrecord.yaml | 105 -- generated/prod/resources_yaml/112_tenant.yaml | 60 -- .../prod/resources_yaml/113_vc_complex.yaml | 158 --- .../resources_yaml/114_gitea_complex.yaml | 28 - .../prod/resources_yaml/115_mariadb.yaml | 457 --------- generated/prod/resources_yaml/116_kafka.yaml | 334 ------- generated/prod/resources_yaml/117_nifi.yaml | 81 -- generated/prod/resources_yaml/119_akhq.yaml | 107 -- .../prod/resources_yaml/120_clickhouse.yaml | 569 ----------- generated/prod/resources_yaml/12_s3.yaml | 130 --- .../prod/resources_yaml/13_s3bucket.yaml | 90 -- .../148_vc_mgmt_sthutrval_cluster.yaml | 176 ---- .../prod/resources_yaml/149_valo_tenant.yaml | 249 ----- .../150_k8s_sthutrval_cluster.yaml | 371 ------- generated/prod/resources_yaml/163_llm_ai.yaml | 189 ---- generated/prod/resources_yaml/19_vc_org.yaml | 140 --- generated/prod/resources_yaml/1_dummy.yaml | 336 ------- .../prod/resources_yaml/20_vc_org_saas.yaml | 60 -- generated/prod/resources_yaml/21_vc_vdc.yaml | 169 ---- generated/prod/resources_yaml/22_vc_nsxt.yaml | 139 --- generated/prod/resources_yaml/23_vc_vm.yaml | 153 --- .../prod/resources_yaml/25_vcexternalip.yaml | 165 ---- generated/prod/resources_yaml/26_vapp.yaml | 74 -- .../prod/resources_yaml/27_vc_vm_v2.yaml | 71 -- .../prod/resources_yaml/28_vc_vm_v3.yaml | 278 ------ .../prod/resources_yaml/29_vc_vdc_group.yaml | 74 -- generated/prod/resources_yaml/2_template.yaml | 221 ----- .../prod/resources_yaml/50_nextcloud.yaml | 483 --------- .../prod/resources_yaml/81_superset.yaml | 107 -- generated/prod/resources_yaml/82_harbor.yaml | 98 -- .../prod/resources_yaml/86_k8s_velero.yaml | 196 ---- .../88_k8s_ziti_controller.yaml | 93 -- generated/prod/resources_yaml/89_flask.yaml | 238 ----- .../prod/resources_yaml/90_postgres.yaml | 816 ---------------- generated/prod/resources_yaml/91_redis.yaml | 219 ----- generated/prod/resources_yaml/92_mongodb.yaml | 194 ---- .../prod/resources_yaml/93_rabbitmq.yaml | 167 ---- generated/prod/resources_yaml/94_lucee.yaml | 246 ----- generated/prod/resources_yaml/95_nodejs.yaml | 246 ----- generated/prod/resources_yaml/96_pgadmin.yaml | 164 ---- generated/prod/resources_yaml/97_nodered.yaml | 95 -- generated/prod/resources_yaml/98_http.yaml | 161 --- generated/prod/resources_yaml/99_gitea.yaml | 169 ---- generated/prod/resources_yaml/embed.go | 8 - generated/test/go/100_openwhisk_resource.go | 322 ------ .../test/go/109_zones_v2_record_resource.go | 366 ------- generated/test/go/109_zones_v2_resource.go | 484 --------- generated/test/go/110_dnszone_resource.go | 495 ---------- generated/test/go/111_dnsrecord_resource.go | 546 ----------- generated/test/go/112_tenant_resource.go | 372 ------- generated/test/go/113_vc_complex_resource.go | 563 ----------- .../test/go/114_gitea_complex_resource.go | 322 ------ .../test/go/115_mariadb_database_resource.go | 343 ------- generated/test/go/115_mariadb_resource.go | 447 --------- .../test/go/115_mariadb_user_resource.go | 367 ------- generated/test/go/116_kafka_resource.go | 389 -------- generated/test/go/116_kafka_topic_resource.go | 364 ------- generated/test/go/116_kafka_user_resource.go | 373 ------- generated/test/go/117_nifi_resource.go | 533 ---------- generated/test/go/119_akhq_resource.go | 554 ----------- .../go/120_clickhouse_database_resource.go | 343 ------- generated/test/go/120_clickhouse_resource.go | 487 --------- .../test/go/120_clickhouse_user_resource.go | 359 ------- generated/test/go/12_s3_resource.go | 421 -------- generated/test/go/12_s3_sub_user_resource.go | 358 ------- generated/test/go/13_s3bucket_resource.go | 478 --------- .../148_vc_mgmt_sthutrval_cluster_resource.go | 419 -------- generated/test/go/149_valo_tenant_resource.go | 431 -------- .../go/150_k8s_sthutrval_cluster_resource.go | 429 -------- ...150_k8s_sthutrval_cluster_user_resource.go | 343 ------- .../test/go/151_k8s_openbao_kv_resource.go | 343 ------- .../go/151_k8s_openbao_policy_resource.go | 355 ------- generated/test/go/151_k8s_openbao_resource.go | 468 --------- .../go/151_k8s_openbao_secrets_resource.go | 368 ------- generated/test/go/163_llm_ai_resource.go | 345 ------- .../test/go/163_llm_ai_secrets_resource.go | 389 -------- generated/test/go/163_llm_ai_team_resource.go | 365 ------- generated/test/go/19_vc_org_resource.go | 391 -------- generated/test/go/19_vc_org_user_resource.go | 343 ------- generated/test/go/1_dummy_resource.go | 582 ----------- generated/test/go/20_vc_org_saas_resource.go | 348 ------- generated/test/go/21_vc_vdc_resource.go | 573 ----------- generated/test/go/22_vc_nsxt_resource.go | 614 ------------ generated/test/go/23_vc_vm_resource.go | 581 ----------- generated/test/go/25_vcexternalip_resource.go | 604 ------------ generated/test/go/26_vapp_resource.go | 571 ----------- generated/test/go/27_vc_vm_v2_resource.go | 350 ------- generated/test/go/28_vc_vm_v3_resource.go | 697 ------------- generated/test/go/29_vc_vdc_group_resource.go | 449 --------- .../test/go/29_vc_vdc_group_vdc_resource.go | 326 ------- generated/test/go/2_template_resource.go | 397 -------- generated/test/go/32_vmpostgre_resource.go | 322 ------ .../test/go/50_nextcloud_backup_resource.go | 344 ------- generated/test/go/50_nextcloud_resource.go | 414 -------- generated/test/go/81_superset_resource.go | 447 --------- generated/test/go/82_harbor_resource.go | 482 --------- generated/test/go/86_k8s_velero_resource.go | 401 -------- .../go/88_k8s_ziti_controller_resource.go | 431 -------- generated/test/go/89_flask_resource.go | 449 --------- .../test/go/90_postgres_backup_resource.go | 326 ------- .../test/go/90_postgres_database_resource.go | 358 ------- generated/test/go/90_postgres_resource.go | 475 --------- .../test/go/90_postgres_user_resource.go | 358 ------- generated/test/go/91_redis_resource.go | 393 -------- generated/test/go/92_mongodb_resource.go | 389 -------- generated/test/go/92_mongodb_user_resource.go | 364 ------- generated/test/go/93_rabbitmq_resource.go | 462 --------- generated/test/go/94_lucee_resource.go | 449 --------- generated/test/go/95_nodejs_resource.go | 449 --------- generated/test/go/96_pgadmin_resource.go | 515 ---------- generated/test/go/97_nodered_resource.go | 431 -------- generated/test/go/98_http_resource.go | 509 ---------- generated/test/go/99_gitea_resource.go | 579 ----------- generated/test/go/registry.go | 78 -- .../test/resources_yaml/100_openwhisk.yaml | 28 - .../test/resources_yaml/109_zones_v2.yaml | 263 ----- .../test/resources_yaml/110_dnszone.yaml | 97 -- .../test/resources_yaml/111_dnsrecord.yaml | 105 -- generated/test/resources_yaml/112_tenant.yaml | 60 -- .../test/resources_yaml/113_vc_complex.yaml | 158 --- .../resources_yaml/114_gitea_complex.yaml | 28 - .../test/resources_yaml/115_mariadb.yaml | 460 --------- generated/test/resources_yaml/116_kafka.yaml | 337 ------- generated/test/resources_yaml/117_nifi.yaml | 81 -- generated/test/resources_yaml/119_akhq.yaml | 109 --- .../test/resources_yaml/120_clickhouse.yaml | 572 ----------- generated/test/resources_yaml/12_s3.yaml | 130 --- .../test/resources_yaml/13_s3bucket.yaml | 90 -- .../148_vc_mgmt_sthutrval_cluster.yaml | 176 ---- .../test/resources_yaml/149_valo_tenant.yaml | 249 ----- .../150_k8s_sthutrval_cluster.yaml | 371 ------- .../test/resources_yaml/151_k8s_openbao.yaml | 391 -------- generated/test/resources_yaml/163_llm_ai.yaml | 189 ---- generated/test/resources_yaml/19_vc_org.yaml | 140 --- generated/test/resources_yaml/1_dummy.yaml | 339 ------- .../test/resources_yaml/20_vc_org_saas.yaml | 60 -- generated/test/resources_yaml/21_vc_vdc.yaml | 169 ---- generated/test/resources_yaml/22_vc_nsxt.yaml | 139 --- generated/test/resources_yaml/23_vc_vm.yaml | 153 --- .../test/resources_yaml/25_vcexternalip.yaml | 165 ---- generated/test/resources_yaml/26_vapp.yaml | 79 -- .../test/resources_yaml/27_vc_vm_v2.yaml | 71 -- .../test/resources_yaml/28_vc_vm_v3.yaml | 278 ------ .../test/resources_yaml/29_vc_vdc_group.yaml | 74 -- generated/test/resources_yaml/2_template.yaml | 224 ----- .../test/resources_yaml/32_vmpostgre.yaml | 28 - .../test/resources_yaml/50_nextcloud.yaml | 489 ---------- .../test/resources_yaml/81_superset.yaml | 109 --- generated/test/resources_yaml/82_harbor.yaml | 126 --- .../test/resources_yaml/86_k8s_velero.yaml | 199 ---- .../88_k8s_ziti_controller.yaml | 95 -- generated/test/resources_yaml/89_flask.yaml | 244 ----- .../test/resources_yaml/90_postgres.yaml | 822 ---------------- generated/test/resources_yaml/91_redis.yaml | 222 ----- generated/test/resources_yaml/92_mongodb.yaml | 241 ----- .../test/resources_yaml/93_rabbitmq.yaml | 169 ---- generated/test/resources_yaml/94_lucee.yaml | 252 ----- generated/test/resources_yaml/95_nodejs.yaml | 252 ----- generated/test/resources_yaml/96_pgadmin.yaml | 168 ---- generated/test/resources_yaml/97_nodered.yaml | 97 -- generated/test/resources_yaml/98_http.yaml | 170 ---- generated/test/resources_yaml/99_gitea.yaml | 169 ---- generated/test/resources_yaml/embed.go | 8 - 355 files changed, 118122 deletions(-) delete mode 100644 generated/dev/go/100_openwhisk_resource.go delete mode 100644 generated/dev/go/109_zones_v2_record_resource.go delete mode 100644 generated/dev/go/109_zones_v2_resource.go delete mode 100644 generated/dev/go/110_dnszone_resource.go delete mode 100644 generated/dev/go/111_dnsrecord_resource.go delete mode 100644 generated/dev/go/112_tenant_resource.go delete mode 100644 generated/dev/go/113_vc_complex_resource.go delete mode 100644 generated/dev/go/115_mariadb_database_resource.go delete mode 100644 generated/dev/go/115_mariadb_resource.go delete mode 100644 generated/dev/go/115_mariadb_user_resource.go delete mode 100644 generated/dev/go/116_kafka_resource.go delete mode 100644 generated/dev/go/116_kafka_topic_resource.go delete mode 100644 generated/dev/go/116_kafka_user_resource.go delete mode 100644 generated/dev/go/119_akhq_resource.go delete mode 100644 generated/dev/go/120_clickhouse_database_resource.go delete mode 100644 generated/dev/go/120_clickhouse_resource.go delete mode 100644 generated/dev/go/120_clickhouse_user_resource.go delete mode 100644 generated/dev/go/12_s3_resource.go delete mode 100644 generated/dev/go/12_s3_sub_user_resource.go delete mode 100644 generated/dev/go/13_s3bucket_resource.go delete mode 100644 generated/dev/go/148_vc_mgmt_sthutrval_cluster_resource.go delete mode 100644 generated/dev/go/149_valo_tenant_resource.go delete mode 100644 generated/dev/go/150_k8s_sthutrval_cluster_resource.go delete mode 100644 generated/dev/go/150_k8s_sthutrval_cluster_user_resource.go delete mode 100644 generated/dev/go/151_k8s_openbao_kv_resource.go delete mode 100644 generated/dev/go/151_k8s_openbao_policy_resource.go delete mode 100644 generated/dev/go/151_k8s_openbao_resource.go delete mode 100644 generated/dev/go/151_k8s_openbao_secrets_resource.go delete mode 100644 generated/dev/go/153_nifi_resource.go delete mode 100644 generated/dev/go/163_llm_ai_resource.go delete mode 100644 generated/dev/go/163_llm_ai_secrets_resource.go delete mode 100644 generated/dev/go/163_llm_ai_team_resource.go delete mode 100644 generated/dev/go/175_k8s_go_resource.go delete mode 100644 generated/dev/go/19_vc_org_resource.go delete mode 100644 generated/dev/go/19_vc_org_user_resource.go delete mode 100644 generated/dev/go/1_dummy_resource.go delete mode 100644 generated/dev/go/20_vc_org_saas_resource.go delete mode 100644 generated/dev/go/21_vc_vdc_resource.go delete mode 100644 generated/dev/go/22_vc_nsxt_resource.go delete mode 100644 generated/dev/go/23_vc_vm_resource.go delete mode 100644 generated/dev/go/24_vc_nat_resource.go delete mode 100644 generated/dev/go/25_vcexternalip_resource.go delete mode 100644 generated/dev/go/26_vapp_resource.go delete mode 100644 generated/dev/go/27_vc_vm_v2_resource.go delete mode 100644 generated/dev/go/28_vc_vm_v3_resource.go delete mode 100644 generated/dev/go/29_vc_vdc_group_resource.go delete mode 100644 generated/dev/go/29_vc_vdc_group_vdc_resource.go delete mode 100644 generated/dev/go/2_template_resource.go delete mode 100644 generated/dev/go/32_vmpostgre_resource.go delete mode 100644 generated/dev/go/50_nextcloud_backup_resource.go delete mode 100644 generated/dev/go/50_nextcloud_resource.go delete mode 100644 generated/dev/go/81_superset_resource.go delete mode 100644 generated/dev/go/82_harbor_resource.go delete mode 100644 generated/dev/go/86_k8s_velero_resource.go delete mode 100644 generated/dev/go/87_k8svalkey_resource.go delete mode 100644 generated/dev/go/87_k8svalkey_user_resource.go delete mode 100644 generated/dev/go/88_k8s_ziti_controller_resource.go delete mode 100644 generated/dev/go/89_flask_resource.go delete mode 100644 generated/dev/go/90_postgres_backup_resource.go delete mode 100644 generated/dev/go/90_postgres_database_resource.go delete mode 100644 generated/dev/go/90_postgres_resource.go delete mode 100644 generated/dev/go/90_postgres_user_resource.go delete mode 100644 generated/dev/go/91_redis_resource.go delete mode 100644 generated/dev/go/92_mongodb_resource.go delete mode 100644 generated/dev/go/92_mongodb_user_resource.go delete mode 100644 generated/dev/go/93_rabbitmq_resource.go delete mode 100644 generated/dev/go/94_lucee_resource.go delete mode 100644 generated/dev/go/95_nodejs_resource.go delete mode 100644 generated/dev/go/96_pgadmin_resource.go delete mode 100644 generated/dev/go/97_nodered_resource.go delete mode 100644 generated/dev/go/98_http_resource.go delete mode 100644 generated/dev/go/99_gitea_resource.go delete mode 100644 generated/dev/go/registry.go delete mode 100644 generated/dev/resources_yaml/100_openwhisk.yaml delete mode 100644 generated/dev/resources_yaml/109_zones_v2.yaml delete mode 100644 generated/dev/resources_yaml/110_dnszone.yaml delete mode 100644 generated/dev/resources_yaml/111_dnsrecord.yaml delete mode 100644 generated/dev/resources_yaml/112_tenant.yaml delete mode 100644 generated/dev/resources_yaml/113_vc_complex.yaml delete mode 100644 generated/dev/resources_yaml/115_mariadb.yaml delete mode 100644 generated/dev/resources_yaml/116_kafka.yaml delete mode 100644 generated/dev/resources_yaml/119_akhq.yaml delete mode 100644 generated/dev/resources_yaml/120_clickhouse.yaml delete mode 100644 generated/dev/resources_yaml/12_s3.yaml delete mode 100644 generated/dev/resources_yaml/13_s3bucket.yaml delete mode 100644 generated/dev/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml delete mode 100644 generated/dev/resources_yaml/149_valo_tenant.yaml delete mode 100644 generated/dev/resources_yaml/150_k8s_sthutrval_cluster.yaml delete mode 100644 generated/dev/resources_yaml/151_k8s_openbao.yaml delete mode 100644 generated/dev/resources_yaml/153_nifi.yaml delete mode 100644 generated/dev/resources_yaml/163_llm_ai.yaml delete mode 100644 generated/dev/resources_yaml/175_k8s_go.yaml delete mode 100644 generated/dev/resources_yaml/19_vc_org.yaml delete mode 100644 generated/dev/resources_yaml/1_dummy.yaml delete mode 100644 generated/dev/resources_yaml/20_vc_org_saas.yaml delete mode 100644 generated/dev/resources_yaml/21_vc_vdc.yaml delete mode 100644 generated/dev/resources_yaml/22_vc_nsxt.yaml delete mode 100644 generated/dev/resources_yaml/23_vc_vm.yaml delete mode 100644 generated/dev/resources_yaml/24_vc_nat.yaml delete mode 100644 generated/dev/resources_yaml/25_vcexternalip.yaml delete mode 100644 generated/dev/resources_yaml/26_vapp.yaml delete mode 100644 generated/dev/resources_yaml/27_vc_vm_v2.yaml delete mode 100644 generated/dev/resources_yaml/28_vc_vm_v3.yaml delete mode 100644 generated/dev/resources_yaml/29_vc_vdc_group.yaml delete mode 100644 generated/dev/resources_yaml/2_template.yaml delete mode 100644 generated/dev/resources_yaml/32_vmpostgre.yaml delete mode 100644 generated/dev/resources_yaml/50_nextcloud.yaml delete mode 100644 generated/dev/resources_yaml/81_superset.yaml delete mode 100644 generated/dev/resources_yaml/82_harbor.yaml delete mode 100644 generated/dev/resources_yaml/86_k8s_velero.yaml delete mode 100644 generated/dev/resources_yaml/87_k8svalkey.yaml delete mode 100644 generated/dev/resources_yaml/88_k8s_ziti_controller.yaml delete mode 100644 generated/dev/resources_yaml/89_flask.yaml delete mode 100644 generated/dev/resources_yaml/90_postgres.yaml delete mode 100644 generated/dev/resources_yaml/91_redis.yaml delete mode 100644 generated/dev/resources_yaml/92_mongodb.yaml delete mode 100644 generated/dev/resources_yaml/93_rabbitmq.yaml delete mode 100644 generated/dev/resources_yaml/94_lucee.yaml delete mode 100644 generated/dev/resources_yaml/95_nodejs.yaml delete mode 100644 generated/dev/resources_yaml/96_pgadmin.yaml delete mode 100644 generated/dev/resources_yaml/97_nodered.yaml delete mode 100644 generated/dev/resources_yaml/98_http.yaml delete mode 100644 generated/dev/resources_yaml/99_gitea.yaml delete mode 100644 generated/dev/resources_yaml/embed.go delete mode 100644 generated/prod/go/100_openwhisk_resource.go delete mode 100644 generated/prod/go/109_zones_v2_record_resource.go delete mode 100644 generated/prod/go/109_zones_v2_resource.go delete mode 100644 generated/prod/go/110_dnszone_resource.go delete mode 100644 generated/prod/go/111_dnsrecord_resource.go delete mode 100644 generated/prod/go/112_tenant_resource.go delete mode 100644 generated/prod/go/113_vc_complex_resource.go delete mode 100644 generated/prod/go/114_gitea_complex_resource.go delete mode 100644 generated/prod/go/115_mariadb_database_resource.go delete mode 100644 generated/prod/go/115_mariadb_resource.go delete mode 100644 generated/prod/go/115_mariadb_user_resource.go delete mode 100644 generated/prod/go/116_kafka_resource.go delete mode 100644 generated/prod/go/116_kafka_topic_resource.go delete mode 100644 generated/prod/go/116_kafka_user_resource.go delete mode 100644 generated/prod/go/117_nifi_resource.go delete mode 100644 generated/prod/go/119_akhq_resource.go delete mode 100644 generated/prod/go/120_clickhouse_database_resource.go delete mode 100644 generated/prod/go/120_clickhouse_resource.go delete mode 100644 generated/prod/go/120_clickhouse_user_resource.go delete mode 100644 generated/prod/go/12_s3_resource.go delete mode 100644 generated/prod/go/12_s3_sub_user_resource.go delete mode 100644 generated/prod/go/13_s3bucket_resource.go delete mode 100644 generated/prod/go/148_vc_mgmt_sthutrval_cluster_resource.go delete mode 100644 generated/prod/go/149_valo_tenant_resource.go delete mode 100644 generated/prod/go/150_k8s_sthutrval_cluster_resource.go delete mode 100644 generated/prod/go/150_k8s_sthutrval_cluster_user_resource.go delete mode 100644 generated/prod/go/163_llm_ai_resource.go delete mode 100644 generated/prod/go/163_llm_ai_secrets_resource.go delete mode 100644 generated/prod/go/163_llm_ai_team_resource.go delete mode 100644 generated/prod/go/19_vc_org_resource.go delete mode 100644 generated/prod/go/19_vc_org_user_resource.go delete mode 100644 generated/prod/go/1_dummy_resource.go delete mode 100644 generated/prod/go/20_vc_org_saas_resource.go delete mode 100644 generated/prod/go/21_vc_vdc_resource.go delete mode 100644 generated/prod/go/22_vc_nsxt_resource.go delete mode 100644 generated/prod/go/23_vc_vm_resource.go delete mode 100644 generated/prod/go/25_vcexternalip_resource.go delete mode 100644 generated/prod/go/26_vapp_resource.go delete mode 100644 generated/prod/go/27_vc_vm_v2_resource.go delete mode 100644 generated/prod/go/28_vc_vm_v3_resource.go delete mode 100644 generated/prod/go/29_vc_vdc_group_resource.go delete mode 100644 generated/prod/go/29_vc_vdc_group_vdc_resource.go delete mode 100644 generated/prod/go/2_template_resource.go delete mode 100644 generated/prod/go/50_nextcloud_backup_resource.go delete mode 100644 generated/prod/go/50_nextcloud_resource.go delete mode 100644 generated/prod/go/81_superset_resource.go delete mode 100644 generated/prod/go/82_harbor_resource.go delete mode 100644 generated/prod/go/86_k8s_velero_resource.go delete mode 100644 generated/prod/go/88_k8s_ziti_controller_resource.go delete mode 100644 generated/prod/go/89_flask_resource.go delete mode 100644 generated/prod/go/90_postgres_backup_resource.go delete mode 100644 generated/prod/go/90_postgres_database_resource.go delete mode 100644 generated/prod/go/90_postgres_resource.go delete mode 100644 generated/prod/go/90_postgres_user_resource.go delete mode 100644 generated/prod/go/91_redis_resource.go delete mode 100644 generated/prod/go/92_mongodb_resource.go delete mode 100644 generated/prod/go/92_mongodb_user_resource.go delete mode 100644 generated/prod/go/93_rabbitmq_resource.go delete mode 100644 generated/prod/go/94_lucee_resource.go delete mode 100644 generated/prod/go/95_nodejs_resource.go delete mode 100644 generated/prod/go/96_pgadmin_resource.go delete mode 100644 generated/prod/go/97_nodered_resource.go delete mode 100644 generated/prod/go/98_http_resource.go delete mode 100644 generated/prod/go/99_gitea_resource.go delete mode 100644 generated/prod/go/registry.go delete mode 100644 generated/prod/resources_yaml/100_openwhisk.yaml delete mode 100644 generated/prod/resources_yaml/109_zones_v2.yaml delete mode 100644 generated/prod/resources_yaml/110_dnszone.yaml delete mode 100644 generated/prod/resources_yaml/111_dnsrecord.yaml delete mode 100644 generated/prod/resources_yaml/112_tenant.yaml delete mode 100644 generated/prod/resources_yaml/113_vc_complex.yaml delete mode 100644 generated/prod/resources_yaml/114_gitea_complex.yaml delete mode 100644 generated/prod/resources_yaml/115_mariadb.yaml delete mode 100644 generated/prod/resources_yaml/116_kafka.yaml delete mode 100644 generated/prod/resources_yaml/117_nifi.yaml delete mode 100644 generated/prod/resources_yaml/119_akhq.yaml delete mode 100644 generated/prod/resources_yaml/120_clickhouse.yaml delete mode 100644 generated/prod/resources_yaml/12_s3.yaml delete mode 100644 generated/prod/resources_yaml/13_s3bucket.yaml delete mode 100644 generated/prod/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml delete mode 100644 generated/prod/resources_yaml/149_valo_tenant.yaml delete mode 100644 generated/prod/resources_yaml/150_k8s_sthutrval_cluster.yaml delete mode 100644 generated/prod/resources_yaml/163_llm_ai.yaml delete mode 100644 generated/prod/resources_yaml/19_vc_org.yaml delete mode 100644 generated/prod/resources_yaml/1_dummy.yaml delete mode 100644 generated/prod/resources_yaml/20_vc_org_saas.yaml delete mode 100644 generated/prod/resources_yaml/21_vc_vdc.yaml delete mode 100644 generated/prod/resources_yaml/22_vc_nsxt.yaml delete mode 100644 generated/prod/resources_yaml/23_vc_vm.yaml delete mode 100644 generated/prod/resources_yaml/25_vcexternalip.yaml delete mode 100644 generated/prod/resources_yaml/26_vapp.yaml delete mode 100644 generated/prod/resources_yaml/27_vc_vm_v2.yaml delete mode 100644 generated/prod/resources_yaml/28_vc_vm_v3.yaml delete mode 100644 generated/prod/resources_yaml/29_vc_vdc_group.yaml delete mode 100644 generated/prod/resources_yaml/2_template.yaml delete mode 100644 generated/prod/resources_yaml/50_nextcloud.yaml delete mode 100644 generated/prod/resources_yaml/81_superset.yaml delete mode 100644 generated/prod/resources_yaml/82_harbor.yaml delete mode 100644 generated/prod/resources_yaml/86_k8s_velero.yaml delete mode 100644 generated/prod/resources_yaml/88_k8s_ziti_controller.yaml delete mode 100644 generated/prod/resources_yaml/89_flask.yaml delete mode 100644 generated/prod/resources_yaml/90_postgres.yaml delete mode 100644 generated/prod/resources_yaml/91_redis.yaml delete mode 100644 generated/prod/resources_yaml/92_mongodb.yaml delete mode 100644 generated/prod/resources_yaml/93_rabbitmq.yaml delete mode 100644 generated/prod/resources_yaml/94_lucee.yaml delete mode 100644 generated/prod/resources_yaml/95_nodejs.yaml delete mode 100644 generated/prod/resources_yaml/96_pgadmin.yaml delete mode 100644 generated/prod/resources_yaml/97_nodered.yaml delete mode 100644 generated/prod/resources_yaml/98_http.yaml delete mode 100644 generated/prod/resources_yaml/99_gitea.yaml delete mode 100644 generated/prod/resources_yaml/embed.go delete mode 100644 generated/test/go/100_openwhisk_resource.go delete mode 100644 generated/test/go/109_zones_v2_record_resource.go delete mode 100644 generated/test/go/109_zones_v2_resource.go delete mode 100644 generated/test/go/110_dnszone_resource.go delete mode 100644 generated/test/go/111_dnsrecord_resource.go delete mode 100644 generated/test/go/112_tenant_resource.go delete mode 100644 generated/test/go/113_vc_complex_resource.go delete mode 100644 generated/test/go/114_gitea_complex_resource.go delete mode 100644 generated/test/go/115_mariadb_database_resource.go delete mode 100644 generated/test/go/115_mariadb_resource.go delete mode 100644 generated/test/go/115_mariadb_user_resource.go delete mode 100644 generated/test/go/116_kafka_resource.go delete mode 100644 generated/test/go/116_kafka_topic_resource.go delete mode 100644 generated/test/go/116_kafka_user_resource.go delete mode 100644 generated/test/go/117_nifi_resource.go delete mode 100644 generated/test/go/119_akhq_resource.go delete mode 100644 generated/test/go/120_clickhouse_database_resource.go delete mode 100644 generated/test/go/120_clickhouse_resource.go delete mode 100644 generated/test/go/120_clickhouse_user_resource.go delete mode 100644 generated/test/go/12_s3_resource.go delete mode 100644 generated/test/go/12_s3_sub_user_resource.go delete mode 100644 generated/test/go/13_s3bucket_resource.go delete mode 100644 generated/test/go/148_vc_mgmt_sthutrval_cluster_resource.go delete mode 100644 generated/test/go/149_valo_tenant_resource.go delete mode 100644 generated/test/go/150_k8s_sthutrval_cluster_resource.go delete mode 100644 generated/test/go/150_k8s_sthutrval_cluster_user_resource.go delete mode 100644 generated/test/go/151_k8s_openbao_kv_resource.go delete mode 100644 generated/test/go/151_k8s_openbao_policy_resource.go delete mode 100644 generated/test/go/151_k8s_openbao_resource.go delete mode 100644 generated/test/go/151_k8s_openbao_secrets_resource.go delete mode 100644 generated/test/go/163_llm_ai_resource.go delete mode 100644 generated/test/go/163_llm_ai_secrets_resource.go delete mode 100644 generated/test/go/163_llm_ai_team_resource.go delete mode 100644 generated/test/go/19_vc_org_resource.go delete mode 100644 generated/test/go/19_vc_org_user_resource.go delete mode 100644 generated/test/go/1_dummy_resource.go delete mode 100644 generated/test/go/20_vc_org_saas_resource.go delete mode 100644 generated/test/go/21_vc_vdc_resource.go delete mode 100644 generated/test/go/22_vc_nsxt_resource.go delete mode 100644 generated/test/go/23_vc_vm_resource.go delete mode 100644 generated/test/go/25_vcexternalip_resource.go delete mode 100644 generated/test/go/26_vapp_resource.go delete mode 100644 generated/test/go/27_vc_vm_v2_resource.go delete mode 100644 generated/test/go/28_vc_vm_v3_resource.go delete mode 100644 generated/test/go/29_vc_vdc_group_resource.go delete mode 100644 generated/test/go/29_vc_vdc_group_vdc_resource.go delete mode 100644 generated/test/go/2_template_resource.go delete mode 100644 generated/test/go/32_vmpostgre_resource.go delete mode 100644 generated/test/go/50_nextcloud_backup_resource.go delete mode 100644 generated/test/go/50_nextcloud_resource.go delete mode 100644 generated/test/go/81_superset_resource.go delete mode 100644 generated/test/go/82_harbor_resource.go delete mode 100644 generated/test/go/86_k8s_velero_resource.go delete mode 100644 generated/test/go/88_k8s_ziti_controller_resource.go delete mode 100644 generated/test/go/89_flask_resource.go delete mode 100644 generated/test/go/90_postgres_backup_resource.go delete mode 100644 generated/test/go/90_postgres_database_resource.go delete mode 100644 generated/test/go/90_postgres_resource.go delete mode 100644 generated/test/go/90_postgres_user_resource.go delete mode 100644 generated/test/go/91_redis_resource.go delete mode 100644 generated/test/go/92_mongodb_resource.go delete mode 100644 generated/test/go/92_mongodb_user_resource.go delete mode 100644 generated/test/go/93_rabbitmq_resource.go delete mode 100644 generated/test/go/94_lucee_resource.go delete mode 100644 generated/test/go/95_nodejs_resource.go delete mode 100644 generated/test/go/96_pgadmin_resource.go delete mode 100644 generated/test/go/97_nodered_resource.go delete mode 100644 generated/test/go/98_http_resource.go delete mode 100644 generated/test/go/99_gitea_resource.go delete mode 100644 generated/test/go/registry.go delete mode 100644 generated/test/resources_yaml/100_openwhisk.yaml delete mode 100644 generated/test/resources_yaml/109_zones_v2.yaml delete mode 100644 generated/test/resources_yaml/110_dnszone.yaml delete mode 100644 generated/test/resources_yaml/111_dnsrecord.yaml delete mode 100644 generated/test/resources_yaml/112_tenant.yaml delete mode 100644 generated/test/resources_yaml/113_vc_complex.yaml delete mode 100644 generated/test/resources_yaml/114_gitea_complex.yaml delete mode 100644 generated/test/resources_yaml/115_mariadb.yaml delete mode 100644 generated/test/resources_yaml/116_kafka.yaml delete mode 100644 generated/test/resources_yaml/117_nifi.yaml delete mode 100644 generated/test/resources_yaml/119_akhq.yaml delete mode 100644 generated/test/resources_yaml/120_clickhouse.yaml delete mode 100644 generated/test/resources_yaml/12_s3.yaml delete mode 100644 generated/test/resources_yaml/13_s3bucket.yaml delete mode 100644 generated/test/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml delete mode 100644 generated/test/resources_yaml/149_valo_tenant.yaml delete mode 100644 generated/test/resources_yaml/150_k8s_sthutrval_cluster.yaml delete mode 100644 generated/test/resources_yaml/151_k8s_openbao.yaml delete mode 100644 generated/test/resources_yaml/163_llm_ai.yaml delete mode 100644 generated/test/resources_yaml/19_vc_org.yaml delete mode 100644 generated/test/resources_yaml/1_dummy.yaml delete mode 100644 generated/test/resources_yaml/20_vc_org_saas.yaml delete mode 100644 generated/test/resources_yaml/21_vc_vdc.yaml delete mode 100644 generated/test/resources_yaml/22_vc_nsxt.yaml delete mode 100644 generated/test/resources_yaml/23_vc_vm.yaml delete mode 100644 generated/test/resources_yaml/25_vcexternalip.yaml delete mode 100644 generated/test/resources_yaml/26_vapp.yaml delete mode 100644 generated/test/resources_yaml/27_vc_vm_v2.yaml delete mode 100644 generated/test/resources_yaml/28_vc_vm_v3.yaml delete mode 100644 generated/test/resources_yaml/29_vc_vdc_group.yaml delete mode 100644 generated/test/resources_yaml/2_template.yaml delete mode 100644 generated/test/resources_yaml/32_vmpostgre.yaml delete mode 100644 generated/test/resources_yaml/50_nextcloud.yaml delete mode 100644 generated/test/resources_yaml/81_superset.yaml delete mode 100644 generated/test/resources_yaml/82_harbor.yaml delete mode 100644 generated/test/resources_yaml/86_k8s_velero.yaml delete mode 100644 generated/test/resources_yaml/88_k8s_ziti_controller.yaml delete mode 100644 generated/test/resources_yaml/89_flask.yaml delete mode 100644 generated/test/resources_yaml/90_postgres.yaml delete mode 100644 generated/test/resources_yaml/91_redis.yaml delete mode 100644 generated/test/resources_yaml/92_mongodb.yaml delete mode 100644 generated/test/resources_yaml/93_rabbitmq.yaml delete mode 100644 generated/test/resources_yaml/94_lucee.yaml delete mode 100644 generated/test/resources_yaml/95_nodejs.yaml delete mode 100644 generated/test/resources_yaml/96_pgadmin.yaml delete mode 100644 generated/test/resources_yaml/97_nodered.yaml delete mode 100644 generated/test/resources_yaml/98_http.yaml delete mode 100644 generated/test/resources_yaml/99_gitea.yaml delete mode 100644 generated/test/resources_yaml/embed.go diff --git a/generated/dev/go/100_openwhisk_resource.go b/generated/dev/go/100_openwhisk_resource.go deleted file mode 100644 index c4cbf39..0000000 --- a/generated/dev/go/100_openwhisk_resource.go +++ /dev/null @@ -1,382 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: openwhisk -// Service ID: 100 - -var _ resource.Resource = &OpenwhiskResource{} -var _ resource.ResourceWithModifyPlan = &OpenwhiskResource{} -var _ resource.ResourceWithImportState = &OpenwhiskResource{} - -type OpenwhiskResource struct { - client *core.UniversalClient -} - -type OpenwhiskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - Username types.String `tfsdk:"username"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewOpenwhiskResource() resource.Resource { - return &OpenwhiskResource{} -} - -func (r *OpenwhiskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_openwhisk" -} - -func (r *OpenwhiskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Имя пользователя Openwhisk Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *OpenwhiskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *OpenwhiskModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Username.IsNull() && !plan.Username.IsUnknown() && !state.Username.IsNull() && !state.Username.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Username.ValueString(), state.Username.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить username", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - if config.Username.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - if config.Username.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 64: resources_core.FormatString(config.Username), - 281: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 100, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *OpenwhiskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if data.Username.IsNull() || data.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 64: resources_core.FormatString(data.Username), - 281: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 100, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *OpenwhiskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 100, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *OpenwhiskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan OpenwhiskModel - var state OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 100, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *OpenwhiskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *OpenwhiskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *OpenwhiskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/109_zones_v2_record_resource.go b/generated/dev/go/109_zones_v2_record_resource.go deleted file mode 100644 index 9cc2073..0000000 --- a/generated/dev/go/109_zones_v2_record_resource.go +++ /dev/null @@ -1,362 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: zones_v2 -// Subresource: record - -var _ resource.Resource = &ZonesV2RecordResource{} - -// ZonesV2RecordResource manages a subresource via operations. -type ZonesV2RecordResource struct { - client *core.UniversalClient -} - -type ZonesV2RecordModel struct { - ID types.String `tfsdk:"id"` - ZonesV2ID types.String `tfsdk:"zones_v2_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - RecordInput types.String `tfsdk:"record_input"` - RecordName types.String `tfsdk:"record_name"` - RecordTTL types.Int64 `tfsdk:"record_t_t_l"` - RecordType types.String `tfsdk:"record_type"` -} - -func NewZonesV2RecordResource() resource.Resource { - return &ZonesV2RecordResource{} -} - -func (r *ZonesV2RecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_zones_v2_record" -} - -func (r *ZonesV2RecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "zones_v2_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "record_input": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции"}, - "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны в конце проставляется автоматически Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена"}, - "record_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("A")}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ZonesV2RecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ZonesV2RecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.RecordName.IsNull() || plan.RecordName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.ZonesV2ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - } - identityCodes := []string{ - "recordName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("record", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - "recordTTL": resources_core.FormatInt64(plan.RecordTTL), - "recordType": resources_core.FormatString(plan.RecordType), - "recordInput": resources_core.FormatString(plan.RecordInput), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_record", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_record", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ZonesV2RecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2RecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ZonesV2RecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_record" == "" { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ZonesV2ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - "recordTTL": resources_core.FormatInt64(plan.RecordTTL), - "recordType": resources_core.FormatString(plan.RecordType), - "recordInput": resources_core.FormatString(plan.RecordInput), - }) - - idParams := map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_record", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ZonesV2RecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_record" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ZonesV2ID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "recordName": resources_core.FormatString(state.RecordName), - } - identityCodes := []string{ - "recordName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("record", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(state.RecordName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_record", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ZonesV2RecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/109_zones_v2_resource.go b/generated/dev/go/109_zones_v2_resource.go deleted file mode 100644 index f07aab8..0000000 --- a/generated/dev/go/109_zones_v2_resource.go +++ /dev/null @@ -1,484 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: zones_v2 -// Service ID: 109 - -var _ resource.Resource = &ZonesV2Resource{} -var _ resource.ResourceWithModifyPlan = &ZonesV2Resource{} -var _ resource.ResourceWithImportState = &ZonesV2Resource{} - -type ZonesV2Resource struct { - client *core.UniversalClient -} - -type ZonesV2Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Records types.String `tfsdk:"records"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ZoneExpire types.Int64 `tfsdk:"zone_expire"` - ZoneKind types.String `tfsdk:"zone_kind"` - ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` - ZoneName types.String `tfsdk:"zone_name"` - ZoneRefresh types.Int64 `tfsdk:"zone_refresh"` - ZoneRetry types.Int64 `tfsdk:"zone_retry"` - ZoneTTL types.Int64 `tfsdk:"zone_t_t_l"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewZonesV2Resource() resource.Resource { - return &ZonesV2Resource{} -} - -func (r *ZonesV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_zones_v2" -} - -func (r *ZonesV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "records": schema.StringAttribute{Required: true}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("powerdns"), MarkdownDescription: "Не изменяется после создания."}, - "zone_expire": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600000), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, - "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, - "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, - "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, - "zone_refresh": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, - "zone_retry": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7200), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, - "zone_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ZonesV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ZonesV2Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneExpire.IsNull() && !plan.ZoneExpire.IsUnknown() && !state.ZoneExpire.IsNull() && !state.ZoneExpire.IsUnknown() { - if plan.ZoneExpire.ValueInt64() != state.ZoneExpire.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_expire", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneKind.ValueString(), state.ZoneKind.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneMastersForSlave.ValueString(), state.ZoneMastersForSlave.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneName.ValueString(), state.ZoneName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneRefresh.IsNull() && !plan.ZoneRefresh.IsUnknown() && !state.ZoneRefresh.IsNull() && !state.ZoneRefresh.IsUnknown() { - if plan.ZoneRefresh.ValueInt64() != state.ZoneRefresh.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_refresh", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneRetry.IsNull() && !plan.ZoneRetry.IsUnknown() && !state.ZoneRetry.IsNull() && !state.ZoneRetry.IsUnknown() { - if plan.ZoneRetry.ValueInt64() != state.ZoneRetry.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_retry", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTTL.IsNull() && !plan.ZoneTTL.IsUnknown() && !state.ZoneTTL.IsNull() && !state.ZoneTTL.IsUnknown() { - if plan.ZoneTTL.ValueInt64() != state.ZoneTTL.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_t_t_l", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Records.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "records is required.") - return - } - if config.Records.IsUnknown() { - return - } - if config.ZoneName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - if config.ZoneName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 761: resources_core.FormatString(config.ZoneName), - 762: resources_core.FormatString(config.ZoneKind), - 763: resources_core.FormatString(config.ZoneMastersForSlave), - 764: resources_core.FormatInt64(config.ZoneRefresh), - 765: resources_core.FormatInt64(config.ZoneRetry), - 766: resources_core.FormatInt64(config.ZoneExpire), - 767: resources_core.FormatInt64(config.ZoneTTL), - 768: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 109, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ZonesV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Records.IsNull() || data.Records.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "records is required.") - return - } - if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 761: resources_core.FormatString(data.ZoneName), - 762: resources_core.FormatString(data.ZoneKind), - 763: resources_core.FormatString(data.ZoneMastersForSlave), - 764: resources_core.FormatInt64(data.ZoneRefresh), - 765: resources_core.FormatInt64(data.ZoneRetry), - 766: resources_core.FormatInt64(data.ZoneExpire), - 767: resources_core.FormatInt64(data.ZoneTTL), - 768: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 109, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 109, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ZonesV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ZonesV2Model - var state ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.Records.IsNull() != state.Records.IsNull() || plan.Records.IsUnknown() != state.Records.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Records.IsNull() && !plan.Records.IsUnknown() && !state.Records.IsNull() && !state.Records.IsUnknown() { - if !strings.EqualFold(plan.Records.ValueString(), state.Records.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 769: resources_core.FormatString(plan.Records), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 109, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ZonesV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ZonesV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/110_dnszone_resource.go b/generated/dev/go/110_dnszone_resource.go deleted file mode 100644 index fab197b..0000000 --- a/generated/dev/go/110_dnszone_resource.go +++ /dev/null @@ -1,495 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnszone -// Service ID: 110 - -var _ resource.Resource = &DnszoneResource{} -var _ resource.ResourceWithModifyPlan = &DnszoneResource{} -var _ resource.ResourceWithImportState = &DnszoneResource{} - -type DnszoneResource struct { - client *core.UniversalClient -} - -type DnszoneModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ZoneEmail types.String `tfsdk:"zone_email"` - ZoneKind types.String `tfsdk:"zone_kind"` - ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` - ZoneName types.String `tfsdk:"zone_name"` - ZoneTimeToExpire types.String `tfsdk:"zone_time_to_expire"` - ZoneTimeToLive types.String `tfsdk:"zone_time_to_live"` - ZoneTimeToRefresh types.String `tfsdk:"zone_time_to_refresh"` - ZoneTimeToRetry types.String `tfsdk:"zone_time_to_retry"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDnszoneResource() resource.Resource { - return &DnszoneResource{} -} - -func (r *DnszoneResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnszone" -} - -func (r *DnszoneResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("powerdns"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "zone_email": schema.StringAttribute{Required: true, MarkdownDescription: "Контактный e-mail Не изменяется после создания."}, - "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, - "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, - "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, - "zone_time_to_expire": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3600000"), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, - "zone_time_to_live": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, - "zone_time_to_refresh": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, - "zone_time_to_retry": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("7200"), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DnszoneResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DnszoneModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneEmail.IsNull() && !plan.ZoneEmail.IsUnknown() && !state.ZoneEmail.IsNull() && !state.ZoneEmail.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneEmail.ValueString(), state.ZoneEmail.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_email", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneKind.ValueString(), state.ZoneKind.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneMastersForSlave.ValueString(), state.ZoneMastersForSlave.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneName.ValueString(), state.ZoneName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToExpire.IsNull() && !plan.ZoneTimeToExpire.IsUnknown() && !state.ZoneTimeToExpire.IsNull() && !state.ZoneTimeToExpire.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToExpire.ValueString(), state.ZoneTimeToExpire.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_expire", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToLive.IsNull() && !plan.ZoneTimeToLive.IsUnknown() && !state.ZoneTimeToLive.IsNull() && !state.ZoneTimeToLive.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToLive.ValueString(), state.ZoneTimeToLive.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_live", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToRefresh.IsNull() && !plan.ZoneTimeToRefresh.IsUnknown() && !state.ZoneTimeToRefresh.IsNull() && !state.ZoneTimeToRefresh.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToRefresh.ValueString(), state.ZoneTimeToRefresh.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_refresh", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToRetry.IsNull() && !plan.ZoneTimeToRetry.IsUnknown() && !state.ZoneTimeToRetry.IsNull() && !state.ZoneTimeToRetry.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToRetry.ValueString(), state.ZoneTimeToRetry.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_retry", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ZoneEmail.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") - return - } - if config.ZoneEmail.IsUnknown() { - return - } - if config.ZoneName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - if config.ZoneName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 66: resources_core.FormatString(config.ZoneEmail), - 67: resources_core.FormatString(config.ZoneName), - 68: resources_core.FormatString(config.ZoneKind), - 69: resources_core.FormatString(config.ZoneMastersForSlave), - 70: resources_core.FormatString(config.ZoneTimeToRefresh), - 71: resources_core.FormatString(config.ZoneTimeToRetry), - 72: resources_core.FormatString(config.ZoneTimeToExpire), - 73: resources_core.FormatString(config.ZoneTimeToLive), - 284: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 110, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DnszoneResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ZoneEmail.IsNull() || data.ZoneEmail.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") - return - } - if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 66: resources_core.FormatString(data.ZoneEmail), - 67: resources_core.FormatString(data.ZoneName), - 68: resources_core.FormatString(data.ZoneKind), - 69: resources_core.FormatString(data.ZoneMastersForSlave), - 70: resources_core.FormatString(data.ZoneTimeToRefresh), - 71: resources_core.FormatString(data.ZoneTimeToRetry), - 72: resources_core.FormatString(data.ZoneTimeToExpire), - 73: resources_core.FormatString(data.ZoneTimeToLive), - 284: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 110, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnszoneResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 110, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DnszoneResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnszoneModel - var state DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 110, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnszoneResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DnszoneResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DnszoneResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/111_dnsrecord_resource.go b/generated/dev/go/111_dnsrecord_resource.go deleted file mode 100644 index b28d2d8..0000000 --- a/generated/dev/go/111_dnsrecord_resource.go +++ /dev/null @@ -1,546 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnsrecord -// Service ID: 111 - -var _ resource.Resource = &DnsrecordResource{} -var _ resource.ResourceWithModifyPlan = &DnsrecordResource{} -var _ resource.ResourceWithImportState = &DnsrecordResource{} - -type DnsrecordResource struct { - client *core.UniversalClient -} - -type DnsrecordModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - RecordInput types.String `tfsdk:"record_input"` - RecordName types.String `tfsdk:"record_name"` - RecordTTL types.Int64 `tfsdk:"record_t_t_l"` - RecordType types.String `tfsdk:"record_type"` - ZoneUid types.String `tfsdk:"zone_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDnsrecordResource() resource.Resource { - return &DnsrecordResource{} -} - -func (r *DnsrecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnsrecord" -} - -func (r *DnsrecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "record_input": schema.StringAttribute{Required: true, MarkdownDescription: "Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP"}, - "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя записи. Имя зоны в конце проставляется автоматически Не изменяется после создания."}, - "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена Не изменяется после создания."}, - "record_type": schema.StringAttribute{Required: true, MarkdownDescription: "Тип DNS-записи Не изменяется после создания."}, - "zone_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID экземпляра услуги DNS-зоны Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DnsrecordResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DnsrecordModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.RecordName.IsNull() && !plan.RecordName.IsUnknown() && !state.RecordName.IsNull() && !state.RecordName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RecordName.ValueString(), state.RecordName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить record_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RecordTTL.IsNull() && !plan.RecordTTL.IsUnknown() && !state.RecordTTL.IsNull() && !state.RecordTTL.IsUnknown() { - if plan.RecordTTL.ValueInt64() != state.RecordTTL.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить record_t_t_l", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RecordType.IsNull() && !plan.RecordType.IsUnknown() && !state.RecordType.IsNull() && !state.RecordType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RecordType.ValueString(), state.RecordType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить record_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneUid.ValueString(), state.ZoneUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.RecordInput.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if config.RecordInput.IsUnknown() { - return - } - if config.RecordName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - if config.RecordName.IsUnknown() { - return - } - if config.RecordType.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") - return - } - if config.RecordType.IsUnknown() { - return - } - if config.ZoneUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") - return - } - if config.ZoneUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.ZoneUid.IsNull() && !config.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, config.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != config.ZoneUid.ValueString() { - config.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - params := map[int]string{ - 75: resources_core.FormatString(config.ZoneUid), - 76: resources_core.FormatString(config.RecordType), - 77: resources_core.FormatString(config.RecordName), - 78: resources_core.FormatString(config.RecordInput), - 450: resources_core.FormatInt64(config.RecordTTL), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 111, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DnsrecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.RecordInput.IsNull() || data.RecordInput.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if data.RecordName.IsNull() || data.RecordName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - if data.RecordType.IsNull() || data.RecordType.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") - return - } - if data.ZoneUid.IsNull() || data.ZoneUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalZoneUid := data.ZoneUid - if !data.ZoneUid.IsNull() && !data.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, data.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedZoneUid != "" && resolvedZoneUid != data.ZoneUid.ValueString() { - data.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 75: resources_core.FormatString(data.ZoneUid), - 76: resources_core.FormatString(data.RecordType), - 77: resources_core.FormatString(data.RecordName), - 78: resources_core.FormatString(data.RecordInput), - 450: resources_core.FormatInt64(data.RecordTTL), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 111, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, state.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { - state.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalZoneUid.IsNull() && !originalZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - if strings.EqualFold(state.ZoneUid.ValueString(), originalZoneUid.ValueString()) { - state.ZoneUid = originalZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnsrecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 111, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, newState.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != newState.ZoneUid.ValueString() { - newState.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() && !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { - if strings.EqualFold(newState.ZoneUid.ValueString(), state.ZoneUid.ValueString()) { - newState.ZoneUid = state.ZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DnsrecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnsrecordModel - var state DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.RecordInput.IsNull() != state.RecordInput.IsNull() || plan.RecordInput.IsUnknown() != state.RecordInput.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.RecordInput.IsNull() && !plan.RecordInput.IsUnknown() && !state.RecordInput.IsNull() && !state.RecordInput.IsUnknown() { - if !strings.EqualFold(plan.RecordInput.ValueString(), state.RecordInput.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 256: resources_core.FormatString(plan.RecordInput), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 111, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, state.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { - state.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - if strings.EqualFold(state.ZoneUid.ValueString(), plan.ZoneUid.ValueString()) { - state.ZoneUid = plan.ZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnsrecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DnsrecordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DnsrecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/112_tenant_resource.go b/generated/dev/go/112_tenant_resource.go deleted file mode 100644 index 57ca92c..0000000 --- a/generated/dev/go/112_tenant_resource.go +++ /dev/null @@ -1,372 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: tenant -// Service ID: 112 - -var _ resource.Resource = &TenantResource{} -var _ resource.ResourceWithModifyPlan = &TenantResource{} -var _ resource.ResourceWithImportState = &TenantResource{} - -type TenantResource struct { - client *core.UniversalClient -} - -type TenantModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - GrafanaOrgName types.String `tfsdk:"grafana_org_name"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewTenantResource() resource.Resource { - return &TenantResource{} -} - -func (r *TenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_tenant" -} - -func (r *TenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "grafana_org_name": schema.StringAttribute{Required: true, MarkdownDescription: "WZ-Код клиента Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("grafana.ngcloud.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *TenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *TenantModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.GrafanaOrgName.IsNull() && !plan.GrafanaOrgName.IsUnknown() && !state.GrafanaOrgName.IsNull() && !state.GrafanaOrgName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.GrafanaOrgName.ValueString(), state.GrafanaOrgName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить grafana_org_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.GrafanaOrgName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") - return - } - if config.GrafanaOrgName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 239: resources_core.FormatString(config.GrafanaOrgName), - 240: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 112, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.GrafanaOrgName.IsNull() || data.GrafanaOrgName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 239: resources_core.FormatString(data.GrafanaOrgName), - 240: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 112, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 112, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *TenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TenantModel - var state TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 112, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *TenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *TenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/113_vc_complex_resource.go b/generated/dev/go/113_vc_complex_resource.go deleted file mode 100644 index f8eace6..0000000 --- a/generated/dev/go/113_vc_complex_resource.go +++ /dev/null @@ -1,563 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_complex -// Service ID: 113 - -var _ resource.Resource = &VcComplexResource{} -var _ resource.ResourceWithModifyPlan = &VcComplexResource{} -var _ resource.ResourceWithImportState = &VcComplexResource{} - -type VcComplexResource struct { - client *core.UniversalClient -} - -type VcComplexModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - CpuAllocated types.String `tfsdk:"cpu_allocated"` - CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - MemAllocated types.Int64 `tfsdk:"mem_allocated"` - MemGuaranteed types.Int64 `tfsdk:"mem_guaranteed"` - NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` - NeedExternalAddressSNAT types.Bool `tfsdk:"need_external_address_s_n_a_t"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SegroupName types.String `tfsdk:"segroup_name"` - StorageConfig types.String `tfsdk:"storage_config"` - VIPCount types.Int64 `tfsdk:"v_i_p_count"` - VappName types.String `tfsdk:"vapp_name"` - VdcNetworkPool types.String `tfsdk:"vdc_network_pool"` - VdcProviderGateway types.String `tfsdk:"vdc_provider_gateway"` - VirtualServicesCount types.String `tfsdk:"virtual_services_count"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcComplexResource() resource.Resource { - return &VcComplexResource{} -} - -func (r *VcComplexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_complex" -} - -func (r *VcComplexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "cpu_allocated": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "шт Не изменяется после создания."}, - "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("internet-ipv4-v1"), MarkdownDescription: "Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT Не изменяется после создания."}, - "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "gb Не изменяется после создания."}, - "mem_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, - "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает AVI в Cloud Director Не изменяется после создания."}, - "need_external_address_s_n_a_t": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP на Edge. Используется для SNAT всего трафика Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "segroup_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("SEGROUP-SANDBOX-CL1-SHARED-01"), MarkdownDescription: "Наименование segroup, в котором будут выделяться пулы на AVI Не изменяется после создания."}, - "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{\"name\": \"\", \"size\": 1024}]"), MarkdownDescription: "Описывается как массив объектов, где описывается имя политики и её размер Не изменяется после создания.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "v_i_p_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "шт Не изменяется после создания."}, - "vapp_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя vApp Не изменяется после создания."}, - "vdc_network_pool": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nsxt-sandbox-geneve-np"), MarkdownDescription: "Не изменяется после создания."}, - "vdc_provider_gateway": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox-v1cl1-pvdc"), MarkdownDescription: "Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String Не изменяется после создания."}, - "virtual_services_count": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcComplexResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcComplexModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.CpuAllocated.ValueString(), state.CpuAllocated.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить cpu_allocated", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { - if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { - if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить mem_allocated", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MemGuaranteed.IsNull() && !plan.MemGuaranteed.IsUnknown() && !state.MemGuaranteed.IsNull() && !state.MemGuaranteed.IsUnknown() { - if plan.MemGuaranteed.ValueInt64() != state.MemGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить mem_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { - if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить need_enable_a_v_i", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NeedExternalAddressSNAT.IsNull() && !plan.NeedExternalAddressSNAT.IsUnknown() && !state.NeedExternalAddressSNAT.IsNull() && !state.NeedExternalAddressSNAT.IsUnknown() { - if plan.NeedExternalAddressSNAT.ValueBool() != state.NeedExternalAddressSNAT.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить need_external_address_s_n_a_t", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.SegroupName.IsNull() && !plan.SegroupName.IsUnknown() && !state.SegroupName.IsNull() && !state.SegroupName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.SegroupName.ValueString(), state.SegroupName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить segroup_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.StorageConfig.ValueString(), state.StorageConfig.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить storage_config", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VIPCount.IsNull() && !plan.VIPCount.IsUnknown() && !state.VIPCount.IsNull() && !state.VIPCount.IsUnknown() { - if plan.VIPCount.ValueInt64() != state.VIPCount.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить v_i_p_count", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappName.ValueString(), state.VappName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcNetworkPool.IsNull() && !plan.VdcNetworkPool.IsUnknown() && !state.VdcNetworkPool.IsNull() && !state.VdcNetworkPool.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcNetworkPool.ValueString(), state.VdcNetworkPool.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_network_pool", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcProviderGateway.IsNull() && !plan.VdcProviderGateway.IsUnknown() && !state.VdcProviderGateway.IsNull() && !state.VdcProviderGateway.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcProviderGateway.ValueString(), state.VdcProviderGateway.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_provider_gateway", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VirtualServicesCount.ValueString(), state.VirtualServicesCount.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить virtual_services_count", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VappName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") - return - } - if config.VappName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 332: resources_core.FormatString(config.ResourceRealm), - 334: resources_core.FormatString(config.VappName), - 358: resources_core.FormatString(config.VdcProviderGateway), - 359: resources_core.FormatString(config.VirtualServicesCount), - 360: resources_core.FormatString(config.IpSpaceName), - 399: resources_core.FormatString(config.StorageConfig), - 400: resources_core.FormatString(config.VdcNetworkPool), - 401: resources_core.FormatInt64(config.CpuGuaranteed), - 402: resources_core.FormatInt64(config.MemGuaranteed), - 403: resources_core.FormatString(config.SegroupName), - 404: resources_core.FormatBool(config.NeedExternalAddressSNAT), - 405: resources_core.FormatBool(config.NeedEnableAVI), - 566: resources_core.FormatString(config.CpuAllocated), - 567: resources_core.FormatInt64(config.MemAllocated), - 568: resources_core.FormatInt64(config.VIPCount), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 113, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcComplexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VappName.IsNull() || data.VappName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 332: resources_core.FormatString(data.ResourceRealm), - 334: resources_core.FormatString(data.VappName), - 358: resources_core.FormatString(data.VdcProviderGateway), - 359: resources_core.FormatString(data.VirtualServicesCount), - 360: resources_core.FormatString(data.IpSpaceName), - 399: resources_core.FormatString(data.StorageConfig), - 400: resources_core.FormatString(data.VdcNetworkPool), - 401: resources_core.FormatInt64(data.CpuGuaranteed), - 402: resources_core.FormatInt64(data.MemGuaranteed), - 403: resources_core.FormatString(data.SegroupName), - 404: resources_core.FormatBool(data.NeedExternalAddressSNAT), - 405: resources_core.FormatBool(data.NeedEnableAVI), - 566: resources_core.FormatString(data.CpuAllocated), - 567: resources_core.FormatInt64(data.MemAllocated), - 568: resources_core.FormatInt64(data.VIPCount), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 113, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcComplexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 113, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcComplexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcComplexModel - var state VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 113, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcComplexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcComplexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcComplexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/115_mariadb_database_resource.go b/generated/dev/go/115_mariadb_database_resource.go deleted file mode 100644 index cd6dd52..0000000 --- a/generated/dev/go/115_mariadb_database_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Subresource: database - -var _ resource.Resource = &MariadbDatabaseResource{} - -// MariadbDatabaseResource manages a subresource via operations. -type MariadbDatabaseResource struct { - client *core.UniversalClient -} - -type MariadbDatabaseModel struct { - ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` -} - -func NewMariadbDatabaseResource() resource.Resource { - return &MariadbDatabaseResource{} -} - -func (r *MariadbDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_database" -} - -func (r *MariadbDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mariadb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("db-dxinlvxx"), MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MariadbDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/115_mariadb_resource.go b/generated/dev/go/115_mariadb_resource.go deleted file mode 100644 index 8ed7b1d..0000000 --- a/generated/dev/go/115_mariadb_resource.go +++ /dev/null @@ -1,447 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Service ID: 115 - -var _ resource.Resource = &MariadbResource{} -var _ resource.ResourceWithModifyPlan = &MariadbResource{} -var _ resource.ResourceWithImportState = &MariadbResource{} - -type MariadbResource struct { - client *core.UniversalClient -} - -// MariadbAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type MariadbAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// MariadbAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type MariadbAutoscaleConfigurationModel struct { - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` -} - -// MariadbBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type MariadbBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// MariadbClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type MariadbClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// MariadbMariadbConfigurationModel — вложенная модель для map-fixed параметра mariadbConfiguration. -type MariadbMariadbConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -// MariadbStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type MariadbStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type MariadbModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *MariadbAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *MariadbAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *MariadbBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *MariadbClusterConfigurationModel `tfsdk:"cluster_configuration"` - MariadbConfiguration *MariadbMariadbConfigurationModel `tfsdk:"mariadb_configuration"` - StartupConfiguration *MariadbStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewMariadbResource() resource.Resource { - return &MariadbResource{} -} - -func (r *MariadbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb" -} - -func (r *MariadbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(720)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать"}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - }}, - "mariadb_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("9.4.0")}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *MariadbModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 877: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 878: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 879: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - 880: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.MariadbConfiguration.Version.ValueString())}), - 881: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 882: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 115, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *MariadbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 877: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 878: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 879: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - 880: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.MariadbConfiguration.Version.ValueString())}), - 881: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 882: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 115, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 115, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *MariadbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbModel - var state MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 883: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 884: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - 885: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.MariadbConfiguration.Version.ValueString())}), - 886: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 887: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 115, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *MariadbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *MariadbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/115_mariadb_user_resource.go b/generated/dev/go/115_mariadb_user_resource.go deleted file mode 100644 index 5a2f790..0000000 --- a/generated/dev/go/115_mariadb_user_resource.go +++ /dev/null @@ -1,367 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Subresource: user - -var _ resource.Resource = &MariadbUserResource{} - -// MariadbUserResource manages a subresource via operations. -type MariadbUserResource struct { - client *core.UniversalClient -} - -type MariadbUserModel struct { - ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - DbName types.String `tfsdk:"db_name"` - Password types.String `tfsdk:"password"` - Username types.String `tfsdk:"username"` -} - -func NewMariadbUserResource() resource.Resource { - return &MariadbUserResource{} -} - -func (r *MariadbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_user" -} - -func (r *MariadbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mariadb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("%"), MarkdownDescription: "Пример: `%` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо указать массив существующих баз Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "password": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("mhs3keq0"), MarkdownDescription: "Секрет будет записан в хранилище секретов и не будет отображаться публично Не изменяется после создания. Изменение требует пересоздания.", Sensitive: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("userz2hsgoyo"), MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "password": resources_core.FormatString(plan.Password), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "password", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "password": resources_core.FormatString(plan.Password), - "dbName": resources_core.FormatString(plan.DbName), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "password": resources_core.FormatString(plan.Password), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(state.AccessHosts), - "dbName": resources_core.FormatString(state.DbName), - "password": resources_core.FormatString(state.Password), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "password", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MariadbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/116_kafka_resource.go b/generated/dev/go/116_kafka_resource.go deleted file mode 100644 index 402172e..0000000 --- a/generated/dev/go/116_kafka_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Service ID: 116 - -var _ resource.Resource = &KafkaResource{} -var _ resource.ResourceWithModifyPlan = &KafkaResource{} -var _ resource.ResourceWithImportState = &KafkaResource{} - -type KafkaResource struct { - client *core.UniversalClient -} - -// KafkaAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type KafkaAccessConfigurationModel struct { - AllocationExternal types.Bool `tfsdk:"allocation_external" json:"allocationExternal"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// KafkaClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type KafkaClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// KafkaStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type KafkaStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type KafkaModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *KafkaAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *KafkaClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *KafkaStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewKafkaResource() resource.Resource { - return &KafkaResource{} -} - -func (r *KafkaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka" -} - -func (r *KafkaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "allocation_external": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048)}, - "replicas": schema.Int64Attribute{Required: true}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *KafkaModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1084: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1085: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1086: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", config.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 116, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *KafkaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1084: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1085: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1086: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", data.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 116, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 116, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *KafkaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaModel - var state KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1087: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1088: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", plan.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 116, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *KafkaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *KafkaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/116_kafka_topic_resource.go b/generated/dev/go/116_kafka_topic_resource.go deleted file mode 100644 index 3f289b3..0000000 --- a/generated/dev/go/116_kafka_topic_resource.go +++ /dev/null @@ -1,364 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Subresource: topic - -var _ resource.Resource = &KafkaTopicResource{} - -// KafkaTopicResource manages a subresource via operations. -type KafkaTopicResource struct { - client *core.UniversalClient -} - -type KafkaTopicModel struct { - ID types.String `tfsdk:"id"` - KafkaID types.String `tfsdk:"kafka_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - NameTopic types.String `tfsdk:"name_topic"` - Partitions types.Int64 `tfsdk:"partitions"` - Replicas types.Int64 `tfsdk:"replicas"` -} - -func NewKafkaTopicResource() resource.Resource { - return &KafkaTopicResource{} -} - -func (r *KafkaTopicResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka_topic" -} - -func (r *KafkaTopicResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "kafka_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций можно увеличить, но уменьшить их нельзя."}, - "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество реплик не может привышать количество реплик брокера"}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaTopicResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan KafkaTopicModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if plan.Partitions.IsNull() || plan.Partitions.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") - return - } - if plan.Replicas.IsNull() || plan.Replicas.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - } - identityCodes := []string{ - "nameTopic", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "partitions": resources_core.FormatInt64(plan.Partitions), - "replicas": resources_core.FormatInt64(plan.Replicas), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaTopicResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaTopicResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaTopicModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_topic" == "" { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "partitions": resources_core.FormatInt64(plan.Partitions), - "replicas": resources_core.FormatInt64(plan.Replicas), - }) - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_topic", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaTopicResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_topic" == "" { - return - } - - instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(state.NameTopic), - } - identityCodes := []string{ - "nameTopic", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(state.NameTopic), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_topic", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *KafkaTopicResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/116_kafka_user_resource.go b/generated/dev/go/116_kafka_user_resource.go deleted file mode 100644 index 52ea73c..0000000 --- a/generated/dev/go/116_kafka_user_resource.go +++ /dev/null @@ -1,373 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Subresource: user - -var _ resource.Resource = &KafkaUserResource{} - -// KafkaUserResource manages a subresource via operations. -type KafkaUserResource struct { - client *core.UniversalClient -} - -type KafkaUserModel struct { - ID types.String `tfsdk:"id"` - KafkaID types.String `tfsdk:"kafka_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - Group types.String `tfsdk:"group"` - NameTopic types.String `tfsdk:"name_topic"` - Operations types.String `tfsdk:"operations"` - Username types.String `tfsdk:"username"` -} - -func NewKafkaUserResource() resource.Resource { - return &KafkaUserResource{} -} - -func (r *KafkaUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka_user" -} - -func (r *KafkaUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "kafka_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("*"), MarkdownDescription: "Пример: `192.168.1.0/24` или `*`"}, - "group": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("*"), MarkdownDescription: "Пример: `order-consumers` или `*`"}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`"}, - "operations": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `Create, Describe, Read, Write`"}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `username-admin` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan KafkaUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if plan.Operations.IsNull() || plan.Operations.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "operations is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "nameTopic": resources_core.FormatString(plan.NameTopic), - "operations": resources_core.FormatString(plan.Operations), - "group": resources_core.FormatString(plan.Group), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_user" == "" { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "operations": resources_core.FormatString(plan.Operations), - "group": resources_core.FormatString(plan.Group), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "username": resources_core.FormatString(plan.Username), - }) - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *KafkaUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/119_akhq_resource.go b/generated/dev/go/119_akhq_resource.go deleted file mode 100644 index 101c30a..0000000 --- a/generated/dev/go/119_akhq_resource.go +++ /dev/null @@ -1,554 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: akhq -// Service ID: 119 - -var _ resource.Resource = &AkhqResource{} -var _ resource.ResourceWithModifyPlan = &AkhqResource{} -var _ resource.ResourceWithImportState = &AkhqResource{} - -type AkhqResource struct { - client *core.UniversalClient -} - -type AkhqModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - KafkaUid types.String `tfsdk:"kafka_uid"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewAkhqResource() resource.Resource { - return &AkhqResource{} -} - -func (r *AkhqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_akhq" -} - -func (r *AkhqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "kafka_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Выбрать кластер из списка"}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `1000` Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `1024` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *AkhqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *AkhqModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Domain.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if config.Domain.IsUnknown() { - return - } - if config.KafkaUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if config.KafkaUid.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { - config.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - params := map[int]string{ - 550: resources_core.FormatString(config.ResourceRealm), - 551: resources_core.FormatInt64(config.ResourceMemory), - 552: resources_core.FormatInt64(config.ResourceCPU), - 553: resources_core.FormatString(config.Domain), - 554: resources_core.FormatInt64(config.ResourceInstances), - 555: resources_core.FormatString(config.KafkaUid), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 119, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *AkhqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Domain.IsNull() || data.Domain.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalKafkaUid := data.KafkaUid - if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { - data.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 550: resources_core.FormatString(data.ResourceRealm), - 551: resources_core.FormatInt64(data.ResourceMemory), - 552: resources_core.FormatInt64(data.ResourceCPU), - 553: resources_core.FormatString(data.Domain), - 554: resources_core.FormatInt64(data.ResourceInstances), - 555: resources_core.FormatString(data.KafkaUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 119, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalKafkaUid.IsNull() && !originalKafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), originalKafkaUid.ValueString()) { - state.KafkaUid = originalKafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *AkhqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 119, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { - newState.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() && !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - if strings.EqualFold(newState.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - newState.KafkaUid = state.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *AkhqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan AkhqModel - var state AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.KafkaUid.IsNull() != state.KafkaUid.IsNull() || plan.KafkaUid.IsUnknown() != state.KafkaUid.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if !strings.EqualFold(plan.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 565: resources_core.FormatString(plan.KafkaUid), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 119, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), plan.KafkaUid.ValueString()) { - state.KafkaUid = plan.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *AkhqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *AkhqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *AkhqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/120_clickhouse_database_resource.go b/generated/dev/go/120_clickhouse_database_resource.go deleted file mode 100644 index cc5c42e..0000000 --- a/generated/dev/go/120_clickhouse_database_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Subresource: database - -var _ resource.Resource = &ClickhouseDatabaseResource{} - -// ClickhouseDatabaseResource manages a subresource via operations. -type ClickhouseDatabaseResource struct { - client *core.UniversalClient -} - -type ClickhouseDatabaseModel struct { - ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` -} - -func NewClickhouseDatabaseResource() resource.Resource { - return &ClickhouseDatabaseResource{} -} - -func (r *ClickhouseDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_database" -} - -func (r *ClickhouseDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "clickhouse_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("db01"), MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ClickhouseDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/120_clickhouse_resource.go b/generated/dev/go/120_clickhouse_resource.go deleted file mode 100644 index 12affc2..0000000 --- a/generated/dev/go/120_clickhouse_resource.go +++ /dev/null @@ -1,487 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Service ID: 120 - -var _ resource.Resource = &ClickhouseResource{} -var _ resource.ResourceWithModifyPlan = &ClickhouseResource{} -var _ resource.ResourceWithImportState = &ClickhouseResource{} - -type ClickhouseResource struct { - client *core.UniversalClient -} - -// ClickhouseAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type ClickhouseAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// ClickhouseAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type ClickhouseAutoscaleConfigurationModel struct { - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` -} - -// ClickhouseBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type ClickhouseBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// ClickhouseClickhouseConfigurationModel — вложенная модель для map-fixed параметра clickhouseConfiguration. -type ClickhouseClickhouseConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -// ClickhouseClickhouseKeeperConfigurationModel — вложенная модель для map-fixed параметра clickhouseKeeperConfiguration. -type ClickhouseClickhouseKeeperConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// ClickhouseClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type ClickhouseClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Shards types.Int64 `tfsdk:"shards" json:"shards"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// ClickhouseStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type ClickhouseStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type ClickhouseModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *ClickhouseAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *ClickhouseAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *ClickhouseBackupConfigurationModel `tfsdk:"backup_configuration"` - ClickhouseConfiguration *ClickhouseClickhouseConfigurationModel `tfsdk:"clickhouse_configuration"` - ClickhouseFilesConfiguration types.String `tfsdk:"clickhouse_files_configuration"` - ClickhouseKeeperConfiguration *ClickhouseClickhouseKeeperConfigurationModel `tfsdk:"clickhouse_keeper_configuration"` - ClusterConfiguration *ClickhouseClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *ClickhouseStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewClickhouseResource() resource.Resource { - return &ClickhouseResource{} -} - -func (r *ClickhouseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse" -} - -func (r *ClickhouseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "clickhouse_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("25.10")}, - }}, - "clickhouse_files_configuration": schema.StringAttribute{Optional: true, MarkdownDescription: "Формат:
- path: somethings3.xml
xmlConfig: '<clickhouse>......</clickhouse>'"}, - "clickhouse_keeper_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда"}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ClickhouseModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 837: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 838: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", config.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 839: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - 840: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 841: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.ClickhouseConfiguration.Version.ValueString())}), - 842: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 843: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64())}), - 851: resources_core.FormatString(config.ClickhouseFilesConfiguration), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 120, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ClickhouseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 837: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 838: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", data.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 839: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - 840: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 841: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.ClickhouseConfiguration.Version.ValueString())}), - 842: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 843: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64())}), - 851: resources_core.FormatString(data.ClickhouseFilesConfiguration), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 120, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 120, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ClickhouseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseModel - var state ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ClickhouseFilesConfiguration.IsNull() != state.ClickhouseFilesConfiguration.IsNull() || plan.ClickhouseFilesConfiguration.IsUnknown() != state.ClickhouseFilesConfiguration.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ClickhouseFilesConfiguration.IsNull() && !plan.ClickhouseFilesConfiguration.IsUnknown() && !state.ClickhouseFilesConfiguration.IsNull() && !state.ClickhouseFilesConfiguration.IsUnknown() { - if !strings.EqualFold(plan.ClickhouseFilesConfiguration.ValueString(), state.ClickhouseFilesConfiguration.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 844: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", plan.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 845: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Memory.ValueInt64())}), - 846: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - 847: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.ClickhouseConfiguration.Version.ValueString())}), - 848: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 849: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - 850: resources_core.FormatString(plan.ClickhouseFilesConfiguration), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 120, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ClickhouseResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ClickhouseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/120_clickhouse_user_resource.go b/generated/dev/go/120_clickhouse_user_resource.go deleted file mode 100644 index 8dc28f7..0000000 --- a/generated/dev/go/120_clickhouse_user_resource.go +++ /dev/null @@ -1,359 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Subresource: user - -var _ resource.Resource = &ClickhouseUserResource{} - -// ClickhouseUserResource manages a subresource via operations. -type ClickhouseUserResource struct { - client *core.UniversalClient -} - -type ClickhouseUserModel struct { - ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - DbName types.String `tfsdk:"db_name"` - Username types.String `tfsdk:"username"` -} - -func NewClickhouseUserResource() resource.Resource { - return &ClickhouseUserResource{} -} - -func (r *ClickhouseUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_user" -} - -func (r *ClickhouseUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "clickhouse_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("::/0"), MarkdownDescription: "Пример: `::/0` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myusername"), MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(state.AccessHosts), - "dbName": resources_core.FormatString(state.DbName), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ClickhouseUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/12_s3_resource.go b/generated/dev/go/12_s3_resource.go deleted file mode 100644 index f4db5ce..0000000 --- a/generated/dev/go/12_s3_resource.go +++ /dev/null @@ -1,421 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 -// Service ID: 12 - -var _ resource.Resource = &S3Resource{} -var _ resource.ResourceWithModifyPlan = &S3Resource{} -var _ resource.ResourceWithImportState = &S3Resource{} - -type S3Resource struct { - client *core.UniversalClient -} - -type S3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DisplayName types.String `tfsdk:"display_name"` - MaxBucketsPerUser types.Int64 `tfsdk:"max_buckets_per_user"` - MaxObjectsPerBucket types.Int64 `tfsdk:"max_objects_per_bucket"` - MaxSizeGbPerUser types.Int64 `tfsdk:"max_size_gb_per_user"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewS3Resource() resource.Resource { - return &S3Resource{} -} - -func (r *S3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3" -} - -func (r *S3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "display_name": schema.StringAttribute{Required: true, MarkdownDescription: "Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента Не изменяется после создания."}, - "max_buckets_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на пользователя. Количество бакетов пользователя. Передаём руками"}, - "max_objects_per_bucket": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на бакет. Кол-во объектов. Передаём руками"}, - "max_size_gb_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "maxSizeGb. Передаём руками"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ceph.tst.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *S3Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.DisplayName.IsNull() && !plan.DisplayName.IsUnknown() && !state.DisplayName.IsNull() && !state.DisplayName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.DisplayName.ValueString(), state.DisplayName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить display_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.DisplayName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") - return - } - if config.DisplayName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 163: resources_core.FormatString(config.ResourceRealm), - 320: resources_core.FormatString(config.DisplayName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 12, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *S3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.DisplayName.IsNull() || data.DisplayName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 163: resources_core.FormatString(data.ResourceRealm), - 320: resources_core.FormatString(data.DisplayName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 12, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 12, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *S3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3Model - var state S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.MaxSizeGbPerUser.IsNull() != state.MaxSizeGbPerUser.IsNull() || plan.MaxSizeGbPerUser.IsUnknown() != state.MaxSizeGbPerUser.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxSizeGbPerUser.IsNull() && !plan.MaxSizeGbPerUser.IsUnknown() && !state.MaxSizeGbPerUser.IsNull() && !state.MaxSizeGbPerUser.IsUnknown() { - if plan.MaxSizeGbPerUser.ValueInt64() != state.MaxSizeGbPerUser.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MaxObjectsPerBucket.IsNull() != state.MaxObjectsPerBucket.IsNull() || plan.MaxObjectsPerBucket.IsUnknown() != state.MaxObjectsPerBucket.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxObjectsPerBucket.IsNull() && !plan.MaxObjectsPerBucket.IsUnknown() && !state.MaxObjectsPerBucket.IsNull() && !state.MaxObjectsPerBucket.IsUnknown() { - if plan.MaxObjectsPerBucket.ValueInt64() != state.MaxObjectsPerBucket.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MaxBucketsPerUser.IsNull() != state.MaxBucketsPerUser.IsNull() || plan.MaxBucketsPerUser.IsUnknown() != state.MaxBucketsPerUser.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxBucketsPerUser.IsNull() && !plan.MaxBucketsPerUser.IsUnknown() && !state.MaxBucketsPerUser.IsNull() && !state.MaxBucketsPerUser.IsUnknown() { - if plan.MaxBucketsPerUser.ValueInt64() != state.MaxBucketsPerUser.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), - 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), - 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 12, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *S3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *S3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/12_s3_sub_user_resource.go b/generated/dev/go/12_s3_sub_user_resource.go deleted file mode 100644 index b8e0168..0000000 --- a/generated/dev/go/12_s3_sub_user_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 -// Subresource: sub_user - -var _ resource.Resource = &S3SubUserResource{} - -// S3SubUserResource manages a subresource via operations. -type S3SubUserResource struct { - client *core.UniversalClient -} - -type S3SubUserModel struct { - ID types.String `tfsdk:"id"` - S3ID types.String `tfsdk:"s3_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SubUserAccess types.String `tfsdk:"sub_user_access"` - SubUserName types.String `tfsdk:"sub_user_name"` -} - -func NewS3SubUserResource() resource.Resource { - return &S3SubUserResource{} -} - -func (r *S3SubUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3_sub_user" -} - -func (r *S3SubUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "s3_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "sub_user_access": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "sub_user_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3SubUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan S3SubUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.SubUserAccess.IsNull() || plan.SubUserAccess.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "sub_user_access is required.") - return - } - if plan.SubUserName.IsNull() || plan.SubUserName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "sub_user_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - "subUserName": resources_core.FormatString(plan.SubUserName), - } - identityCodes := []string{ - "subUserAccess", - "subUserName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "subUserName": resources_core.FormatString(plan.SubUserName), - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *S3SubUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3SubUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3SubUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - "subUserName": resources_core.FormatString(plan.SubUserName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *S3SubUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_sub_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.S3ID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(state.SubUserAccess), - "subUserName": resources_core.FormatString(state.SubUserName), - } - identityCodes := []string{ - "subUserAccess", - "subUserName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "subUserName": resources_core.FormatString(state.SubUserName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_sub_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *S3SubUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/13_s3bucket_resource.go b/generated/dev/go/13_s3bucket_resource.go deleted file mode 100644 index deb2272..0000000 --- a/generated/dev/go/13_s3bucket_resource.go +++ /dev/null @@ -1,478 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3bucket -// Service ID: 13 - -var _ resource.Resource = &S3bucketResource{} -var _ resource.ResourceWithModifyPlan = &S3bucketResource{} -var _ resource.ResourceWithImportState = &S3bucketResource{} - -type S3bucketResource struct { - client *core.UniversalClient -} - -type S3bucketModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - BucketName types.String `tfsdk:"bucket_name"` - CorsAll types.Bool `tfsdk:"cors_all"` - ListAll types.Bool `tfsdk:"list_all"` - MaxSize types.String `tfsdk:"max_size"` - Placement types.String `tfsdk:"placement"` - ReadAll types.Bool `tfsdk:"read_all"` - S3UserUid types.String `tfsdk:"s3_user_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewS3bucketResource() resource.Resource { - return &S3bucketResource{} -} - -func (r *S3bucketResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3bucket" -} - -func (r *S3bucketResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "bucket_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя бакета s3 Не изменяется после создания."}, - "cors_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS Не изменяется после создания."}, - "list_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. Не изменяется после создания."}, - "max_size": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("-1"), MarkdownDescription: "Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes Не изменяется после создания."}, - "placement": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("HOT"), MarkdownDescription: "Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения Не изменяется после создания."}, - "read_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. Не изменяется после создания."}, - "s3_user_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UID Корневой услуги S3. На основе него будет создан бакет. Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3bucketResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *S3bucketModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.BucketName.IsNull() && !plan.BucketName.IsUnknown() && !state.BucketName.IsNull() && !state.BucketName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.BucketName.ValueString(), state.BucketName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить bucket_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.CorsAll.IsNull() && !plan.CorsAll.IsUnknown() && !state.CorsAll.IsNull() && !state.CorsAll.IsUnknown() { - if plan.CorsAll.ValueBool() != state.CorsAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить cors_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ListAll.IsNull() && !plan.ListAll.IsUnknown() && !state.ListAll.IsNull() && !state.ListAll.IsUnknown() { - if plan.ListAll.ValueBool() != state.ListAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить list_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MaxSize.IsNull() && !plan.MaxSize.IsUnknown() && !state.MaxSize.IsNull() && !state.MaxSize.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.MaxSize.ValueString(), state.MaxSize.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить max_size", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Placement.IsNull() && !plan.Placement.IsUnknown() && !state.Placement.IsNull() && !state.Placement.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Placement.ValueString(), state.Placement.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить placement", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ReadAll.IsNull() && !plan.ReadAll.IsUnknown() && !state.ReadAll.IsNull() && !state.ReadAll.IsUnknown() { - if plan.ReadAll.ValueBool() != state.ReadAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить read_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() && !state.S3UserUid.IsNull() && !state.S3UserUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.S3UserUid.ValueString(), state.S3UserUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить s3_user_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.BucketName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") - return - } - if config.BucketName.IsUnknown() { - return - } - if config.S3UserUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") - return - } - if config.S3UserUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.S3UserUid.IsNull() && !config.S3UserUid.IsUnknown() { - resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3UserUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) - } else if resolvedS3UserUid != "" && resolvedS3UserUid != config.S3UserUid.ValueString() { - config.S3UserUid = types.StringValue(resolvedS3UserUid) - } - } - params := map[int]string{ - 124: resources_core.FormatString(config.S3UserUid), - 125: resources_core.FormatString(config.BucketName), - 126: resources_core.FormatString(config.MaxSize), - 127: resources_core.FormatBool(config.ReadAll), - 128: resources_core.FormatBool(config.ListAll), - 129: resources_core.FormatBool(config.CorsAll), - 130: resources_core.FormatString(config.Placement), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 13, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *S3bucketResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.BucketName.IsNull() || data.BucketName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") - return - } - if data.S3UserUid.IsNull() || data.S3UserUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 124: resources_core.FormatString(data.S3UserUid), - 125: resources_core.FormatString(data.BucketName), - 126: resources_core.FormatString(data.MaxSize), - 127: resources_core.FormatBool(data.ReadAll), - 128: resources_core.FormatBool(data.ListAll), - 129: resources_core.FormatBool(data.CorsAll), - 130: resources_core.FormatString(data.Placement), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 13, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3bucketResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 13, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *S3bucketResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3bucketModel - var state S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 13, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *S3bucketResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *S3bucketResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/148_vc_mgmt_sthutrval_cluster_resource.go b/generated/dev/go/148_vc_mgmt_sthutrval_cluster_resource.go deleted file mode 100644 index 17540bd..0000000 --- a/generated/dev/go/148_vc_mgmt_sthutrval_cluster_resource.go +++ /dev/null @@ -1,419 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_mgmt_sthutrval_cluster -// Service ID: 148 - -var _ resource.Resource = &VcMgmtSthutrvalClusterResource{} -var _ resource.ResourceWithModifyPlan = &VcMgmtSthutrvalClusterResource{} -var _ resource.ResourceWithImportState = &VcMgmtSthutrvalClusterResource{} - -type VcMgmtSthutrvalClusterResource struct { - client *core.UniversalClient -} - -// VcMgmtSthutrvalClusterAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type VcMgmtSthutrvalClusterAccessConfigurationModel struct { - AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i" json:"accessIpListAPI"` - NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress" json:"needExternalAddressIngress"` - AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress" json:"accessIpListIngress"` - NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i" json:"needExternalAddressAPI"` -} - -// VcMgmtSthutrvalClusterClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type VcMgmtSthutrvalClusterClusterConfigurationModel struct { - AppVersion types.String `tfsdk:"app_version" json:"appVersion"` -} - -// VcMgmtSthutrvalClusterControlPlaneConfigurationModel — вложенная модель для map-fixed параметра controlPlaneConfiguration. -type VcMgmtSthutrvalClusterControlPlaneConfigurationModel struct { - SizingDisk types.Int64 `tfsdk:"sizing_disk" json:"sizingDisk"` - Count types.Int64 `tfsdk:"count" json:"count"` - SizingPolicy types.String `tfsdk:"sizing_policy" json:"sizingPolicy"` -} - -// VcMgmtSthutrvalClusterStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type VcMgmtSthutrvalClusterStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` - ClusterName types.String `tfsdk:"cluster_name" json:"clusterName"` -} - -type VcMgmtSthutrvalClusterModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *VcMgmtSthutrvalClusterAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *VcMgmtSthutrvalClusterClusterConfigurationModel `tfsdk:"cluster_configuration"` - ControlPlaneConfiguration *VcMgmtSthutrvalClusterControlPlaneConfigurationModel `tfsdk:"control_plane_configuration"` - StartupConfiguration *VcMgmtSthutrvalClusterStartupConfigurationModel `tfsdk:"startup_configuration"` - WorkerConfiguration types.String `tfsdk:"worker_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcMgmtSthutrvalClusterResource() resource.Resource { - return &VcMgmtSthutrvalClusterResource{} -} - -func (r *VcMgmtSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_mgmt_sthutrval_cluster" -} - -func (r *VcMgmtSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**"}, - "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress"}, - "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**"}, - "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.13.1")}, - }}, - "control_plane_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50)}, - "count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "sizing_policy": schema.StringAttribute{Required: true, MarkdownDescription: "Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Облако в котором будет развёрнут тенант"}, - "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example-e5uamzll")}, - }}, - "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcMgmtSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.WorkerConfiguration.ValueString(), state.WorkerConfiguration.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить worker_configuration", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 771: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ClusterName.ValueString())}), - 772: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.AppVersion.ValueString())}), - 773: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", config.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", config.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", config.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 774: resources_core.FormatString(config.WorkerConfiguration), - 775: resources_core.BuildJSON(map[string]string{"accessIpListAPI": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListIngress.ValueString()), "needExternalAddressAPI": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressAPI.ValueBool())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 148, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 148, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 771: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ClusterName.ValueString())}), - 772: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.AppVersion.ValueString())}), - 773: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", data.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", data.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", data.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 774: resources_core.FormatString(data.WorkerConfiguration), - 775: resources_core.BuildJSON(map[string]string{"accessIpListAPI": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListIngress.ValueString()), "needExternalAddressAPI": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressAPI.ValueBool())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 148, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 148, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 148, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcMgmtSthutrvalClusterModel - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 148, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcMgmtSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcMgmtSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/149_valo_tenant_resource.go b/generated/dev/go/149_valo_tenant_resource.go deleted file mode 100644 index a3c1a9f..0000000 --- a/generated/dev/go/149_valo_tenant_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: valo_tenant -// Service ID: 149 - -var _ resource.Resource = &ValoTenantResource{} -var _ resource.ResourceWithModifyPlan = &ValoTenantResource{} -var _ resource.ResourceWithImportState = &ValoTenantResource{} - -type ValoTenantResource struct { - client *core.UniversalClient -} - -// ValoTenantValoOptionsModel — вложенная модель для map-fixed параметра valoOptions. -type ValoTenantValoOptionsModel struct { - UserCount types.Int64 `tfsdk:"user_count" json:"userCount"` - RequestApproveMaxPeriod types.Int64 `tfsdk:"request_approve_max_period" json:"requestApproveMaxPeriod"` - FfOnlineEditor types.Bool `tfsdk:"ff_online_editor" json:"ffOnlineEditor"` - FfWatermark types.Bool `tfsdk:"ff_watermark" json:"ffWatermark"` - FfUserImport types.Bool `tfsdk:"ff_user_import" json:"ffUserImport"` - FfThemes types.Bool `tfsdk:"ff_themes" json:"ffThemes"` - FfPublicLink types.Bool `tfsdk:"ff_public_link" json:"ffPublicLink"` - FfLogging types.Bool `tfsdk:"ff_logging" json:"ffLogging"` -} - -type ValoTenantModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceRealm types.String `tfsdk:"resource_realm"` - S3Storage types.Int64 `tfsdk:"s3_storage"` - ValoOptions *ValoTenantValoOptionsModel `tfsdk:"valo_options"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewValoTenantResource() resource.Resource { - return &ValoTenantResource{} -} - -func (r *ValoTenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_valo_tenant" -} - -func (r *ValoTenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Будет добавлено в зону valo.ngcloud.ru Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("valo.ngcloud.ru"), MarkdownDescription: "Не изменяется после создания."}, - "s3_storage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Параметр указывается в `Gb`"}, - "valo_options": schema.SingleNestedAttribute{Optional: true, Computed: true, - Attributes: map[string]schema.Attribute{ - "user_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10)}, - "request_approve_max_period": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2), MarkdownDescription: "Максимальное значение периода согласования (в днях)"}, - "ff_online_editor": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_watermark": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_user_import": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_themes": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_public_link": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_logging": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ValoTenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ValoTenantModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Domain.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if config.Domain.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 655: resources_core.FormatString(config.ResourceRealm), - 656: resources_core.FormatString(config.Domain), - 657: resources_core.BuildJSON(map[string]string{"userCount": fmt.Sprintf("%d", config.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", config.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", config.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", config.ValoOptions.FfWatermark.ValueBool()), "ffUserImport": fmt.Sprintf("%v", config.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", config.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", config.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", config.ValoOptions.FfLogging.ValueBool())}), - 658: resources_core.FormatInt64(config.S3Storage), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 149, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ValoTenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Domain.IsNull() || data.Domain.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 655: resources_core.FormatString(data.ResourceRealm), - 656: resources_core.FormatString(data.Domain), - 657: resources_core.BuildJSON(map[string]string{"userCount": fmt.Sprintf("%d", data.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", data.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", data.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", data.ValoOptions.FfWatermark.ValueBool()), "ffUserImport": fmt.Sprintf("%v", data.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", data.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", data.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", data.ValoOptions.FfLogging.ValueBool())}), - 658: resources_core.FormatInt64(data.S3Storage), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 149, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ValoTenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 149, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ValoTenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ValoTenantModel - var state ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.S3Storage.IsNull() != state.S3Storage.IsNull() || plan.S3Storage.IsUnknown() != state.S3Storage.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.S3Storage.IsNull() && !plan.S3Storage.IsUnknown() && !state.S3Storage.IsNull() && !state.S3Storage.IsUnknown() { - if plan.S3Storage.ValueInt64() != state.S3Storage.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 659: resources_core.BuildJSON(map[string]string{"ffUserImport": fmt.Sprintf("%v", plan.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", plan.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", plan.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", plan.ValoOptions.FfLogging.ValueBool()), "userCount": fmt.Sprintf("%d", plan.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", plan.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", plan.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", plan.ValoOptions.FfWatermark.ValueBool())}), - 660: resources_core.FormatInt64(plan.S3Storage), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 149, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ValoTenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ValoTenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ValoTenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/150_k8s_sthutrval_cluster_resource.go b/generated/dev/go/150_k8s_sthutrval_cluster_resource.go deleted file mode 100644 index 97d9772..0000000 --- a/generated/dev/go/150_k8s_sthutrval_cluster_resource.go +++ /dev/null @@ -1,429 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_sthutrval_cluster -// Service ID: 150 - -var _ resource.Resource = &K8sSthutrvalClusterResource{} -var _ resource.ResourceWithModifyPlan = &K8sSthutrvalClusterResource{} -var _ resource.ResourceWithImportState = &K8sSthutrvalClusterResource{} - -type K8sSthutrvalClusterResource struct { - client *core.UniversalClient -} - -// K8sSthutrvalClusterAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type K8sSthutrvalClusterAccessConfigurationModel struct { - NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress" json:"needExternalAddressIngress"` - AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress" json:"accessIpListIngress"` - NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i" json:"needExternalAddressAPI"` - AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i" json:"accessIpListAPI"` -} - -// K8sSthutrvalClusterClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sSthutrvalClusterClusterConfigurationModel struct { - AppVersion types.String `tfsdk:"app_version" json:"appVersion"` -} - -// K8sSthutrvalClusterControlPlaneConfigurationModel — вложенная модель для map-fixed параметра controlPlaneConfiguration. -type K8sSthutrvalClusterControlPlaneConfigurationModel struct { - SizingDisk types.Int64 `tfsdk:"sizing_disk" json:"sizingDisk"` - Count types.Int64 `tfsdk:"count" json:"count"` - SizingPolicy types.String `tfsdk:"sizing_policy" json:"sizingPolicy"` -} - -// K8sSthutrvalClusterStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sSthutrvalClusterStartupConfigurationModel struct { - ClusterName types.String `tfsdk:"cluster_name" json:"clusterName"` - VdcUid types.String `tfsdk:"vdc_uid" json:"vdcUid"` - NsxtUid types.String `tfsdk:"nsxt_uid" json:"nsxtUid"` -} - -type K8sSthutrvalClusterModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *K8sSthutrvalClusterAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *K8sSthutrvalClusterClusterConfigurationModel `tfsdk:"cluster_configuration"` - ControlPlaneConfiguration *K8sSthutrvalClusterControlPlaneConfigurationModel `tfsdk:"control_plane_configuration"` - StartupConfiguration *K8sSthutrvalClusterStartupConfigurationModel `tfsdk:"startup_configuration"` - WorkerConfiguration types.String `tfsdk:"worker_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sSthutrvalClusterResource() resource.Resource { - return &K8sSthutrvalClusterResource{} -} - -func (r *K8sSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster" -} - -func (r *K8sSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress"}, - "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, - "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false"}, - "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.13.1")}, - }}, - "control_plane_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50), MarkdownDescription: "Дисковая политика берётся из услуги VDC, первый в списке"}, - "count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "sizing_policy": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр`"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example-vlzeyhxy")}, - "vdc_uid": schema.StringAttribute{Required: true}, - "nsxt_uid": schema.StringAttribute{Required: true}, - }}, - "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]")}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 750: resources_core.BuildJSON(map[string]string{"clusterName": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ClusterName.ValueString()), "vdcUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.VdcUid.ValueString()), "nsxtUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.NsxtUid.ValueString())}), - 751: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", config.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", config.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", config.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 752: resources_core.FormatString(config.WorkerConfiguration), - 753: resources_core.BuildJSON(map[string]string{"needExternalAddressIngress": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListIngress.ValueString()), "needExternalAddressAPI": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListAPI.ValueString())}), - 755: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.AppVersion.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 150, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 750: resources_core.BuildJSON(map[string]string{"clusterName": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ClusterName.ValueString()), "vdcUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.VdcUid.ValueString()), "nsxtUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.NsxtUid.ValueString())}), - 751: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", data.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", data.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", data.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 752: resources_core.FormatString(data.WorkerConfiguration), - 753: resources_core.BuildJSON(map[string]string{"needExternalAddressIngress": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListIngress.ValueString()), "needExternalAddressAPI": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListAPI.ValueString())}), - 755: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.AppVersion.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 150, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 150, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sSthutrvalClusterModel - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.WorkerConfiguration.IsNull() != state.WorkerConfiguration.IsNull() || plan.WorkerConfiguration.IsUnknown() != state.WorkerConfiguration.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { - if !strings.EqualFold(plan.WorkerConfiguration.ValueString(), state.WorkerConfiguration.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 756: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", plan.ClusterConfiguration.AppVersion.ValueString())}), - 757: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", plan.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", plan.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", plan.ControlPlaneConfiguration.Count.ValueInt64())}), - 758: resources_core.FormatString(plan.WorkerConfiguration), - 759: resources_core.BuildJSON(map[string]string{"needExternalAddressIngress": fmt.Sprintf("%v", plan.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.AccessIpListIngress.ValueString()), "needExternalAddressAPI": fmt.Sprintf("%v", plan.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.AccessIpListAPI.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 150, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/150_k8s_sthutrval_cluster_user_resource.go b/generated/dev/go/150_k8s_sthutrval_cluster_user_resource.go deleted file mode 100644 index 55faf0e..0000000 --- a/generated/dev/go/150_k8s_sthutrval_cluster_user_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_sthutrval_cluster -// Subresource: user - -var _ resource.Resource = &K8sSthutrvalClusterUserResource{} - -// K8sSthutrvalClusterUserResource manages a subresource via operations. -type K8sSthutrvalClusterUserResource struct { - client *core.UniversalClient -} - -type K8sSthutrvalClusterUserModel struct { - ID types.String `tfsdk:"id"` - K8sSthutrvalClusterID types.String `tfsdk:"k8s_sthutrval_cluster_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Admins types.String `tfsdk:"admins"` -} - -func NewK8sSthutrvalClusterUserResource() resource.Resource { - return &K8sSthutrvalClusterUserResource{} -} - -func (r *K8sSthutrvalClusterUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster_user" -} - -func (r *K8sSthutrvalClusterUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_sthutrval_cluster_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "admins": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sSthutrvalClusterUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sSthutrvalClusterUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sSthutrvalClusterUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(state.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(state.Admins), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sSthutrvalClusterUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/151_k8s_openbao_kv_resource.go b/generated/dev/go/151_k8s_openbao_kv_resource.go deleted file mode 100644 index 773a5c2..0000000 --- a/generated/dev/go/151_k8s_openbao_kv_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: kv - -var _ resource.Resource = &K8sOpenbaoKvResource{} - -// K8sOpenbaoKvResource manages a subresource via operations. -type K8sOpenbaoKvResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoKvModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KvName types.String `tfsdk:"kv_name"` -} - -func NewK8sOpenbaoKvResource() resource.Resource { - return &K8sOpenbaoKvResource{} -} - -func (r *K8sOpenbaoKvResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_kv" -} - -func (r *K8sOpenbaoKvResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "kv_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("kvname-ldnwybqu"), MarkdownDescription: "Будет создан `v2 тип движка`

Уникальное в рамках услуги Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoKvResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoKvModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - } - identityCodes := []string{ - "kvName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("kv", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_kv", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_kv", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoKvResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoKvResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoKvModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoKvResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_kv" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - } - identityCodes := []string{ - "kvName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("kv", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_kv", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoKvResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/151_k8s_openbao_policy_resource.go b/generated/dev/go/151_k8s_openbao_policy_resource.go deleted file mode 100644 index 668ed29..0000000 --- a/generated/dev/go/151_k8s_openbao_policy_resource.go +++ /dev/null @@ -1,355 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: policy - -var _ resource.Resource = &K8sOpenbaoPolicyResource{} - -// K8sOpenbaoPolicyResource manages a subresource via operations. -type K8sOpenbaoPolicyResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoPolicyModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - PolicyName types.String `tfsdk:"policy_name"` - PolicySettings types.String `tfsdk:"policy_settings"` -} - -func NewK8sOpenbaoPolicyResource() resource.Resource { - return &K8sOpenbaoPolicyResource{} -} - -func (r *K8sOpenbaoPolicyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_policy" -} - -func (r *K8sOpenbaoPolicyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "policy_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("policy-re1w8vbp"), MarkdownDescription: "Сделано для дальнейшего удобного использования Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "policy_settings": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoPolicyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.PolicySettings.IsNull() || plan.PolicySettings.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "policy_settings is required.") - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "policyName": resources_core.FormatString(plan.PolicyName), - "policySettings": resources_core.FormatString(plan.PolicySettings), - } - identityCodes := []string{ - "policyName", - "policySettings", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("policy", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "policySettings": resources_core.FormatString(plan.PolicySettings), - "policyName": resources_core.FormatString(plan.PolicyName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_policy", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_policy", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoPolicyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoPolicyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "policyName": resources_core.FormatString(plan.PolicyName), - "policySettings": resources_core.FormatString(plan.PolicySettings), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoPolicyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_policy" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "policyName": resources_core.FormatString(state.PolicyName), - "policySettings": resources_core.FormatString(state.PolicySettings), - } - identityCodes := []string{ - "policyName", - "policySettings", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("policy", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "policyName": resources_core.FormatString(state.PolicyName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_policy", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoPolicyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/151_k8s_openbao_resource.go b/generated/dev/go/151_k8s_openbao_resource.go deleted file mode 100644 index 821648e..0000000 --- a/generated/dev/go/151_k8s_openbao_resource.go +++ /dev/null @@ -1,468 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Service ID: 151 - -var _ resource.Resource = &K8sOpenbaoResource{} -var _ resource.ResourceWithModifyPlan = &K8sOpenbaoResource{} -var _ resource.ResourceWithImportState = &K8sOpenbaoResource{} - -type K8sOpenbaoResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AppVersion types.String `tfsdk:"app_version"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sOpenbaoResource() resource.Resource { - return &K8sOpenbaoResource{} -} - -func (r *K8sOpenbaoResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao" -} - -func (r *K8sOpenbaoResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.5.2"), MarkdownDescription: "Выбрать из списка"}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("bao-pasczlik"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `500`"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `10`"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3), MarkdownDescription: "Пример: `3`"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `2048`"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sOpenbaoModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 663: resources_core.FormatString(config.ResourceRealm), - 664: resources_core.FormatInt64(config.ResourceCPU), - 665: resources_core.FormatInt64(config.ResourceInstances), - 666: resources_core.FormatInt64(config.ResourceMemory), - 667: resources_core.FormatInt64(config.ResourceDisk), - 668: resources_core.FormatString(config.AppVersion), - 669: resources_core.FormatString(config.Domain), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 151, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sOpenbaoResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 151, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 663: resources_core.FormatString(data.ResourceRealm), - 664: resources_core.FormatInt64(data.ResourceCPU), - 665: resources_core.FormatInt64(data.ResourceInstances), - 666: resources_core.FormatInt64(data.ResourceMemory), - 667: resources_core.FormatInt64(data.ResourceDisk), - 668: resources_core.FormatString(data.AppVersion), - 669: resources_core.FormatString(data.Domain), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 151, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 151, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 151, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sOpenbaoResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoModel - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { - if !strings.EqualFold(plan.AppVersion.ValueString(), state.AppVersion.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 670: resources_core.FormatInt64(plan.ResourceInstances), - 671: resources_core.FormatInt64(plan.ResourceCPU), - 672: resources_core.FormatInt64(plan.ResourceMemory), - 673: resources_core.FormatInt64(plan.ResourceDisk), - 674: resources_core.FormatString(plan.AppVersion), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 151, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sOpenbaoResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sOpenbaoResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/151_k8s_openbao_secrets_resource.go b/generated/dev/go/151_k8s_openbao_secrets_resource.go deleted file mode 100644 index 161164e..0000000 --- a/generated/dev/go/151_k8s_openbao_secrets_resource.go +++ /dev/null @@ -1,368 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: secrets - -var _ resource.Resource = &K8sOpenbaoSecretsResource{} - -// K8sOpenbaoSecretsResource manages a subresource via operations. -type K8sOpenbaoSecretsResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoSecretsModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KvName types.String `tfsdk:"kv_name"` - SecretData types.String `tfsdk:"secret_data"` - SecretPath types.String `tfsdk:"secret_path"` -} - -func NewK8sOpenbaoSecretsResource() resource.Resource { - return &K8sOpenbaoSecretsResource{} -} - -func (r *K8sOpenbaoSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_secrets" -} - -func (r *K8sOpenbaoSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "kv_name": schema.StringAttribute{Required: true, MarkdownDescription: "Выберите из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "secret_data": schema.StringAttribute{Required: true, MarkdownDescription: "Хотя бы один секрет необходимо добавить Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "secret_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("path/to/mysecret"), MarkdownDescription: "Пример: `path` или `road/to/path` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.KvName.IsNull() || plan.KvName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kv_name is required.") - return - } - if plan.SecretData.IsNull() || plan.SecretData.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "secret_data is required.") - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretData": resources_core.FormatString(plan.SecretData), - "secretPath": resources_core.FormatString(plan.SecretPath), - } - identityCodes := []string{ - "kvName", - "secretData", - "secretPath", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretPath": resources_core.FormatString(plan.SecretPath), - "secretData": resources_core.FormatString(plan.SecretData), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretData": resources_core.FormatString(plan.SecretData), - "secretPath": resources_core.FormatString(plan.SecretPath), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_secrets" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - "secretData": resources_core.FormatString(state.SecretData), - "secretPath": resources_core.FormatString(state.SecretPath), - } - identityCodes := []string{ - "kvName", - "secretData", - "secretPath", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - "secretPath": resources_core.FormatString(state.SecretPath), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_secrets", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/153_nifi_resource.go b/generated/dev/go/153_nifi_resource.go deleted file mode 100644 index 12049ba..0000000 --- a/generated/dev/go/153_nifi_resource.go +++ /dev/null @@ -1,474 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nifi -// Service ID: 153 - -var _ resource.Resource = &NifiResource{} -var _ resource.ResourceWithModifyPlan = &NifiResource{} -var _ resource.ResourceWithImportState = &NifiResource{} - -type NifiResource struct { - client *core.UniversalClient -} - -type NifiModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AppVersion types.String `tfsdk:"app_version"` - Domain types.String `tfsdk:"domain"` - JdbcDrivers types.String `tfsdk:"jdbc_drivers"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNifiResource() resource.Resource { - return &NifiResource{} -} - -func (r *NifiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nifi" -} - -func (r *NifiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.6.0"), MarkdownDescription: "Выбрать из списка Не изменяется после создания."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nifi-lvu4xj7j"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "jdbc_drivers": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `https://jdbc.postgresql.org/download/postgresql-42.7.8.jar` Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Пример: `2000` Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `10` Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1` Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(4096), MarkdownDescription: "Пример: `2048` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NifiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NifiModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.AppVersion.ValueString(), state.AppVersion.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить app_version", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.JdbcDrivers.IsNull() && !plan.JdbcDrivers.IsUnknown() && !state.JdbcDrivers.IsNull() && !state.JdbcDrivers.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.JdbcDrivers.ValueString(), state.JdbcDrivers.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить jdbc_drivers", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.JdbcDrivers.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "jdbc_drivers is required.") - return - } - if config.JdbcDrivers.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 711: resources_core.FormatString(config.Domain), - 712: resources_core.FormatInt64(config.ResourceCPU), - 713: resources_core.FormatInt64(config.ResourceMemory), - 714: resources_core.FormatInt64(config.ResourceInstances), - 715: resources_core.FormatInt64(config.ResourceDisk), - 716: resources_core.FormatString(config.AppVersion), - 717: resources_core.FormatString(config.ResourceRealm), - 718: resources_core.FormatString(config.JdbcDrivers), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 153, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NifiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.JdbcDrivers.IsNull() || data.JdbcDrivers.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "jdbc_drivers is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 153, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 711: resources_core.FormatString(data.Domain), - 712: resources_core.FormatInt64(data.ResourceCPU), - 713: resources_core.FormatInt64(data.ResourceMemory), - 714: resources_core.FormatInt64(data.ResourceInstances), - 715: resources_core.FormatInt64(data.ResourceDisk), - 716: resources_core.FormatString(data.AppVersion), - 717: resources_core.FormatString(data.ResourceRealm), - 718: resources_core.FormatString(data.JdbcDrivers), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 153, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 153, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "array-map-fixed"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NifiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 153, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "array-map-fixed"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NifiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NifiModel - var state NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 153, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "jdbcDrivers", Field: "JdbcDrivers", Type: "array-map-fixed"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NifiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NifiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NifiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/163_llm_ai_resource.go b/generated/dev/go/163_llm_ai_resource.go deleted file mode 100644 index 22a1d70..0000000 --- a/generated/dev/go/163_llm_ai_resource.go +++ /dev/null @@ -1,345 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Service ID: 163 - -var _ resource.Resource = &LlmAiResource{} -var _ resource.ResourceWithModifyPlan = &LlmAiResource{} -var _ resource.ResourceWithImportState = &LlmAiResource{} - -type LlmAiResource struct { - client *core.UniversalClient -} - -type LlmAiModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewLlmAiResource() resource.Resource { - return &LlmAiResource{} -} - -func (r *LlmAiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai" -} - -func (r *LlmAiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("llmAi.aillm.ru"), MarkdownDescription: "Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *LlmAiModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 826: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 163, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *LlmAiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 163, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 826: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 163, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 163, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 163, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *LlmAiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiModel - var state LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 163, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *LlmAiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *LlmAiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/163_llm_ai_secrets_resource.go b/generated/dev/go/163_llm_ai_secrets_resource.go deleted file mode 100644 index 0525342..0000000 --- a/generated/dev/go/163_llm_ai_secrets_resource.go +++ /dev/null @@ -1,401 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Subresource: secrets - -var _ resource.Resource = &LlmAiSecretsResource{} - -// LlmAiSecretsResource manages a subresource via operations. -type LlmAiSecretsResource struct { - client *core.UniversalClient -} - -type LlmAiSecretsModel struct { - ID types.String `tfsdk:"id"` - LlmAiID types.String `tfsdk:"llm_ai_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KeyName types.String `tfsdk:"key_name"` - Models types.String `tfsdk:"models"` - RpmLimit types.Int64 `tfsdk:"rpm_limit"` - TeamName types.String `tfsdk:"team_name"` - TpmLimit types.Int64 `tfsdk:"tpm_limit"` - VariablesKeys types.String `tfsdk:"variables_keys"` -} - -func NewLlmAiSecretsResource() resource.Resource { - return &LlmAiSecretsResource{} -} - -func (r *LlmAiSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai_secrets" -} - -func (r *LlmAiSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "llm_ai_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "key_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "models": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "rpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(60), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()}}, - "team_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "tpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100000), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()}}, - "variables_keys": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LlmAiSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.KeyName.IsNull() || plan.KeyName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "key_name is required.") - return - } - if plan.Models.IsNull() || plan.Models.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "models is required.") - return - } - if plan.TeamName.IsNull() || plan.TeamName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "team_name is required.") - return - } - if plan.VariablesKeys.IsNull() || plan.VariablesKeys.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "variables_keys is required.") - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "keyName": resources_core.FormatString(plan.KeyName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "teamName": resources_core.FormatString(plan.TeamName), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - "variablesKeys": resources_core.FormatString(plan.VariablesKeys), - } - identityCodes := []string{ - "keyName", - "models", - "rpmLimit", - "teamName", - "tpmLimit", - "variablesKeys", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - "keyName": resources_core.FormatString(plan.KeyName), - "teamName": resources_core.FormatString(plan.TeamName), - "variablesKeys": resources_core.FormatString(plan.VariablesKeys), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "keyName": resources_core.FormatString(plan.KeyName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "teamName": resources_core.FormatString(plan.TeamName), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - "variablesKeys": resources_core.FormatString(plan.VariablesKeys), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_secrets" == "" { - return - } - - instanceUID := strings.TrimSpace(state.LlmAiID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "keyName": resources_core.FormatString(state.KeyName), - "models": resources_core.FormatString(state.Models), - "rpmLimit": resources_core.FormatInt64(state.RpmLimit), - "teamName": resources_core.FormatString(state.TeamName), - "tpmLimit": resources_core.FormatInt64(state.TpmLimit), - "variablesKeys": resources_core.FormatString(state.VariablesKeys), - } - identityCodes := []string{ - "keyName", - "models", - "rpmLimit", - "teamName", - "tpmLimit", - "variablesKeys", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "keyName": resources_core.FormatString(state.KeyName), - "teamName": resources_core.FormatString(state.TeamName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_secrets", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *LlmAiSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/163_llm_ai_team_resource.go b/generated/dev/go/163_llm_ai_team_resource.go deleted file mode 100644 index e411362..0000000 --- a/generated/dev/go/163_llm_ai_team_resource.go +++ /dev/null @@ -1,365 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Subresource: team - -var _ resource.Resource = &LlmAiTeamResource{} - -// LlmAiTeamResource manages a subresource via operations. -type LlmAiTeamResource struct { - client *core.UniversalClient -} - -type LlmAiTeamModel struct { - ID types.String `tfsdk:"id"` - LlmAiID types.String `tfsdk:"llm_ai_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Models types.String `tfsdk:"models"` - RpmLimit types.Int64 `tfsdk:"rpm_limit"` - TeamName types.String `tfsdk:"team_name"` - TpmLimit types.Int64 `tfsdk:"tpm_limit"` -} - -func NewLlmAiTeamResource() resource.Resource { - return &LlmAiTeamResource{} -} - -func (r *LlmAiTeamResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai_team" -} - -func (r *LlmAiTeamResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "llm_ai_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "models": schema.StringAttribute{Required: true}, - "rpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(60)}, - "team_name": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "tpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100000)}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiTeamResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LlmAiTeamModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Models.IsNull() || plan.Models.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "models is required.") - return - } - if plan.TeamName.IsNull() || plan.TeamName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "team_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - } - identityCodes := []string{ - "teamName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("team", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_team", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_team", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiTeamResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiTeamResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiTeamModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_team" == "" { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - }) - - idParams := map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_team", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiTeamResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_team" == "" { - return - } - - instanceUID := strings.TrimSpace(state.LlmAiID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "teamName": resources_core.FormatString(state.TeamName), - } - identityCodes := []string{ - "teamName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("team", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(state.TeamName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_team", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *LlmAiTeamResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/175_k8s_go_resource.go b/generated/dev/go/175_k8s_go_resource.go deleted file mode 100644 index f6cc30f..0000000 --- a/generated/dev/go/175_k8s_go_resource.go +++ /dev/null @@ -1,422 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_go -// Service ID: 175 - -var _ resource.Resource = &K8sGoResource{} -var _ resource.ResourceWithModifyPlan = &K8sGoResource{} -var _ resource.ResourceWithImportState = &K8sGoResource{} - -type K8sGoResource struct { - client *core.UniversalClient -} - -// K8sGoAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type K8sGoAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// K8sGoAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type K8sGoAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// K8sGoClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sGoClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// K8sGoStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sGoStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type K8sGoModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *K8sGoAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *K8sGoAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *K8sGoClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *K8sGoStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sGoResource() resource.Resource { - return &K8sGoResource{} -} - -func (r *K8sGoResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_go" -} - -func (r *K8sGoResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("lucee-utovgtao"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("6.0")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testlucee"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sGoResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sGoModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sGoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sGoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1046: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1047: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1048: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1049: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1050: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 175, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sGoResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sGoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 175, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1046: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1047: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1048: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1049: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1050: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 175, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 175, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sGoResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sGoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 175, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sGoResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sGoModel - var state K8sGoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1051: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1052: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1053: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 175, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sGoResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sGoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sGoResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sGoResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/19_vc_org_resource.go b/generated/dev/go/19_vc_org_resource.go deleted file mode 100644 index c2e768b..0000000 --- a/generated/dev/go/19_vc_org_resource.go +++ /dev/null @@ -1,391 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org -// Service ID: 19 - -var _ resource.Resource = &VcOrgResource{} -var _ resource.ResourceWithModifyPlan = &VcOrgResource{} -var _ resource.ResourceWithImportState = &VcOrgResource{} - -type VcOrgResource struct { - client *core.UniversalClient -} - -type VcOrgModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - OrganizationType types.String `tfsdk:"organization_type"` - ResourceRealm types.String `tfsdk:"resource_realm"` - VIPConfigure types.String `tfsdk:"v_i_p_configure"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcOrgResource() resource.Resource { - return &VcOrgResource{} -} - -func (r *VcOrgResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org" -} - -func (r *VcOrgResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "organization_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("iaas"), MarkdownDescription: "Доступ к организации предоставляется только при выборе `iaas` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Облако в котором будет развёрнут тенант Не изменяется после создания."}, - "v_i_p_configure": schema.StringAttribute{Required: true}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcOrgModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.OrganizationType.IsNull() && !plan.OrganizationType.IsUnknown() && !state.OrganizationType.IsNull() && !state.OrganizationType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.OrganizationType.ValueString(), state.OrganizationType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить organization_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VIPConfigure.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") - return - } - if config.VIPConfigure.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 418: resources_core.FormatString(config.ResourceRealm), - 556: resources_core.FormatString(config.OrganizationType), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 19, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcOrgResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VIPConfigure.IsNull() || data.VIPConfigure.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 418: resources_core.FormatString(data.ResourceRealm), - 556: resources_core.FormatString(data.OrganizationType), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 19, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 19, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcOrgResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgModel - var state VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VIPConfigure.IsNull() != state.VIPConfigure.IsNull() || plan.VIPConfigure.IsUnknown() != state.VIPConfigure.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VIPConfigure.IsNull() && !plan.VIPConfigure.IsUnknown() && !state.VIPConfigure.IsNull() && !state.VIPConfigure.IsUnknown() { - if !strings.EqualFold(plan.VIPConfigure.ValueString(), state.VIPConfigure.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 662: resources_core.FormatString(plan.VIPConfigure), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 19, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcOrgResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcOrgResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/19_vc_org_user_resource.go b/generated/dev/go/19_vc_org_user_resource.go deleted file mode 100644 index b18fcdd..0000000 --- a/generated/dev/go/19_vc_org_user_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org -// Subresource: user - -var _ resource.Resource = &VcOrgUserResource{} - -// VcOrgUserResource manages a subresource via operations. -type VcOrgUserResource struct { - client *core.UniversalClient -} - -type VcOrgUserModel struct { - ID types.String `tfsdk:"id"` - VcOrgID types.String `tfsdk:"vc_org_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Admins types.String `tfsdk:"admins"` -} - -func NewVcOrgUserResource() resource.Resource { - return &VcOrgUserResource{} -} - -func (r *VcOrgUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org_user" -} - -func (r *VcOrgUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "vc_org_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "admins": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcOrgUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcOrgUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcOrgUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.VcOrgID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(state.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(state.Admins), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *VcOrgUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/1_dummy_resource.go b/generated/dev/go/1_dummy_resource.go deleted file mode 100644 index e2070da..0000000 --- a/generated/dev/go/1_dummy_resource.go +++ /dev/null @@ -1,582 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dummy -// Service ID: 1 - -var _ resource.Resource = &DummyResource{} -var _ resource.ResourceWithModifyPlan = &DummyResource{} -var _ resource.ResourceWithImportState = &DummyResource{} - -type DummyResource struct { - client *core.UniversalClient -} - -// DummyMapExampleModel — вложенная модель для map-fixed параметра mapExample. -type DummyMapExampleModel struct { - Subparam1 types.String `tfsdk:"subparam1" json:"subparam1"` - Secret types.String `tfsdk:"secret" json:"secret"` - Subparam2 types.String `tfsdk:"subparam2" json:"subparam2"` -} - -// DummyMapFixedModel — вложенная модель для map-fixed параметра mapFixed. -type DummyMapFixedModel struct { - Bol1 types.Bool `tfsdk:"bol1" json:"bol1"` - MinStr1 types.String `tfsdk:"min_str1" json:"minStr1"` - Param1 types.Int64 `tfsdk:"param1" json:"param1"` - Param2 types.String `tfsdk:"param2" json:"param2"` - Param3 types.Int64 `tfsdk:"param3" json:"param3"` -} - -type DummyModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Arr types.String `tfsdk:"arr"` - ArrayMapFixedExample types.String `tfsdk:"array_map_fixed_example"` - Bodymessage types.String `tfsdk:"bodymessage"` - DurationMs types.Int64 `tfsdk:"duration_ms"` - FailAtStart types.Bool `tfsdk:"fail_at_start"` - FailInProgress types.Bool `tfsdk:"fail_in_progress"` - JsonExample types.String `tfsdk:"json_example"` - MapExample *DummyMapExampleModel `tfsdk:"map_example"` - MapFixed *DummyMapFixedModel `tfsdk:"map_fixed"` - NestedRefExample types.String `tfsdk:"nested_ref_example"` - ResourceRealm types.String `tfsdk:"resource_realm"` - WhereFail types.Int64 `tfsdk:"where_fail"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDummyResource() resource.Resource { - return &DummyResource{} -} - -func (r *DummyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dummy" -} - -func (r *DummyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "arr": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "array_map_fixed_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Array of map-fixed Не изменяется после создания."}, - "bodymessage": schema.StringAttribute{Optional: true, MarkdownDescription: "Дата, уходящая в Vault"}, - "duration_ms": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "перед завершением подождать (sleep) столько миллисекунд"}, - "fail_at_start": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, завершить джоб, ничего не записывая в cmdb"}, - "fail_in_progress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb"}, - "json_example": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для тестирования интерфейса", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "map_example": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "subparam1": schema.StringAttribute{Optional: true, MarkdownDescription: "это для администратора"}, - "secret": schema.StringAttribute{Optional: true, MarkdownDescription: "это секрет"}, - "subparam2": schema.StringAttribute{Optional: true}, - }}, - "map_fixed": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "bol1": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Булевский, обязательный"}, - "min_str1": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abracadabra"), MarkdownDescription: "не меньше 10 символов"}, - "param1": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Integer, secret"}, - "param2": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Дескрипшен"}, - "param3": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "целое положительное"}, - }}, - "nested_ref_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("dummy"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "where_fail": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write"}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DummyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DummyModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Arr.IsNull() && !plan.Arr.IsUnknown() && !state.Arr.IsNull() && !state.Arr.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Arr.ValueString(), state.Arr.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить arr", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ArrayMapFixedExample.IsNull() && !plan.ArrayMapFixedExample.IsUnknown() && !state.ArrayMapFixedExample.IsNull() && !state.ArrayMapFixedExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ArrayMapFixedExample.ValueString(), state.ArrayMapFixedExample.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить array_map_fixed_example", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NestedRefExample.IsNull() && !plan.NestedRefExample.IsUnknown() && !state.NestedRefExample.IsNull() && !state.NestedRefExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NestedRefExample.ValueString(), state.NestedRefExample.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить nested_ref_example", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Arr.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - if config.Arr.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 198: resources_core.FormatInt64(config.DurationMs), - 199: resources_core.FormatBool(config.FailAtStart), - 200: resources_core.FormatBool(config.FailInProgress), - 201: resources_core.FormatInt64(config.WhereFail), - 242: resources_core.FormatString(config.ResourceRealm), - 286: resources_core.FormatString(config.Bodymessage), - 321: resources_core.BuildJSON(map[string]string{"subparam1": fmt.Sprintf("\"%s\"", config.MapExample.Subparam1.ValueString()), "secret": fmt.Sprintf("\"%s\"", config.MapExample.Secret.ValueString()), "subparam2": fmt.Sprintf("\"%s\"", config.MapExample.Subparam2.ValueString())}), - 322: resources_core.FormatString(config.JsonExample), - 396: resources_core.FormatString(config.NestedRefExample), - 647: resources_core.BuildJSON(map[string]string{"bol1": fmt.Sprintf("%v", config.MapFixed.Bol1.ValueBool()), "minStr1": fmt.Sprintf("\"%s\"", config.MapFixed.MinStr1.ValueString()), "param1": fmt.Sprintf("%d", config.MapFixed.Param1.ValueInt64()), "param2": fmt.Sprintf("\"%s\"", config.MapFixed.Param2.ValueString()), "param3": fmt.Sprintf("%d", config.MapFixed.Param3.ValueInt64())}), - 654: resources_core.FormatString(config.ArrayMapFixedExample), - 863: resources_core.FormatString(config.Arr), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 1, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Arr.IsNull() || data.Arr.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 198: resources_core.FormatInt64(data.DurationMs), - 199: resources_core.FormatBool(data.FailAtStart), - 200: resources_core.FormatBool(data.FailInProgress), - 201: resources_core.FormatInt64(data.WhereFail), - 242: resources_core.FormatString(data.ResourceRealm), - 286: resources_core.FormatString(data.Bodymessage), - 321: resources_core.BuildJSON(map[string]string{"subparam1": fmt.Sprintf("\"%s\"", data.MapExample.Subparam1.ValueString()), "secret": fmt.Sprintf("\"%s\"", data.MapExample.Secret.ValueString()), "subparam2": fmt.Sprintf("\"%s\"", data.MapExample.Subparam2.ValueString())}), - 322: resources_core.FormatString(data.JsonExample), - 396: resources_core.FormatString(data.NestedRefExample), - 647: resources_core.BuildJSON(map[string]string{"bol1": fmt.Sprintf("%v", data.MapFixed.Bol1.ValueBool()), "minStr1": fmt.Sprintf("\"%s\"", data.MapFixed.MinStr1.ValueString()), "param1": fmt.Sprintf("%d", data.MapFixed.Param1.ValueInt64()), "param2": fmt.Sprintf("\"%s\"", data.MapFixed.Param2.ValueString()), "param3": fmt.Sprintf("%d", data.MapFixed.Param3.ValueInt64())}), - 654: resources_core.FormatString(data.ArrayMapFixedExample), - 863: resources_core.FormatString(data.Arr), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 1, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DummyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 1, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DummyModel - var state DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.DurationMs.IsNull() != state.DurationMs.IsNull() || plan.DurationMs.IsUnknown() != state.DurationMs.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.DurationMs.IsNull() && !plan.DurationMs.IsUnknown() && !state.DurationMs.IsNull() && !state.DurationMs.IsUnknown() { - if plan.DurationMs.ValueInt64() != state.DurationMs.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.FailAtStart.IsNull() != state.FailAtStart.IsNull() || plan.FailAtStart.IsUnknown() != state.FailAtStart.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.FailAtStart.IsNull() && !plan.FailAtStart.IsUnknown() && !state.FailAtStart.IsNull() && !state.FailAtStart.IsUnknown() { - if plan.FailAtStart.ValueBool() != state.FailAtStart.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.FailInProgress.IsNull() != state.FailInProgress.IsNull() || plan.FailInProgress.IsUnknown() != state.FailInProgress.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.FailInProgress.IsNull() && !plan.FailInProgress.IsUnknown() && !state.FailInProgress.IsNull() && !state.FailInProgress.IsUnknown() { - if plan.FailInProgress.ValueBool() != state.FailInProgress.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.WhereFail.IsNull() != state.WhereFail.IsNull() || plan.WhereFail.IsUnknown() != state.WhereFail.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.WhereFail.IsNull() && !plan.WhereFail.IsUnknown() && !state.WhereFail.IsNull() && !state.WhereFail.IsUnknown() { - if plan.WhereFail.ValueInt64() != state.WhereFail.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.Bodymessage.IsNull() != state.Bodymessage.IsNull() || plan.Bodymessage.IsUnknown() != state.Bodymessage.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Bodymessage.IsNull() && !plan.Bodymessage.IsUnknown() && !state.Bodymessage.IsNull() && !state.Bodymessage.IsUnknown() { - if !strings.EqualFold(plan.Bodymessage.ValueString(), state.Bodymessage.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.JsonExample.IsNull() != state.JsonExample.IsNull() || plan.JsonExample.IsUnknown() != state.JsonExample.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonExample.IsNull() && !plan.JsonExample.IsUnknown() && !state.JsonExample.IsNull() && !state.JsonExample.IsUnknown() { - if !strings.EqualFold(plan.JsonExample.ValueString(), state.JsonExample.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 287: resources_core.FormatInt64(plan.DurationMs), - 288: resources_core.FormatBool(plan.FailAtStart), - 289: resources_core.FormatBool(plan.FailInProgress), - 290: resources_core.FormatInt64(plan.WhereFail), - 291: resources_core.FormatString(plan.Bodymessage), - 373: resources_core.BuildJSON(map[string]string{"secret": fmt.Sprintf("\"%s\"", plan.MapExample.Secret.ValueString())}), - 374: resources_core.FormatString(plan.JsonExample), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 1, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DummyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DummyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DummyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/20_vc_org_saas_resource.go b/generated/dev/go/20_vc_org_saas_resource.go deleted file mode 100644 index 83cdd6e..0000000 --- a/generated/dev/go/20_vc_org_saas_resource.go +++ /dev/null @@ -1,348 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org_saas -// Service ID: 20 - -var _ resource.Resource = &VcOrgSaasResource{} -var _ resource.ResourceWithModifyPlan = &VcOrgSaasResource{} -var _ resource.ResourceWithImportState = &VcOrgSaasResource{} - -type VcOrgSaasResource struct { - client *core.UniversalClient -} - -type VcOrgSaasModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcOrgSaasResource() resource.Resource { - return &VcOrgSaasResource{} -} - -func (r *VcOrgSaasResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org_saas" -} - -func (r *VcOrgSaasResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgSaasResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcOrgSaasModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 185: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 20, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcOrgSaasResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 185: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 20, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgSaasResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 20, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcOrgSaasResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgSaasModel - var state VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 20, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgSaasResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcOrgSaasResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcOrgSaasResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/21_vc_vdc_resource.go b/generated/dev/go/21_vc_vdc_resource.go deleted file mode 100644 index 21d0d3d..0000000 --- a/generated/dev/go/21_vc_vdc_resource.go +++ /dev/null @@ -1,573 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc -// Service ID: 21 - -var _ resource.Resource = &VcVdcResource{} -var _ resource.ResourceWithModifyPlan = &VcVdcResource{} -var _ resource.ResourceWithImportState = &VcVdcResource{} - -type VcVdcResource struct { - client *core.UniversalClient -} - -type VcVdcModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - CpuAllocated types.Int64 `tfsdk:"cpu_allocated"` - CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` - MemAllocated types.Int64 `tfsdk:"mem_allocated"` - NetworkProvider types.String `tfsdk:"network_provider"` - OrganizationUid types.String `tfsdk:"organization_uid"` - ProviderVdc types.String `tfsdk:"provider_vdc"` - StorageConfig types.String `tfsdk:"storage_config"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVdcResource() resource.Resource { - return &VcVdcResource{} -} - -func (r *VcVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc" -} - -func (r *VcVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "cpu_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(80), MarkdownDescription: "шт"}, - "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "% резервирования Не изменяется после создания."}, - "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "gb"}, - "network_provider": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "organization_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` Не изменяется после создания."}, - "provider_vdc": schema.StringAttribute{Required: true, MarkdownDescription: "Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
Не изменяется после создания."}, - "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Первая политика в списке указывается как "По умолчанию""}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVdcModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { - if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NetworkProvider.IsNull() && !plan.NetworkProvider.IsUnknown() && !state.NetworkProvider.IsNull() && !state.NetworkProvider.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NetworkProvider.ValueString(), state.NetworkProvider.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить network_provider", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.OrganizationUid.ValueString(), state.OrganizationUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить organization_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ProviderVdc.IsNull() && !plan.ProviderVdc.IsUnknown() && !state.ProviderVdc.IsNull() && !state.ProviderVdc.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ProviderVdc.ValueString(), state.ProviderVdc.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить provider_vdc", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.NetworkProvider.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") - return - } - if config.NetworkProvider.IsUnknown() { - return - } - if config.OrganizationUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") - return - } - if config.OrganizationUid.IsUnknown() { - return - } - if config.ProviderVdc.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") - return - } - if config.ProviderVdc.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.OrganizationUid.IsNull() && !config.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, config.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != config.OrganizationUid.ValueString() { - config.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - params := map[int]string{ - 30: resources_core.FormatString(config.OrganizationUid), - 335: resources_core.FormatString(config.ProviderVdc), - 361: resources_core.FormatString(config.StorageConfig), - 397: resources_core.FormatInt64(config.CpuGuaranteed), - 557: resources_core.FormatInt64(config.CpuAllocated), - 558: resources_core.FormatInt64(config.MemAllocated), - 746: resources_core.FormatString(config.NetworkProvider), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 21, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.NetworkProvider.IsNull() || data.NetworkProvider.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") - return - } - if data.OrganizationUid.IsNull() || data.OrganizationUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") - return - } - if data.ProviderVdc.IsNull() || data.ProviderVdc.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalOrganizationUid := data.OrganizationUid - if !data.OrganizationUid.IsNull() && !data.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, data.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedOrganizationUid != "" && resolvedOrganizationUid != data.OrganizationUid.ValueString() { - data.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 30: resources_core.FormatString(data.OrganizationUid), - 335: resources_core.FormatString(data.ProviderVdc), - 361: resources_core.FormatString(data.StorageConfig), - 397: resources_core.FormatInt64(data.CpuGuaranteed), - 557: resources_core.FormatInt64(data.CpuAllocated), - 558: resources_core.FormatInt64(data.MemAllocated), - 746: resources_core.FormatString(data.NetworkProvider), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 21, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { - state.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalOrganizationUid.IsNull() && !originalOrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - if strings.EqualFold(state.OrganizationUid.ValueString(), originalOrganizationUid.ValueString()) { - state.OrganizationUid = originalOrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 21, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, newState.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != newState.OrganizationUid.ValueString() { - newState.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() && !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { - if strings.EqualFold(newState.OrganizationUid.ValueString(), state.OrganizationUid.ValueString()) { - newState.OrganizationUid = state.OrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcModel - var state VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.CpuAllocated.IsNull() != state.CpuAllocated.IsNull() || plan.CpuAllocated.IsUnknown() != state.CpuAllocated.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { - if plan.CpuAllocated.ValueInt64() != state.CpuAllocated.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MemAllocated.IsNull() != state.MemAllocated.IsNull() || plan.MemAllocated.IsUnknown() != state.MemAllocated.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { - if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.StorageConfig.IsNull() != state.StorageConfig.IsNull() || plan.StorageConfig.IsUnknown() != state.StorageConfig.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { - if !strings.EqualFold(plan.StorageConfig.ValueString(), state.StorageConfig.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 560: resources_core.FormatInt64(plan.CpuAllocated), - 561: resources_core.FormatInt64(plan.MemAllocated), - 562: resources_core.FormatString(plan.StorageConfig), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 21, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { - state.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - if strings.EqualFold(state.OrganizationUid.ValueString(), plan.OrganizationUid.ValueString()) { - state.OrganizationUid = plan.OrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVdcResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/22_vc_nsxt_resource.go b/generated/dev/go/22_vc_nsxt_resource.go deleted file mode 100644 index 58189cc..0000000 --- a/generated/dev/go/22_vc_nsxt_resource.go +++ /dev/null @@ -1,614 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_nsxt -// Service ID: 22 - -var _ resource.Resource = &VcNsxtResource{} -var _ resource.ResourceWithModifyPlan = &VcNsxtResource{} -var _ resource.ResourceWithImportState = &VcNsxtResource{} - -type VcNsxtResource struct { - client *core.UniversalClient -} - -type VcNsxtModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` - QosProfile types.String `tfsdk:"qos_profile"` - VdcGroupUid types.String `tfsdk:"vdc_group_uid"` - VdcType types.String `tfsdk:"vdc_type"` - VdcUid types.String `tfsdk:"vdc_uid"` - VirtualServicesCount types.Int64 `tfsdk:"virtual_services_count"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcNsxtResource() resource.Resource { - return &VcNsxtResource{} -} - -func (r *VcNsxtResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_nsxt" -} - -func (r *VcNsxtResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ip Space для внешнего IP"}, - "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включение Load Balancer"}, - "qos_profile": schema.StringAttribute{Optional: true, MarkdownDescription: "Параметр пока не работает
Должен возвращать поле из ресурсной платформы типа vc из .vcd.hardware.gatewayQoSProfiles

Как по идее должен работать.
Нужно заполнить или CFS vdcUid, или vdcGroupUid
Идеально конечно проверять `if (params.vdcType == "vdc" && params.vdcUid != "")` или `if (params.vdcType == "vdcGroup" && params.vdcGroupUid != "")`

Далее надо пойти по стейту vdc -> org -> resPlatform, взять gatewayQoSProfiles
Или пойти по стейту vdcgroup -> vdc -> org -> resPlatform, взять gatewayQoSProfiles

Регулярку могу написать (Виталя)"}, - "vdc_group_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Группа виртуальных датацентров (groupvDC)` Не изменяется после создания."}, - "vdc_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vdc"), MarkdownDescription: "Тип родительской услуги Не изменяется после создания."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, - "virtual_services_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Кол-во виртуальных сервисов, которые **резервируются** на AVI"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcNsxtResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcNsxtModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcGroupUid.ValueString(), state.VdcGroupUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_group_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcType.IsNull() && !plan.VdcType.IsUnknown() && !state.VdcType.IsNull() && !state.VdcType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcType.ValueString(), state.VdcType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VdcGroupUid.IsNull() && !config.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, config.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != config.VdcGroupUid.ValueString() { - config.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 8: resources_core.FormatString(config.VdcUid), - 340: resources_core.FormatBool(config.NeedEnableAVI), - 341: resources_core.FormatInt64(config.VirtualServicesCount), - 621: resources_core.FormatString(config.VdcType), - 622: resources_core.FormatString(config.VdcGroupUid), - 825: resources_core.FormatString(config.QosProfile), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 22, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcNsxtResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcGroupUid := data.VdcGroupUid - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.VdcGroupUid.IsNull() && !data.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, data.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != data.VdcGroupUid.ValueString() { - data.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 8: resources_core.FormatString(data.VdcUid), - 340: resources_core.FormatBool(data.NeedEnableAVI), - 341: resources_core.FormatInt64(data.VirtualServicesCount), - 621: resources_core.FormatString(data.VdcType), - 622: resources_core.FormatString(data.VdcGroupUid), - 825: resources_core.FormatString(data.QosProfile), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 22, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { - state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVdcGroupUid.IsNull() && !originalVdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - if strings.EqualFold(state.VdcGroupUid.ValueString(), originalVdcGroupUid.ValueString()) { - state.VdcGroupUid = originalVdcGroupUid - } - } - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNsxtResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 22, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, newState.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != newState.VdcGroupUid.ValueString() { - newState.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() && !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { - if strings.EqualFold(newState.VdcGroupUid.ValueString(), state.VdcGroupUid.ValueString()) { - newState.VdcGroupUid = state.VdcGroupUid - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcNsxtResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcNsxtModel - var state VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.NeedEnableAVI.IsNull() != state.NeedEnableAVI.IsNull() || plan.NeedEnableAVI.IsUnknown() != state.NeedEnableAVI.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { - if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VirtualServicesCount.IsNull() != state.VirtualServicesCount.IsNull() || plan.VirtualServicesCount.IsUnknown() != state.VirtualServicesCount.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { - if plan.VirtualServicesCount.ValueInt64() != state.VirtualServicesCount.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.QosProfile.IsNull() != state.QosProfile.IsNull() || plan.QosProfile.IsUnknown() != state.QosProfile.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.QosProfile.IsNull() && !plan.QosProfile.IsUnknown() && !state.QosProfile.IsNull() && !state.QosProfile.IsUnknown() { - if !strings.EqualFold(plan.QosProfile.ValueString(), state.QosProfile.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 368: resources_core.FormatBool(plan.NeedEnableAVI), - 369: resources_core.FormatInt64(plan.VirtualServicesCount), - 372: resources_core.FormatString(plan.IpSpaceName), - 856: resources_core.FormatString(plan.QosProfile), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 22, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { - state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - if strings.EqualFold(state.VdcGroupUid.ValueString(), plan.VdcGroupUid.ValueString()) { - state.VdcGroupUid = plan.VdcGroupUid - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNsxtResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcNsxtResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcNsxtResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/23_vc_vm_resource.go b/generated/dev/go/23_vc_vm_resource.go deleted file mode 100644 index d39e2e1..0000000 --- a/generated/dev/go/23_vc_vm_resource.go +++ /dev/null @@ -1,581 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm -// Service ID: 23 - -var _ resource.Resource = &VcVmResource{} -var _ resource.ResourceWithModifyPlan = &VcVmResource{} -var _ resource.ResourceWithImportState = &VcVmResource{} - -type VcVmResource struct { - client *core.UniversalClient -} - -type VcVmModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessIpList types.String `tfsdk:"access_ip_list"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - TemplateName types.String `tfsdk:"template_name"` - VappUid types.String `tfsdk:"vapp_uid"` - VmCpu types.String `tfsdk:"vm_cpu"` - VmDisksize types.String `tfsdk:"vm_disksize"` - VmName types.String `tfsdk:"vm_name"` - VmRam types.String `tfsdk:"vm_ram"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmResource() resource.Resource { - return &VcVmResource{} -} - -func (r *VcVmResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm" -} - -func (r *VcVmResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("79.111.119.78/32 185.247.187.1/32"), MarkdownDescription: "Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов"}, - "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) Не изменяется после создания."}, - "template_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("RockyLinux_9-16G"), MarkdownDescription: "ОС Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "InstanceUid vApp. vApp должен быть создан Не изменяется после создания."}, - "vm_cpu": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "CPU"}, - "vm_disksize": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("10"), MarkdownDescription: "Размер диска под сервис (GB)"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vm0001"), MarkdownDescription: "Имя ВМ. Не поддерживает спец-символы Не изменяется после создания."}, - "vm_ram": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "RAM (GB)"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.TemplateName.IsNull() && !plan.TemplateName.IsUnknown() && !state.TemplateName.IsNull() && !state.TemplateName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.TemplateName.ValueString(), state.TemplateName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить template_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappUid.ValueString(), state.VappUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmName.ValueString(), state.VmName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.VappUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - if config.VappUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { - config.VappUid = types.StringValue(resolvedVappUid) - } - } - params := map[int]string{ - 33: resources_core.FormatString(config.VappUid), - 35: resources_core.FormatString(config.VmName), - 36: resources_core.FormatString(config.VmCpu), - 37: resources_core.FormatString(config.VmRam), - 38: resources_core.FormatString(config.VmDisksize), - 176: resources_core.FormatString(config.TemplateName), - 177: resources_core.FormatString(config.AccessIpList), - 406: resources_core.FormatString(config.IpSpaceName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 23, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.VappUid.IsNull() || data.VappUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVappUid := data.VappUid - if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { - data.VappUid = types.StringValue(resolvedVappUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 33: resources_core.FormatString(data.VappUid), - 35: resources_core.FormatString(data.VmName), - 36: resources_core.FormatString(data.VmCpu), - 37: resources_core.FormatString(data.VmRam), - 38: resources_core.FormatString(data.VmDisksize), - 176: resources_core.FormatString(data.TemplateName), - 177: resources_core.FormatString(data.AccessIpList), - 406: resources_core.FormatString(data.IpSpaceName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 23, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVappUid.IsNull() && !originalVappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), originalVappUid.ValueString()) { - state.VappUid = originalVappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 23, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { - newState.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() && !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - if strings.EqualFold(newState.VappUid.ValueString(), state.VappUid.ValueString()) { - newState.VappUid = state.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmModel - var state VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { - if !strings.EqualFold(plan.VmCpu.ValueString(), state.VmCpu.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { - if !strings.EqualFold(plan.VmRam.ValueString(), state.VmRam.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmDisksize.IsNull() != state.VmDisksize.IsNull() || plan.VmDisksize.IsUnknown() != state.VmDisksize.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmDisksize.IsNull() && !plan.VmDisksize.IsUnknown() && !state.VmDisksize.IsNull() && !state.VmDisksize.IsUnknown() { - if !strings.EqualFold(plan.VmDisksize.ValueString(), state.VmDisksize.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { - if !strings.EqualFold(plan.AccessIpList.ValueString(), state.AccessIpList.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 186: resources_core.FormatString(plan.VmCpu), - 187: resources_core.FormatString(plan.VmRam), - 188: resources_core.FormatString(plan.VmDisksize), - 189: resources_core.FormatString(plan.AccessIpList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 23, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), plan.VappUid.ValueString()) { - state.VappUid = plan.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/24_vc_nat_resource.go b/generated/dev/go/24_vc_nat_resource.go deleted file mode 100644 index 20fe4bd..0000000 --- a/generated/dev/go/24_vc_nat_resource.go +++ /dev/null @@ -1,322 +0,0 @@ -package resources_gen - -import ( - "context" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_nat -// Service ID: 24 - -var _ resource.Resource = &VcNatResource{} -var _ resource.ResourceWithModifyPlan = &VcNatResource{} -var _ resource.ResourceWithImportState = &VcNatResource{} - -type VcNatResource struct { - client *core.UniversalClient -} - -type VcNatModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcNatResource() resource.Resource { - return &VcNatResource{} -} - -func (r *VcNatResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_nat" -} - -func (r *VcNatResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcNatResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcNatModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcNatModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcNatModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{} - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 24, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcNatResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcNatModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 24, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{} - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 24, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 24, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNatResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcNatModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 24, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcNatResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcNatModel - var state VcNatModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 24, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNatResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcNatModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcNatResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcNatResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/25_vcexternalip_resource.go b/generated/dev/go/25_vcexternalip_resource.go deleted file mode 100644 index c375621..0000000 --- a/generated/dev/go/25_vcexternalip_resource.go +++ /dev/null @@ -1,604 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vcexternalip -// Service ID: 25 - -var _ resource.Resource = &VcexternalipResource{} -var _ resource.ResourceWithModifyPlan = &VcexternalipResource{} -var _ resource.ResourceWithImportState = &VcexternalipResource{} - -type VcexternalipResource struct { - client *core.UniversalClient -} - -type VcexternalipModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DnatCreate types.Bool `tfsdk:"dnat_create"` - FromServiceCloudEdgeName types.String `tfsdk:"from_service_cloud_edge_name"` - FromServiceCloudEdgeScope types.String `tfsdk:"from_service_cloud_edge_scope"` - FromServiceCloudOrgName types.String `tfsdk:"from_service_cloud_org_name"` - FromServiceCloudVdcName types.String `tfsdk:"from_service_cloud_vdc_name"` - FromServiceCloudVmwareUrl types.String `tfsdk:"from_service_cloud_vmware_url"` - FromServiceNamespace types.String `tfsdk:"from_service_namespace"` - FromServiceVdcGroupName types.String `tfsdk:"from_service_vdc_group_name"` - InternalAddrAccess types.String `tfsdk:"internal_addr_access"` - InternalPortAccess types.String `tfsdk:"internal_port_access"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ServiceUid types.String `tfsdk:"service_uid"` - SnatCreate types.Bool `tfsdk:"snat_create"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcexternalipResource() resource.Resource { - return &VcexternalipResource{} -} - -func (r *VcexternalipResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vcexternalip" -} - -func (r *VcexternalipResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "dnat_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходимо создать DNAT-правило."}, - "from_service_cloud_edge_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_edge_scope": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "from_service_cloud_org_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Org составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_vdc_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_vmware_url": schema.StringAttribute{Optional: true, MarkdownDescription: "Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_namespace": schema.StringAttribute{Required: true, MarkdownDescription: "Namespace составной услуги (для отображения правил FW) Не изменяется после создания."}, - "from_service_vdc_group_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "internal_addr_access": schema.StringAttribute{Required: true, MarkdownDescription: "Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true"}, - "internal_port_access": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("5432"), MarkdownDescription: "Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
"}, - "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "service_uid": schema.StringAttribute{Required: true}, - "snat_create": schema.BoolAttribute{Optional: true, MarkdownDescription: "Необходимо создать SNAT-правило.
"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcexternalipResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcexternalipModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.FromServiceCloudEdgeName.IsNull() && !plan.FromServiceCloudEdgeName.IsUnknown() && !state.FromServiceCloudEdgeName.IsNull() && !state.FromServiceCloudEdgeName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudEdgeName.ValueString(), state.FromServiceCloudEdgeName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudEdgeScope.IsNull() && !plan.FromServiceCloudEdgeScope.IsUnknown() && !state.FromServiceCloudEdgeScope.IsNull() && !state.FromServiceCloudEdgeScope.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudEdgeScope.ValueString(), state.FromServiceCloudEdgeScope.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_scope", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudOrgName.IsNull() && !plan.FromServiceCloudOrgName.IsUnknown() && !state.FromServiceCloudOrgName.IsNull() && !state.FromServiceCloudOrgName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudOrgName.ValueString(), state.FromServiceCloudOrgName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_org_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudVdcName.IsNull() && !plan.FromServiceCloudVdcName.IsUnknown() && !state.FromServiceCloudVdcName.IsNull() && !state.FromServiceCloudVdcName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudVdcName.ValueString(), state.FromServiceCloudVdcName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vdc_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudVmwareUrl.IsNull() && !plan.FromServiceCloudVmwareUrl.IsUnknown() && !state.FromServiceCloudVmwareUrl.IsNull() && !state.FromServiceCloudVmwareUrl.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudVmwareUrl.ValueString(), state.FromServiceCloudVmwareUrl.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vmware_url", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceNamespace.IsNull() && !plan.FromServiceNamespace.IsUnknown() && !state.FromServiceNamespace.IsNull() && !state.FromServiceNamespace.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceNamespace.ValueString(), state.FromServiceNamespace.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_namespace", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceVdcGroupName.IsNull() && !plan.FromServiceVdcGroupName.IsUnknown() && !state.FromServiceVdcGroupName.IsNull() && !state.FromServiceVdcGroupName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceVdcGroupName.ValueString(), state.FromServiceVdcGroupName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_vdc_group_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.FromServiceNamespace.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") - return - } - if config.FromServiceNamespace.IsUnknown() { - return - } - if config.InternalAddrAccess.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") - return - } - if config.InternalAddrAccess.IsUnknown() { - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.ServiceUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") - return - } - if config.ServiceUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 140: resources_core.FormatString(config.ServiceUid), - 141: resources_core.FormatBool(config.DnatCreate), - 142: resources_core.FormatString(config.InternalPortAccess), - 143: resources_core.FormatBool(config.SnatCreate), - 144: resources_core.FormatString(config.InternalAddrAccess), - 324: resources_core.FormatString(config.FromServiceNamespace), - 325: resources_core.FormatString(config.FromServiceCloudEdgeName), - 326: resources_core.FormatString(config.FromServiceCloudVdcName), - 327: resources_core.FormatString(config.FromServiceCloudOrgName), - 328: resources_core.FormatString(config.FromServiceCloudVmwareUrl), - 336: resources_core.FormatString(config.IpSpaceName), - 488: resources_core.FormatString(config.ResourceRealm), - 624: resources_core.FormatString(config.FromServiceCloudEdgeScope), - 626: resources_core.FormatString(config.FromServiceVdcGroupName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 25, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcexternalipResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.FromServiceNamespace.IsNull() || data.FromServiceNamespace.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") - return - } - if data.InternalAddrAccess.IsNull() || data.InternalAddrAccess.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.ServiceUid.IsNull() || data.ServiceUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 140: resources_core.FormatString(data.ServiceUid), - 141: resources_core.FormatBool(data.DnatCreate), - 142: resources_core.FormatString(data.InternalPortAccess), - 143: resources_core.FormatBool(data.SnatCreate), - 144: resources_core.FormatString(data.InternalAddrAccess), - 324: resources_core.FormatString(data.FromServiceNamespace), - 325: resources_core.FormatString(data.FromServiceCloudEdgeName), - 326: resources_core.FormatString(data.FromServiceCloudVdcName), - 327: resources_core.FormatString(data.FromServiceCloudOrgName), - 328: resources_core.FormatString(data.FromServiceCloudVmwareUrl), - 336: resources_core.FormatString(data.IpSpaceName), - 488: resources_core.FormatString(data.ResourceRealm), - 624: resources_core.FormatString(data.FromServiceCloudEdgeScope), - 626: resources_core.FormatString(data.FromServiceVdcGroupName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 25, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcexternalipResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 25, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcexternalipResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcexternalipModel - var state VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ServiceUid.IsNull() != state.ServiceUid.IsNull() || plan.ServiceUid.IsUnknown() != state.ServiceUid.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ServiceUid.IsNull() && !plan.ServiceUid.IsUnknown() && !state.ServiceUid.IsNull() && !state.ServiceUid.IsUnknown() { - if !strings.EqualFold(plan.ServiceUid.ValueString(), state.ServiceUid.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.DnatCreate.IsNull() != state.DnatCreate.IsNull() || plan.DnatCreate.IsUnknown() != state.DnatCreate.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.DnatCreate.IsNull() && !plan.DnatCreate.IsUnknown() && !state.DnatCreate.IsNull() && !state.DnatCreate.IsUnknown() { - if plan.DnatCreate.ValueBool() != state.DnatCreate.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.SnatCreate.IsNull() != state.SnatCreate.IsNull() || plan.SnatCreate.IsUnknown() != state.SnatCreate.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.SnatCreate.IsNull() && !plan.SnatCreate.IsUnknown() && !state.SnatCreate.IsNull() && !state.SnatCreate.IsUnknown() { - if plan.SnatCreate.ValueBool() != state.SnatCreate.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.InternalPortAccess.IsNull() != state.InternalPortAccess.IsNull() || plan.InternalPortAccess.IsUnknown() != state.InternalPortAccess.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.InternalPortAccess.IsNull() && !plan.InternalPortAccess.IsUnknown() && !state.InternalPortAccess.IsNull() && !state.InternalPortAccess.IsUnknown() { - if !strings.EqualFold(plan.InternalPortAccess.ValueString(), state.InternalPortAccess.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.InternalAddrAccess.IsNull() != state.InternalAddrAccess.IsNull() || plan.InternalAddrAccess.IsUnknown() != state.InternalAddrAccess.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.InternalAddrAccess.IsNull() && !plan.InternalAddrAccess.IsUnknown() && !state.InternalAddrAccess.IsNull() && !state.InternalAddrAccess.IsUnknown() { - if !strings.EqualFold(plan.InternalAddrAccess.ValueString(), state.InternalAddrAccess.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 627: resources_core.FormatString(plan.ServiceUid), - 628: resources_core.FormatString(plan.IpSpaceName), - 629: resources_core.FormatBool(plan.DnatCreate), - 630: resources_core.FormatBool(plan.SnatCreate), - 631: resources_core.FormatString(plan.InternalPortAccess), - 632: resources_core.FormatString(plan.InternalAddrAccess), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 25, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcexternalipResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcexternalipResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcexternalipResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/26_vapp_resource.go b/generated/dev/go/26_vapp_resource.go deleted file mode 100644 index e210b7f..0000000 --- a/generated/dev/go/26_vapp_resource.go +++ /dev/null @@ -1,571 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vapp -// Service ID: 26 - -var _ resource.Resource = &VappResource{} -var _ resource.ResourceWithModifyPlan = &VappResource{} -var _ resource.ResourceWithImportState = &VappResource{} - -type VappResource struct { - client *core.UniversalClient -} - -type VappModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - NsxtUid types.String `tfsdk:"nsxt_uid"` - VappName types.String `tfsdk:"vapp_name"` - VdcUid types.String `tfsdk:"vdc_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVappResource() resource.Resource { - return &VappResource{} -} - -func (r *VappResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vapp" -} - -func (r *VappResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "nsxt_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Сетевой шлюз периметра (Edge)` Не изменяется после создания."}, - "vapp_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("9pv2jybu"), MarkdownDescription: "Уникальное в рамках организации Не изменяется после создания."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VappResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VappModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NsxtUid.ValueString(), state.NsxtUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить nsxt_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappName.ValueString(), state.VappName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.NsxtUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") - return - } - if config.NsxtUid.IsUnknown() { - return - } - if config.VdcUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - if config.VdcUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.NsxtUid.IsNull() && !config.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, config.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != config.NsxtUid.ValueString() { - config.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 190: resources_core.FormatString(config.NsxtUid), - 191: resources_core.FormatString(config.VappName), - 623: resources_core.FormatString(config.VdcUid), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 26, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VappResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.NsxtUid.IsNull() || data.NsxtUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") - return - } - if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalNsxtUid := data.NsxtUid - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.NsxtUid.IsNull() && !data.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, data.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedNsxtUid != "" && resolvedNsxtUid != data.NsxtUid.ValueString() { - data.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 190: resources_core.FormatString(data.NsxtUid), - 191: resources_core.FormatString(data.VappName), - 623: resources_core.FormatString(data.VdcUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 26, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { - state.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalNsxtUid.IsNull() && !originalNsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - if strings.EqualFold(state.NsxtUid.ValueString(), originalNsxtUid.ValueString()) { - state.NsxtUid = originalNsxtUid - } - } - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VappResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 26, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, newState.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != newState.NsxtUid.ValueString() { - newState.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() && !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { - if strings.EqualFold(newState.NsxtUid.ValueString(), state.NsxtUid.ValueString()) { - newState.NsxtUid = state.NsxtUid - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VappResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VappModel - var state VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 26, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { - state.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - if strings.EqualFold(state.NsxtUid.ValueString(), plan.NsxtUid.ValueString()) { - state.NsxtUid = plan.NsxtUid - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VappResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VappResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VappResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/27_vc_vm_v2_resource.go b/generated/dev/go/27_vc_vm_v2_resource.go deleted file mode 100644 index 6a05554..0000000 --- a/generated/dev/go/27_vc_vm_v2_resource.go +++ /dev/null @@ -1,350 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v2 -// Service ID: 27 - -var _ resource.Resource = &VcVmV2Resource{} -var _ resource.ResourceWithModifyPlan = &VcVmV2Resource{} -var _ resource.ResourceWithImportState = &VcVmV2Resource{} - -type VcVmV2Resource struct { - client *core.UniversalClient -} - -type VcVmV2Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ServerListJson types.String `tfsdk:"server_list_json"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmV2Resource() resource.Resource { - return &VcVmV2Resource{} -} - -func (r *VcVmV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v2" -} - -func (r *VcVmV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "server_list_json": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmV2Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 301: resources_core.FormatString(config.ServerListJson), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 27, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 301: resources_core.FormatString(data.ServerListJson), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 27, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 27, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV2Model - var state VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ServerListJson.IsNull() != state.ServerListJson.IsNull() || plan.ServerListJson.IsUnknown() != state.ServerListJson.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ServerListJson.IsNull() && !plan.ServerListJson.IsUnknown() && !state.ServerListJson.IsNull() && !state.ServerListJson.IsUnknown() { - if !strings.EqualFold(plan.ServerListJson.ValueString(), state.ServerListJson.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 302: resources_core.FormatString(plan.ServerListJson), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 27, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/28_vc_vm_v3_resource.go b/generated/dev/go/28_vc_vm_v3_resource.go deleted file mode 100644 index 61fe97c..0000000 --- a/generated/dev/go/28_vc_vm_v3_resource.go +++ /dev/null @@ -1,697 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v3 -// Service ID: 28 - -var _ resource.Resource = &VcVmV3Resource{} -var _ resource.ResourceWithModifyPlan = &VcVmV3Resource{} -var _ resource.ResourceWithImportState = &VcVmV3Resource{} - -type VcVmV3Resource struct { - client *core.UniversalClient -} - -type VcVmV3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessIpList types.String `tfsdk:"access_ip_list"` - AccessPortList types.String `tfsdk:"access_port_list"` - CloudInit types.String `tfsdk:"cloud_init"` - ImageVm types.String `tfsdk:"image_vm"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - UserLogin types.String `tfsdk:"user_login"` - UserPublicKey types.String `tfsdk:"user_public_key"` - VappUid types.String `tfsdk:"vapp_uid"` - VmCpu types.Int64 `tfsdk:"vm_cpu"` - VmDisk types.Int64 `tfsdk:"vm_disk"` - VmName types.String `tfsdk:"vm_name"` - VmRam types.Int64 `tfsdk:"vm_ram"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmV3Resource() resource.Resource { - return &VcVmV3Resource{} -} - -func (r *VcVmV3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v3" -} - -func (r *VcVmV3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "access_port_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"22\"]"), MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, - "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. Не изменяется после создания."}, - "image_vm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Required: true}, - "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myuser"), MarkdownDescription: "Учётка SSH Не изменяется после создания."}, - "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=26 (mapped to UUID in core) Не изменяется после создания."}, - "vm_cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "шт"}, - "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Не изменяется после создания."}, - "vm_ram": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Gb"}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmV3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmV3Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CloudInit.IsNull() && !plan.CloudInit.IsUnknown() && !state.CloudInit.IsNull() && !state.CloudInit.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.CloudInit.ValueString(), state.CloudInit.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить cloud_init", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ImageVm.IsNull() && !plan.ImageVm.IsUnknown() && !state.ImageVm.IsNull() && !state.ImageVm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ImageVm.ValueString(), state.ImageVm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить image_vm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserLogin.ValueString(), state.UserLogin.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserPublicKey.IsNull() && !plan.UserPublicKey.IsUnknown() && !state.UserPublicKey.IsNull() && !state.UserPublicKey.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserPublicKey.ValueString(), state.UserPublicKey.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_public_key", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappUid.ValueString(), state.VappUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmName.ValueString(), state.VmName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ImageVm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") - return - } - if config.ImageVm.IsUnknown() { - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.UserPublicKey.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") - return - } - if config.UserPublicKey.IsUnknown() { - return - } - if config.VappUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - if config.VappUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { - config.VappUid = types.StringValue(resolvedVappUid) - } - } - params := map[int]string{ - 407: resources_core.FormatString(config.VappUid), - 408: resources_core.FormatString(config.VmName), - 409: resources_core.FormatInt64(config.VmCpu), - 410: resources_core.FormatInt64(config.VmRam), - 411: resources_core.FormatInt64(config.VmDisk), - 412: resources_core.FormatString(config.IpSpaceName), - 413: resources_core.FormatString(config.AccessIpList), - 414: resources_core.FormatString(config.ImageVm), - 415: resources_core.FormatString(config.CloudInit), - 416: resources_core.FormatString(config.UserLogin), - 417: resources_core.FormatString(config.UserPublicKey), - 448: resources_core.FormatString(config.AccessPortList), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 28, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmV3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ImageVm.IsNull() || data.ImageVm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.UserPublicKey.IsNull() || data.UserPublicKey.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") - return - } - if data.VappUid.IsNull() || data.VappUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVappUid := data.VappUid - if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { - data.VappUid = types.StringValue(resolvedVappUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 407: resources_core.FormatString(data.VappUid), - 408: resources_core.FormatString(data.VmName), - 409: resources_core.FormatInt64(data.VmCpu), - 410: resources_core.FormatInt64(data.VmRam), - 411: resources_core.FormatInt64(data.VmDisk), - 412: resources_core.FormatString(data.IpSpaceName), - 413: resources_core.FormatString(data.AccessIpList), - 414: resources_core.FormatString(data.ImageVm), - 415: resources_core.FormatString(data.CloudInit), - 416: resources_core.FormatString(data.UserLogin), - 417: resources_core.FormatString(data.UserPublicKey), - 448: resources_core.FormatString(data.AccessPortList), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 28, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVappUid.IsNull() && !originalVappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), originalVappUid.ValueString()) { - state.VappUid = originalVappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 28, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { - newState.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() && !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - if strings.EqualFold(newState.VappUid.ValueString(), state.VappUid.ValueString()) { - newState.VappUid = state.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV3Model - var state VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { - if plan.VmCpu.ValueInt64() != state.VmCpu.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { - if plan.VmRam.ValueInt64() != state.VmRam.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmDisk.IsNull() != state.VmDisk.IsNull() || plan.VmDisk.IsUnknown() != state.VmDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmDisk.IsNull() && !plan.VmDisk.IsUnknown() && !state.VmDisk.IsNull() && !state.VmDisk.IsUnknown() { - if plan.VmDisk.ValueInt64() != state.VmDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { - if !strings.EqualFold(plan.AccessIpList.ValueString(), state.AccessIpList.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessPortList.IsNull() != state.AccessPortList.IsNull() || plan.AccessPortList.IsUnknown() != state.AccessPortList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessPortList.IsNull() && !plan.AccessPortList.IsUnknown() && !state.AccessPortList.IsNull() && !state.AccessPortList.IsUnknown() { - if !strings.EqualFold(plan.AccessPortList.ValueString(), state.AccessPortList.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 493: resources_core.FormatInt64(plan.VmCpu), - 494: resources_core.FormatInt64(plan.VmRam), - 495: resources_core.FormatInt64(plan.VmDisk), - 496: resources_core.FormatString(plan.IpSpaceName), - 497: resources_core.FormatString(plan.AccessIpList), - 498: resources_core.FormatString(plan.AccessPortList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 28, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), plan.VappUid.ValueString()) { - state.VappUid = plan.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmV3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmV3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/29_vc_vdc_group_resource.go b/generated/dev/go/29_vc_vdc_group_resource.go deleted file mode 100644 index 41864ef..0000000 --- a/generated/dev/go/29_vc_vdc_group_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc_group -// Service ID: 29 - -var _ resource.Resource = &VcVdcGroupResource{} -var _ resource.ResourceWithModifyPlan = &VcVdcGroupResource{} -var _ resource.ResourceWithImportState = &VcVdcGroupResource{} - -type VcVdcGroupResource struct { - client *core.UniversalClient -} - -type VcVdcGroupModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - VdcUid types.String `tfsdk:"vdc_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVdcGroupResource() resource.Resource { - return &VcVdcGroupResource{} -} - -func (r *VcVdcGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc_group" -} - -func (r *VcVdcGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core) Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVdcGroupModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VdcUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - if config.VdcUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 618: resources_core.FormatString(config.VdcUid), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 29, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVdcGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 618: resources_core.FormatString(data.VdcUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 29, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 29, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVdcGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcGroupModel - var state VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 29, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVdcGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVdcGroupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/29_vc_vdc_group_vdc_resource.go b/generated/dev/go/29_vc_vdc_group_vdc_resource.go deleted file mode 100644 index 3e3e8ef..0000000 --- a/generated/dev/go/29_vc_vdc_group_vdc_resource.go +++ /dev/null @@ -1,326 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc_group -// Subresource: vdc - -var _ resource.Resource = &VcVdcGroupVdcResource{} - -// VcVdcGroupVdcResource manages a subresource via operations. -type VcVdcGroupVdcResource struct { - client *core.UniversalClient -} - -type VcVdcGroupVdcModel struct { - ID types.String `tfsdk:"id"` - VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewVcVdcGroupVdcResource() resource.Resource { - return &VcVdcGroupVdcResource{} -} - -func (r *VcVdcGroupVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc_group_vdc" -} - -func (r *VcVdcGroupVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "vc_vdc_group_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcGroupVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcVdcGroupVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("vdc", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcVdcGroupVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcGroupVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcVdcGroupVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.VcVdcGroupID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("vdc", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *VcVdcGroupVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/2_template_resource.go b/generated/dev/go/2_template_resource.go deleted file mode 100644 index d3e71ae..0000000 --- a/generated/dev/go/2_template_resource.go +++ /dev/null @@ -1,397 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: template -// Service ID: 2 - -var _ resource.Resource = &TemplateResource{} -var _ resource.ResourceWithModifyPlan = &TemplateResource{} -var _ resource.ResourceWithImportState = &TemplateResource{} - -type TemplateResource struct { - client *core.UniversalClient -} - -// TemplateAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type TemplateAccessConfigurationModel struct { - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` - Type types.String `tfsdk:"type" json:"type"` - Domain types.String `tfsdk:"domain" json:"domain"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` -} - -// TemplateClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type TemplateClusterConfigurationModel struct { - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// TemplateStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type TemplateStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type TemplateModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *TemplateAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *TemplateClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *TemplateStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewTemplateResource() resource.Resource { - return &TemplateResource{} -} - -func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_template" -} - -func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]")}, - "type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ingress")}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abracadabra-mgonny3n")}, - "master_ip_space": schema.StringAttribute{Required: true}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]")}, - "slave_ip_space": schema.StringAttribute{Required: true}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(5)}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(128)}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *TemplateResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *TemplateModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 813: resources_core.BuildJSON(map[string]string{"disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 814: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 815: resources_core.BuildJSON(map[string]string{"slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString()), "type": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 2, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *TemplateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 813: resources_core.BuildJSON(map[string]string{"disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 814: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 815: resources_core.BuildJSON(map[string]string{"slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString()), "type": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 2, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 2, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TemplateModel - var state TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 816: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 817: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString()), "type": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 2, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *TemplateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/32_vmpostgre_resource.go b/generated/dev/go/32_vmpostgre_resource.go deleted file mode 100644 index 5d39c90..0000000 --- a/generated/dev/go/32_vmpostgre_resource.go +++ /dev/null @@ -1,514 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vmpostgre -// Service ID: 32 - -var _ resource.Resource = &VmpostgreResource{} -var _ resource.ResourceWithModifyPlan = &VmpostgreResource{} -var _ resource.ResourceWithImportState = &VmpostgreResource{} - -type VmpostgreResource struct { - client *core.UniversalClient -} - -type VmpostgreModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - UserDb types.String `tfsdk:"user_db"` - UserLogin types.String `tfsdk:"user_login"` - UserPassword types.String `tfsdk:"user_password"` - VmUid types.String `tfsdk:"vm_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVmpostgreResource() resource.Resource { - return &VmpostgreResource{} -} - -func (r *VmpostgreResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vmpostgre" -} - -func (r *VmpostgreResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "user_db": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("servicedb"), MarkdownDescription: "Наименование Базы данных
Не изменяется после создания."}, - "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("serviceuser"), MarkdownDescription: "Пользователь базы данных
Не изменяется после создания."}, - "user_password": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("fj29gm48fm!d374mc9"), MarkdownDescription: "Пароль от доступа к Базе данных
Не изменяется после создания."}, - "vm_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "InstanceUid ВМ. ВМ должна быть создана
Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VmpostgreResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VmpostgreModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserDb.IsNull() && !plan.UserDb.IsUnknown() && !state.UserDb.IsNull() && !state.UserDb.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserDb.ValueString(), state.UserDb.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_db", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserLogin.ValueString(), state.UserLogin.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserPassword.IsNull() && !plan.UserPassword.IsUnknown() && !state.UserPassword.IsNull() && !state.UserPassword.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserPassword.ValueString(), state.UserPassword.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_password", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmUid.IsNull() && !plan.VmUid.IsUnknown() && !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmUid.ValueString(), state.VmUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VmUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vm_uid is required.") - return - } - if config.VmUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VmUid.IsNull() && !config.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, config.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) - } else if resolvedVmUid != "" && resolvedVmUid != config.VmUid.ValueString() { - config.VmUid = types.StringValue(resolvedVmUid) - } - } - params := map[int]string{ - 45: resources_core.FormatString(config.VmUid), - 46: resources_core.FormatString(config.UserDb), - 47: resources_core.FormatString(config.UserLogin), - 48: resources_core.FormatString(config.UserPassword), - 278: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 32, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VmpostgreResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VmUid.IsNull() || data.VmUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vm_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVmUid := data.VmUid - if !data.VmUid.IsNull() && !data.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, data.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVmUid != "" && resolvedVmUid != data.VmUid.ValueString() { - data.VmUid = types.StringValue(resolvedVmUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 32, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 45: resources_core.FormatString(data.VmUid), - 46: resources_core.FormatString(data.UserDb), - 47: resources_core.FormatString(data.UserLogin), - 48: resources_core.FormatString(data.UserPassword), - 278: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 32, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 32, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "userDb", Field: "UserDb", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPassword", Field: "UserPassword", Type: "string"}, - {Code: "vmUid", Field: "VmUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, state.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) - } else if resolvedVmUid != "" && resolvedVmUid != state.VmUid.ValueString() { - state.VmUid = types.StringValue(resolvedVmUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVmUid.IsNull() && !originalVmUid.IsUnknown() && !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - if strings.EqualFold(state.VmUid.ValueString(), originalVmUid.ValueString()) { - state.VmUid = originalVmUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VmpostgreResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 32, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "userDb", Field: "UserDb", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPassword", Field: "UserPassword", Type: "string"}, - {Code: "vmUid", Field: "VmUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VmUid.IsNull() && !newState.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, newState.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) - } else if resolvedVmUid != "" && resolvedVmUid != newState.VmUid.ValueString() { - newState.VmUid = types.StringValue(resolvedVmUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VmUid.IsNull() && !state.VmUid.IsUnknown() && !newState.VmUid.IsNull() && !newState.VmUid.IsUnknown() { - if strings.EqualFold(newState.VmUid.ValueString(), state.VmUid.ValueString()) { - newState.VmUid = state.VmUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VmpostgreResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VmpostgreModel - var state VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 32, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "userDb", Field: "UserDb", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPassword", Field: "UserPassword", Type: "string"}, - {Code: "vmUid", Field: "VmUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - resolvedVmUid, err := r.client.ResolveRefSvcParamValue(ctx, 23, state.VmUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vm_uid", err.Error()) - } else if resolvedVmUid != "" && resolvedVmUid != state.VmUid.ValueString() { - state.VmUid = types.StringValue(resolvedVmUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VmUid.IsNull() && !plan.VmUid.IsUnknown() && !state.VmUid.IsNull() && !state.VmUid.IsUnknown() { - if strings.EqualFold(state.VmUid.ValueString(), plan.VmUid.ValueString()) { - state.VmUid = plan.VmUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VmpostgreResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VmpostgreResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VmpostgreResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/50_nextcloud_backup_resource.go b/generated/dev/go/50_nextcloud_backup_resource.go deleted file mode 100644 index feb1f0e..0000000 --- a/generated/dev/go/50_nextcloud_backup_resource.go +++ /dev/null @@ -1,344 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Subresource: backup - -var _ resource.Resource = &NextcloudBackupResource{} - -// NextcloudBackupResource manages a subresource via operations. -type NextcloudBackupResource struct { - client *core.UniversalClient -} - -type NextcloudBackupModel struct { - ID types.String `tfsdk:"id"` - NextcloudID types.String `tfsdk:"nextcloud_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` -} - -func NewNextcloudBackupResource() resource.Resource { - return &NextcloudBackupResource{} -} - -func (r *NextcloudBackupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud_backup" -} - -func (r *NextcloudBackupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "nextcloud_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudBackupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan NextcloudBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - } - identityCodes := []string{ - "startupConfiguration", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudBackupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudBackupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudBackupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.NextcloudID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(state.StartupConfiguration), - } - identityCodes := []string{ - "startupConfiguration", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *NextcloudBackupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/50_nextcloud_resource.go b/generated/dev/go/50_nextcloud_resource.go deleted file mode 100644 index 3e5c608..0000000 --- a/generated/dev/go/50_nextcloud_resource.go +++ /dev/null @@ -1,414 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Service ID: 50 - -var _ resource.Resource = &NextcloudResource{} -var _ resource.ResourceWithModifyPlan = &NextcloudResource{} -var _ resource.ResourceWithImportState = &NextcloudResource{} - -type NextcloudResource struct { - client *core.UniversalClient -} - -// NextcloudAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type NextcloudAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// NextcloudAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type NextcloudAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - DocumentServer types.String `tfsdk:"document_server" json:"documentServer"` - LogLevel types.String `tfsdk:"log_level" json:"logLevel"` -} - -// NextcloudClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type NextcloudClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// NextcloudStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type NextcloudStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` - PsqlEnabled types.Bool `tfsdk:"psql_enabled" json:"psqlEnabled"` - RedisEnabled types.Bool `tfsdk:"redis_enabled" json:"redisEnabled"` - PsqlUid types.String `tfsdk:"psql_uid" json:"psqlUid"` - RedisUid types.String `tfsdk:"redis_uid" json:"redisUid"` -} - -type NextcloudModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *NextcloudAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *NextcloudAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *NextcloudClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *NextcloudStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNextcloudResource() resource.Resource { - return &NextcloudResource{} -} - -func (r *NextcloudResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud" -} - -func (r *NextcloudResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nextcloud-xcq3u70l"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("33.0.4")}, - "document_server": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("collabora"), MarkdownDescription: "Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ"}, - "log_level": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("INFO"), MarkdownDescription: "Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - "psql_enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud"}, - "redis_enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud"}, - "psql_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета`"}, - "redis_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета`"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NextcloudModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1055: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString()), "psqlEnabled": fmt.Sprintf("%v", config.StartupConfiguration.PsqlEnabled.ValueBool()), "redisEnabled": fmt.Sprintf("%v", config.StartupConfiguration.RedisEnabled.ValueBool()), "psqlUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.PsqlUid.ValueString()), "redisUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.RedisUid.ValueString())}), - 1056: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1057: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1058: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", config.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", config.AppConfiguration.LogLevel.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 50, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NextcloudResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1055: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString()), "psqlEnabled": fmt.Sprintf("%v", data.StartupConfiguration.PsqlEnabled.ValueBool()), "redisEnabled": fmt.Sprintf("%v", data.StartupConfiguration.RedisEnabled.ValueBool()), "psqlUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.PsqlUid.ValueString()), "redisUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.RedisUid.ValueString())}), - 1056: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1057: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1058: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", data.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", data.AppConfiguration.LogLevel.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 50, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 50, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NextcloudResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudModel - var state NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1059: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1060: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", plan.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", plan.AppConfiguration.LogLevel.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 50, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NextcloudResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NextcloudResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/81_superset_resource.go b/generated/dev/go/81_superset_resource.go deleted file mode 100644 index 94602e6..0000000 --- a/generated/dev/go/81_superset_resource.go +++ /dev/null @@ -1,447 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: superset -// Service ID: 81 - -var _ resource.Resource = &SupersetResource{} -var _ resource.ResourceWithModifyPlan = &SupersetResource{} -var _ resource.ResourceWithImportState = &SupersetResource{} - -type SupersetResource struct { - client *core.UniversalClient -} - -type SupersetModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - Emails types.String `tfsdk:"emails"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewSupersetResource() resource.Resource { - return &SupersetResource{} -} - -func (r *SupersetResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_superset" -} - -func (r *SupersetResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("superset-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "emails": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("testuser1"), MarkdownDescription: "Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *SupersetResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *SupersetModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Emails.IsNull() && !plan.Emails.IsUnknown() && !state.Emails.IsNull() && !state.Emails.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Emails.ValueString(), state.Emails.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить emails", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 14: resources_core.FormatString(config.Domain), - 20: resources_core.FormatString(config.Emails), - 96: resources_core.FormatInt64(config.ResourceCPU), - 97: resources_core.FormatInt64(config.ResourceMemory), - 98: resources_core.FormatInt64(config.ResourceDisk), - 109: resources_core.FormatString(config.ResourceRealm), - 227: resources_core.FormatInt64(config.ResourceInstances), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 81, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *SupersetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 14: resources_core.FormatString(data.Domain), - 20: resources_core.FormatString(data.Emails), - 96: resources_core.FormatInt64(data.ResourceCPU), - 97: resources_core.FormatInt64(data.ResourceMemory), - 98: resources_core.FormatInt64(data.ResourceDisk), - 109: resources_core.FormatString(data.ResourceRealm), - 227: resources_core.FormatInt64(data.ResourceInstances), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 81, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *SupersetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 81, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *SupersetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan SupersetModel - var state SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 81, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *SupersetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *SupersetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *SupersetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/82_harbor_resource.go b/generated/dev/go/82_harbor_resource.go deleted file mode 100644 index b53b4c3..0000000 --- a/generated/dev/go/82_harbor_resource.go +++ /dev/null @@ -1,482 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: harbor -// Service ID: 82 - -var _ resource.Resource = &HarborResource{} -var _ resource.ResourceWithModifyPlan = &HarborResource{} -var _ resource.ResourceWithImportState = &HarborResource{} - -type HarborResource struct { - client *core.UniversalClient -} - -type HarborModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - S3Uid types.String `tfsdk:"s3_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewHarborResource() resource.Resource { - return &HarborResource{} -} - -func (r *HarborResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_harbor" -} - -func (r *HarborResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("harbor-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "s3_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID экземпляра услуги S3 Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *HarborResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *HarborModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.S3Uid.ValueString(), state.S3Uid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - if config.S3Uid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") - return - } - if config.S3Uid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { - resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) - } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { - config.S3Uid = types.StringValue(resolvedS3Uid) - } - } - params := map[int]string{ - 16: resources_core.FormatString(config.Domain), - 110: resources_core.FormatString(config.ResourceRealm), - 228: resources_core.FormatInt64(config.ResourceCPU), - 229: resources_core.FormatInt64(config.ResourceMemory), - 230: resources_core.FormatInt64(config.ResourceInstances), - 231: resources_core.FormatString(config.S3Uid), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 82, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *HarborResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 16: resources_core.FormatString(data.Domain), - 110: resources_core.FormatString(data.ResourceRealm), - 228: resources_core.FormatInt64(data.ResourceCPU), - 229: resources_core.FormatInt64(data.ResourceMemory), - 230: resources_core.FormatInt64(data.ResourceInstances), - 231: resources_core.FormatString(data.S3Uid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 82, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HarborResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 82, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *HarborResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HarborModel - var state HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 822: resources_core.FormatInt64(plan.ResourceInstances), - 823: resources_core.FormatInt64(plan.ResourceCPU), - 824: resources_core.FormatInt64(plan.ResourceMemory), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 82, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HarborResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *HarborResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *HarborResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/86_k8s_velero_resource.go b/generated/dev/go/86_k8s_velero_resource.go deleted file mode 100644 index 1a180ac..0000000 --- a/generated/dev/go/86_k8s_velero_resource.go +++ /dev/null @@ -1,401 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_velero -// Service ID: 86 - -var _ resource.Resource = &K8sVeleroResource{} -var _ resource.ResourceWithModifyPlan = &K8sVeleroResource{} -var _ resource.ResourceWithImportState = &K8sVeleroResource{} - -type K8sVeleroResource struct { - client *core.UniversalClient -} - -// K8sVeleroBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type K8sVeleroBackupConfigurationModel struct { - S3BucketUid types.String `tfsdk:"s3_bucket_uid" json:"s3BucketUid"` -} - -// K8sVeleroClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sVeleroClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// K8sVeleroNodeAgentConfigurationModel — вложенная модель для map-fixed параметра nodeAgentConfiguration. -type K8sVeleroNodeAgentConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// K8sVeleroStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sVeleroStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type K8sVeleroModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - BackupConfiguration *K8sVeleroBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *K8sVeleroClusterConfigurationModel `tfsdk:"cluster_configuration"` - NodeAgentConfiguration *K8sVeleroNodeAgentConfigurationModel `tfsdk:"node_agent_configuration"` - StartupConfiguration *K8sVeleroStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sVeleroResource() resource.Resource { - return &K8sVeleroResource{} -} - -func (r *K8sVeleroResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_velero" -} - -func (r *K8sVeleroResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_bucket_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3-Бакета обязателен
Учётная запись будет создана автоматически, если её нету"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - }}, - "node_agent_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sVeleroResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sVeleroModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 962: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 963: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 964: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.NodeAgentConfiguration.Memory.ValueInt64())}), - 1076: resources_core.BuildJSON(map[string]string{"s3BucketUid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3BucketUid.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 86, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sVeleroResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 86, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 962: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 963: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 964: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.NodeAgentConfiguration.Memory.ValueInt64())}), - 1076: resources_core.BuildJSON(map[string]string{"s3BucketUid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3BucketUid.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 86, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 86, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sVeleroResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 86, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sVeleroResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sVeleroModel - var state K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 965: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 966: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.NodeAgentConfiguration.Memory.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 86, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sVeleroResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sVeleroResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sVeleroResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/87_k8svalkey_resource.go b/generated/dev/go/87_k8svalkey_resource.go deleted file mode 100644 index 1ac11ca..0000000 --- a/generated/dev/go/87_k8svalkey_resource.go +++ /dev/null @@ -1,423 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8svalkey -// Service ID: 87 - -var _ resource.Resource = &K8svalkeyResource{} -var _ resource.ResourceWithModifyPlan = &K8svalkeyResource{} -var _ resource.ResourceWithImportState = &K8svalkeyResource{} - -type K8svalkeyResource struct { - client *core.UniversalClient -} - -// K8svalkeyAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type K8svalkeyAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// K8svalkeyClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8svalkeyClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Shards types.Int64 `tfsdk:"shards" json:"shards"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// K8svalkeyStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8svalkeyStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -// K8svalkeyValkeyConfigurationModel — вложенная модель для map-fixed параметра valkeyConfiguration. -type K8svalkeyValkeyConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -type K8svalkeyModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *K8svalkeyAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *K8svalkeyClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *K8svalkeyStartupConfigurationModel `tfsdk:"startup_configuration"` - ValkeyConf types.String `tfsdk:"valkey_conf"` - ValkeyConfiguration *K8svalkeyValkeyConfigurationModel `tfsdk:"valkey_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8svalkeyResource() resource.Resource { - return &K8svalkeyResource{} -} - -func (r *K8svalkeyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8svalkey" -} - -func (r *K8svalkeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "valkey_conf": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "valkey_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0.1.0")}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8svalkeyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8svalkeyModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8svalkeyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8svalkeyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ValkeyConf.IsNull() && !plan.ValkeyConf.IsUnknown() && !state.ValkeyConf.IsNull() && !state.ValkeyConf.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ValkeyConf.ValueString(), state.ValkeyConf.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить valkey_conf", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1066: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1067: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", config.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1068: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - 1071: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.ValkeyConfiguration.Version.ValueString())}), - 1072: resources_core.FormatString(config.ValkeyConf), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 87, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8svalkeyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8svalkeyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 87, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1066: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1067: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", data.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1068: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - 1071: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.ValkeyConfiguration.Version.ValueString())}), - 1072: resources_core.FormatString(data.ValkeyConf), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 87, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 87, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "valkeyConf", Field: "ValkeyConf", Type: "array-map-fixed"}, - {Code: "valkeyConfiguration", Field: "ValkeyConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8svalkeyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8svalkeyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 87, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "valkeyConf", Field: "ValkeyConf", Type: "array-map-fixed"}, - {Code: "valkeyConfiguration", Field: "ValkeyConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8svalkeyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8svalkeyModel - var state K8svalkeyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1069: resources_core.BuildJSON(map[string]string{"shards": fmt.Sprintf("%d", plan.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1070: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 87, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "valkeyConf", Field: "ValkeyConf", Type: "array-map-fixed"}, - {Code: "valkeyConfiguration", Field: "ValkeyConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8svalkeyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8svalkeyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8svalkeyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8svalkeyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/87_k8svalkey_user_resource.go b/generated/dev/go/87_k8svalkey_user_resource.go deleted file mode 100644 index 3dbe8e0..0000000 --- a/generated/dev/go/87_k8svalkey_user_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8svalkey -// Subresource: user - -var _ resource.Resource = &K8svalkeyUserResource{} - -// K8svalkeyUserResource manages a subresource via operations. -type K8svalkeyUserResource struct { - client *core.UniversalClient -} - -type K8svalkeyUserModel struct { - ID types.String `tfsdk:"id"` - K8svalkeyID types.String `tfsdk:"k8svalkey_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Permissions types.String `tfsdk:"permissions"` - Username types.String `tfsdk:"username"` -} - -func NewK8svalkeyUserResource() resource.Resource { - return &K8svalkeyUserResource{} -} - -func (r *K8svalkeyUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8svalkey_user" -} - -func (r *K8svalkeyUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8svalkey_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "permissions": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8svalkeyUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8svalkeyUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Permissions.IsNull() || plan.Permissions.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "permissions is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.K8svalkeyID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "permissions": resources_core.FormatString(plan.Permissions), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "permissions", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "permissions": resources_core.FormatString(plan.Permissions), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8svalkeyUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8svalkeyUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8svalkeyUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8svalkeyUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8svalkeyUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8svalkeyID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "permissions": resources_core.FormatString(plan.Permissions), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8svalkeyUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8svalkeyUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8svalkeyID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "permissions": resources_core.FormatString(state.Permissions), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "permissions", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8svalkeyUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/88_k8s_ziti_controller_resource.go b/generated/dev/go/88_k8s_ziti_controller_resource.go deleted file mode 100644 index 234875e..0000000 --- a/generated/dev/go/88_k8s_ziti_controller_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_ziti_controller -// Service ID: 88 - -var _ resource.Resource = &K8sZitiControllerResource{} -var _ resource.ResourceWithModifyPlan = &K8sZitiControllerResource{} -var _ resource.ResourceWithImportState = &K8sZitiControllerResource{} - -type K8sZitiControllerResource struct { - client *core.UniversalClient -} - -type K8sZitiControllerModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sZitiControllerResource() resource.Resource { - return &K8sZitiControllerResource{} -} - -func (r *K8sZitiControllerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_ziti_controller" -} - -func (r *K8sZitiControllerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8sZitiController-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores) Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sZitiControllerResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sZitiControllerModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 294: resources_core.FormatInt64(config.ResourceCPU), - 295: resources_core.FormatInt64(config.ResourceMemory), - 296: resources_core.FormatInt64(config.ResourceDisk), - 297: resources_core.FormatInt64(config.ResourceInstances), - 298: resources_core.FormatString(config.ResourceRealm), - 299: resources_core.FormatString(config.Domain), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 88, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sZitiControllerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 294: resources_core.FormatInt64(data.ResourceCPU), - 295: resources_core.FormatInt64(data.ResourceMemory), - 296: resources_core.FormatInt64(data.ResourceDisk), - 297: resources_core.FormatInt64(data.ResourceInstances), - 298: resources_core.FormatString(data.ResourceRealm), - 299: resources_core.FormatString(data.Domain), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 88, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sZitiControllerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 88, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sZitiControllerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sZitiControllerModel - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 88, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sZitiControllerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sZitiControllerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sZitiControllerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/89_flask_resource.go b/generated/dev/go/89_flask_resource.go deleted file mode 100644 index f6624df..0000000 --- a/generated/dev/go/89_flask_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: flask -// Service ID: 89 - -var _ resource.Resource = &FlaskResource{} -var _ resource.ResourceWithModifyPlan = &FlaskResource{} -var _ resource.ResourceWithImportState = &FlaskResource{} - -type FlaskResource struct { - client *core.UniversalClient -} - -// FlaskAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type FlaskAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// FlaskAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type FlaskAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// FlaskClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type FlaskClusterConfigurationModel struct { - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// FlaskStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type FlaskStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type FlaskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *FlaskAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *FlaskAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *FlaskClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *FlaskStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewFlaskResource() resource.Resource { - return &FlaskResource{} -} - -func (r *FlaskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_flask" -} - -func (r *FlaskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("flask-y0lfavjf"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3.12")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/Foxyhhd/Baldurs-Gate-test.git"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *FlaskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *FlaskModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1037: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1038: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64())}), - 1039: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1040: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1041: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 89, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *FlaskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1037: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1038: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64())}), - 1039: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1040: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1041: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 89, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *FlaskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 89, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan FlaskModel - var state FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1042: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1043: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1044: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 89, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *FlaskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *FlaskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *FlaskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/90_postgres_backup_resource.go b/generated/dev/go/90_postgres_backup_resource.go deleted file mode 100644 index 957eb3e..0000000 --- a/generated/dev/go/90_postgres_backup_resource.go +++ /dev/null @@ -1,326 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: backup - -var _ resource.Resource = &PostgresBackupResource{} - -// PostgresBackupResource manages a subresource via operations. -type PostgresBackupResource struct { - client *core.UniversalClient -} - -type PostgresBackupModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewPostgresBackupResource() resource.Resource { - return &PostgresBackupResource{} -} - -func (r *PostgresBackupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_backup" -} - -func (r *PostgresBackupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresBackupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresBackupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresBackupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresBackupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresBackupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/90_postgres_database_resource.go b/generated/dev/go/90_postgres_database_resource.go deleted file mode 100644 index 7310720..0000000 --- a/generated/dev/go/90_postgres_database_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: database - -var _ resource.Resource = &PostgresDatabaseResource{} - -// PostgresDatabaseResource manages a subresource via operations. -type PostgresDatabaseResource struct { - client *core.UniversalClient -} - -type PostgresDatabaseModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` - DbOwner types.String `tfsdk:"db_owner"` -} - -func NewPostgresDatabaseResource() resource.Resource { - return &PostgresDatabaseResource{} -} - -func (r *PostgresDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_database" -} - -func (r *PostgresDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_owner": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.DbName.IsNull() || plan.DbName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") - return - } - if plan.DbOwner.IsNull() || plan.DbOwner.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_owner is required.") - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - } - identityCodes := []string{ - "dbName", - "dbOwner", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - "dbOwner": resources_core.FormatString(state.DbOwner), - } - identityCodes := []string{ - "dbName", - "dbOwner", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/90_postgres_resource.go b/generated/dev/go/90_postgres_resource.go deleted file mode 100644 index 518f22c..0000000 --- a/generated/dev/go/90_postgres_resource.go +++ /dev/null @@ -1,499 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Service ID: 90 - -var _ resource.Resource = &PostgresResource{} -var _ resource.ResourceWithModifyPlan = &PostgresResource{} -var _ resource.ResourceWithImportState = &PostgresResource{} - -type PostgresResource struct { - client *core.UniversalClient -} - -// PostgresAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type PostgresAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` -} - -// PostgresAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type PostgresAutoscaleConfigurationModel struct { - Percent types.Int64 `tfsdk:"percent" json:"percent"` - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` -} - -// PostgresBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type PostgresBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// PostgresClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type PostgresClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// PostgresMtlsConfigurationModel — вложенная модель для map-fixed параметра mtlsConfiguration. -type PostgresMtlsConfigurationModel struct { - Type types.String `tfsdk:"type" json:"type"` - DurationCA types.Int64 `tfsdk:"duration_c_a" json:"durationCA"` - DurationServer types.Int64 `tfsdk:"duration_server" json:"durationServer"` - CertCA types.String `tfsdk:"cert_c_a" json:"certCA"` - CertServer types.String `tfsdk:"cert_server" json:"certServer"` -} - -// PostgresPostgresConfigurationModel — вложенная модель для map-fixed параметра postgresConfiguration. -type PostgresPostgresConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - SslRequired types.Bool `tfsdk:"ssl_required" json:"sslRequired"` - PoolerMaster types.Bool `tfsdk:"pooler_master" json:"poolerMaster"` - PoolerSlave types.Bool `tfsdk:"pooler_slave" json:"poolerSlave"` -} - -// PostgresStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type PostgresStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type PostgresModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *PostgresAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *PostgresAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *PostgresBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *PostgresClusterConfigurationModel `tfsdk:"cluster_configuration"` - MtlsConfiguration *PostgresMtlsConfigurationModel `tfsdk:"mtls_configuration"` - PostgresConf types.String `tfsdk:"postgres_conf"` - PostgresConfiguration *PostgresPostgresConfigurationModel `tfsdk:"postgres_configuration"` - StartupConfiguration *PostgresStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewPostgresResource() resource.Resource { - return &PostgresResource{} -} - -func (r *PostgresResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres" -} - -func (r *PostgresResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "slave_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - }}, - "mtls_configuration": schema.SingleNestedAttribute{Optional: true, Computed: true, - Attributes: map[string]schema.Attribute{ - "type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("off"), MarkdownDescription: "`off` - Настройка MTLS производиться не будет
`manual` - После создания кластера в полях данных будет запрос на создание сертификата. **Вам необходимо будет выписать сертификат через вашу CA** и уже после включить через операцию **modify** режим MTLS
`auto` - Сертификаты будут созданы через Cert-Manager

Режимы **auto** и **manual** требует включить настройку `Включить sslMode` - **true**
MTLS в режиме **auto** будет работать сразу, в режиме **manual** только после того как будет предоставлен **CA** и **Сертификат сервера**"}, - "duration_c_a": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(175200), MarkdownDescription: "Указывается в **часах**
Необходимо настраивать, когда параметр `Режим настройки` - **auto**"}, - "duration_server": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(87600), MarkdownDescription: "Указывается в **часах**
Не может быть больше чем время жизни СА
Обновляется **раз в год**, если выставлено значение **более 2 лет**
Обновляется **раз в пол года**, если выставлено значение **менее 2 лет**
Необходимо настраивать, когда параметр `Режим настройки` - **auto**"}, - "cert_c_a": schema.StringAttribute{Optional: true}, - "cert_server": schema.StringAttribute{Optional: true}, - }}, - "postgres_conf": schema.StringAttribute{Optional: true}, - "postgres_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("17")}, - "ssl_required": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "По умолчанию подключение обязательное (sslmode=require)"}, - "pooler_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi**"}, - "pooler_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi**"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *PostgresModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 788: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 789: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 790: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString())}), - 791: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", config.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", config.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", config.PostgresConfiguration.PoolerSlave.ValueBool())}), - 792: resources_core.FormatString(config.PostgresConf), - 793: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 794: resources_core.BuildJSON(map[string]string{"percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64())}), - 1094: resources_core.BuildJSON(map[string]string{"type": fmt.Sprintf("\"%s\"", config.MtlsConfiguration.Type.ValueString()), "durationCA": fmt.Sprintf("%d", config.MtlsConfiguration.DurationCA.ValueInt64()), "durationServer": fmt.Sprintf("%d", config.MtlsConfiguration.DurationServer.ValueInt64()), "certCA": fmt.Sprintf("\"%s\"", config.MtlsConfiguration.CertCA.ValueString()), "certServer": fmt.Sprintf("\"%s\"", config.MtlsConfiguration.CertServer.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 90, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *PostgresResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 788: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 789: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 790: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString())}), - 791: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", data.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", data.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", data.PostgresConfiguration.PoolerSlave.ValueBool())}), - 792: resources_core.FormatString(data.PostgresConf), - 793: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 794: resources_core.BuildJSON(map[string]string{"percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64())}), - 1094: resources_core.BuildJSON(map[string]string{"type": fmt.Sprintf("\"%s\"", data.MtlsConfiguration.Type.ValueString()), "durationCA": fmt.Sprintf("%d", data.MtlsConfiguration.DurationCA.ValueInt64()), "durationServer": fmt.Sprintf("%d", data.MtlsConfiguration.DurationServer.ValueInt64()), "certCA": fmt.Sprintf("\"%s\"", data.MtlsConfiguration.CertCA.ValueString()), "certServer": fmt.Sprintf("\"%s\"", data.MtlsConfiguration.CertServer.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 90, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mtlsConfiguration", Field: "MtlsConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 90, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mtlsConfiguration", Field: "MtlsConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *PostgresResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresModel - var state PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.PostgresConf.IsNull() != state.PostgresConf.IsNull() || plan.PostgresConf.IsUnknown() != state.PostgresConf.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.PostgresConf.IsNull() && !plan.PostgresConf.IsUnknown() && !state.PostgresConf.IsNull() && !state.PostgresConf.IsUnknown() { - if !strings.EqualFold(plan.PostgresConf.ValueString(), state.PostgresConf.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 795: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 796: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString())}), - 797: resources_core.BuildJSON(map[string]string{"poolerSlave": fmt.Sprintf("%v", plan.PostgresConfiguration.PoolerSlave.ValueBool()), "version": fmt.Sprintf("\"%s\"", plan.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", plan.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", plan.PostgresConfiguration.PoolerMaster.ValueBool())}), - 798: resources_core.FormatString(plan.PostgresConf), - 799: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 800: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - 1095: resources_core.BuildJSON(map[string]string{"certServer": fmt.Sprintf("\"%s\"", plan.MtlsConfiguration.CertServer.ValueString()), "type": fmt.Sprintf("\"%s\"", plan.MtlsConfiguration.Type.ValueString()), "durationCA": fmt.Sprintf("%d", plan.MtlsConfiguration.DurationCA.ValueInt64()), "durationServer": fmt.Sprintf("%d", plan.MtlsConfiguration.DurationServer.ValueInt64()), "certCA": fmt.Sprintf("\"%s\"", plan.MtlsConfiguration.CertCA.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 90, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mtlsConfiguration", Field: "MtlsConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *PostgresResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *PostgresResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/90_postgres_user_resource.go b/generated/dev/go/90_postgres_user_resource.go deleted file mode 100644 index 8ac874c..0000000 --- a/generated/dev/go/90_postgres_user_resource.go +++ /dev/null @@ -1,367 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: user - -var _ resource.Resource = &PostgresUserResource{} - -// PostgresUserResource manages a subresource via operations. -type PostgresUserResource struct { - client *core.UniversalClient -} - -type PostgresUserModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - MtlsAccess types.Bool `tfsdk:"mtls_access"` - Role types.String `tfsdk:"role"` - Username types.String `tfsdk:"username"` -} - -func NewPostgresUserResource() resource.Resource { - return &PostgresUserResource{} -} - -func (r *PostgresUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_user" -} - -func (r *PostgresUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "mtls_access": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Подключение пользователя без ввода паролей
Политика применяется **ко всем базам** внутри Postgresql

RBAC на базы настраивать вручную Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Bool{boolplanmodifier.RequiresReplace()}}, - "role": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать роль из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Role.IsNull() || plan.Role.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "role is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "mtlsAccess": resources_core.FormatBool(plan.MtlsAccess), - "role": resources_core.FormatString(plan.Role), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "mtlsAccess", - "role", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "role": resources_core.FormatString(plan.Role), - "mtlsAccess": resources_core.FormatBool(plan.MtlsAccess), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "mtlsAccess": resources_core.FormatBool(plan.MtlsAccess), - "role": resources_core.FormatString(plan.Role), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "mtlsAccess": resources_core.FormatBool(state.MtlsAccess), - "role": resources_core.FormatString(state.Role), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "mtlsAccess", - "role", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/91_redis_resource.go b/generated/dev/go/91_redis_resource.go deleted file mode 100644 index 2462093..0000000 --- a/generated/dev/go/91_redis_resource.go +++ /dev/null @@ -1,393 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: redis -// Service ID: 91 - -var _ resource.Resource = &RedisResource{} -var _ resource.ResourceWithModifyPlan = &RedisResource{} -var _ resource.ResourceWithImportState = &RedisResource{} - -type RedisResource struct { - client *core.UniversalClient -} - -// RedisAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type RedisAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` -} - -// RedisClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type RedisClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// RedisStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type RedisStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type RedisModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *RedisAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *RedisClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *RedisStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewRedisResource() resource.Resource { - return &RedisResource{} -} - -func (r *RedisResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_redis" -} - -func (r *RedisResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "slave_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *RedisResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *RedisModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1061: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1062: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1063: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 91, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *RedisResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1061: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1062: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1063: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 91, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RedisResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 91, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *RedisResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RedisModel - var state RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1064: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 1065: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 91, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RedisResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *RedisResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *RedisResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/92_mongodb_resource.go b/generated/dev/go/92_mongodb_resource.go deleted file mode 100644 index 2a8e46d..0000000 --- a/generated/dev/go/92_mongodb_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mongodb -// Service ID: 92 - -var _ resource.Resource = &MongodbResource{} -var _ resource.ResourceWithModifyPlan = &MongodbResource{} -var _ resource.ResourceWithImportState = &MongodbResource{} - -type MongodbResource struct { - client *core.UniversalClient -} - -// MongodbAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type MongodbAccessConfigurationModel struct { - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` -} - -// MongodbClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type MongodbClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// MongodbStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type MongodbStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type MongodbModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *MongodbAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *MongodbClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *MongodbStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewMongodbResource() resource.Resource { - return &MongodbResource{} -} - -func (r *MongodbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mongodb" -} - -func (r *MongodbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024)}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(5), MarkdownDescription: "Указывается в Gb"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MongodbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *MongodbModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1089: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1090: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 1091: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 92, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *MongodbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1089: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1090: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 1091: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 92, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 92, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *MongodbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MongodbModel - var state MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1092: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1093: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 92, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *MongodbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *MongodbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/92_mongodb_user_resource.go b/generated/dev/go/92_mongodb_user_resource.go deleted file mode 100644 index 6f0e40d..0000000 --- a/generated/dev/go/92_mongodb_user_resource.go +++ /dev/null @@ -1,364 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mongodb -// Subresource: user - -var _ resource.Resource = &MongodbUserResource{} - -// MongodbUserResource manages a subresource via operations. -type MongodbUserResource struct { - client *core.UniversalClient -} - -type MongodbUserModel struct { - ID types.String `tfsdk:"id"` - MongodbID types.String `tfsdk:"mongodb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` - Role types.String `tfsdk:"role"` - Username types.String `tfsdk:"username"` -} - -func NewMongodbUserResource() resource.Resource { - return &MongodbUserResource{} -} - -func (r *MongodbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mongodb_user" -} - -func (r *MongodbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mongodb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Required: true}, - "role": schema.StringAttribute{Required: true}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MongodbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MongodbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.DbName.IsNull() || plan.DbName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") - return - } - if plan.Role.IsNull() || plan.Role.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "role is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "role": resources_core.FormatString(plan.Role), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MongodbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MongodbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_user" == "" { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "role": resources_core.FormatString(plan.Role), - }) - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MongodbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MongodbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MongodbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/93_rabbitmq_resource.go b/generated/dev/go/93_rabbitmq_resource.go deleted file mode 100644 index a43cd4d..0000000 --- a/generated/dev/go/93_rabbitmq_resource.go +++ /dev/null @@ -1,474 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: rabbitmq -// Service ID: 93 - -var _ resource.Resource = &RabbitmqResource{} -var _ resource.ResourceWithModifyPlan = &RabbitmqResource{} -var _ resource.ResourceWithImportState = &RabbitmqResource{} - -type RabbitmqResource struct { - client *core.UniversalClient -} - -type RabbitmqModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` - MasterAccessList types.String `tfsdk:"master_access_list"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewRabbitmqResource() resource.Resource { - return &RabbitmqResource{} -} - -func (r *RabbitmqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_rabbitmq" -} - -func (r *RabbitmqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name_master": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Список IP-адресов, с которых разрешён доступ. Если оставить пустым — доступ открыт всем.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `2000`"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Пример: `20`"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1124), MarkdownDescription: "Пример: `2048`"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *RabbitmqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *RabbitmqModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.IpSpaceNameMaster.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name_master is required.") - return - } - if config.IpSpaceNameMaster.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 116: resources_core.FormatInt64(config.ResourceCPU), - 117: resources_core.FormatInt64(config.ResourceMemory), - 118: resources_core.FormatInt64(config.ResourceDisk), - 119: resources_core.FormatString(config.ResourceRealm), - 224: resources_core.FormatInt64(config.ResourceInstances), - 225: resources_core.FormatString(config.MasterAccessList), - 543: resources_core.FormatString(config.IpSpaceNameMaster), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 93, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *RabbitmqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.IpSpaceNameMaster.IsNull() || data.IpSpaceNameMaster.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name_master is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 116: resources_core.FormatInt64(data.ResourceCPU), - 117: resources_core.FormatInt64(data.ResourceMemory), - 118: resources_core.FormatInt64(data.ResourceDisk), - 119: resources_core.FormatString(data.ResourceRealm), - 224: resources_core.FormatInt64(data.ResourceInstances), - 225: resources_core.FormatString(data.MasterAccessList), - 543: resources_core.FormatString(data.IpSpaceNameMaster), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 93, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RabbitmqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 93, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *RabbitmqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RabbitmqModel - var state RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MasterAccessList.IsNull() != state.MasterAccessList.IsNull() || plan.MasterAccessList.IsUnknown() != state.MasterAccessList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MasterAccessList.IsNull() && !plan.MasterAccessList.IsUnknown() && !state.MasterAccessList.IsNull() && !state.MasterAccessList.IsUnknown() { - if !strings.EqualFold(plan.MasterAccessList.ValueString(), state.MasterAccessList.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceNameMaster.ValueString(), state.IpSpaceNameMaster.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 304: resources_core.FormatInt64(plan.ResourceCPU), - 305: resources_core.FormatInt64(plan.ResourceMemory), - 306: resources_core.FormatInt64(plan.ResourceDisk), - 307: resources_core.FormatInt64(plan.ResourceInstances), - 308: resources_core.FormatString(plan.MasterAccessList), - 545: resources_core.FormatString(plan.IpSpaceNameMaster), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 93, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RabbitmqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *RabbitmqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *RabbitmqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/94_lucee_resource.go b/generated/dev/go/94_lucee_resource.go deleted file mode 100644 index d78129f..0000000 --- a/generated/dev/go/94_lucee_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: lucee -// Service ID: 94 - -var _ resource.Resource = &LuceeResource{} -var _ resource.ResourceWithModifyPlan = &LuceeResource{} -var _ resource.ResourceWithImportState = &LuceeResource{} - -type LuceeResource struct { - client *core.UniversalClient -} - -// LuceeAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type LuceeAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// LuceeAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type LuceeAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// LuceeClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type LuceeClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// LuceeStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type LuceeStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type LuceeModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *LuceeAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *LuceeAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *LuceeClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *LuceeStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewLuceeResource() resource.Resource { - return &LuceeResource{} -} - -func (r *LuceeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_lucee" -} - -func (r *LuceeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("lucee-kctea8wd"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("6.0")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testlucee"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LuceeResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *LuceeModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1019: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1020: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1021: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1022: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1023: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 94, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *LuceeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1019: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1020: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1021: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1022: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1023: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 94, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LuceeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 94, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LuceeModel - var state LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1024: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1025: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1026: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 94, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LuceeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *LuceeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *LuceeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/95_nodejs_resource.go b/generated/dev/go/95_nodejs_resource.go deleted file mode 100644 index 10900b8..0000000 --- a/generated/dev/go/95_nodejs_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodejs -// Service ID: 95 - -var _ resource.Resource = &NodejsResource{} -var _ resource.ResourceWithModifyPlan = &NodejsResource{} -var _ resource.ResourceWithImportState = &NodejsResource{} - -type NodejsResource struct { - client *core.UniversalClient -} - -// NodejsAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type NodejsAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// NodejsAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type NodejsAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// NodejsClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type NodejsClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// NodejsStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type NodejsStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type NodejsModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *NodejsAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *NodejsAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *NodejsClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *NodejsStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNodejsResource() resource.Resource { - return &NodejsResource{} -} - -func (r *NodejsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodejs" -} - -func (r *NodejsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("node-bd1l2h8o"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("23")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testnode.git"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NodejsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NodejsModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1028: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1029: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1030: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1031: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1032: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 95, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NodejsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1028: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1029: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1030: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1031: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1032: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 95, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NodejsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 95, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NodejsModel - var state NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1033: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1034: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1035: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 95, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NodejsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NodejsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NodejsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/96_pgadmin_resource.go b/generated/dev/go/96_pgadmin_resource.go deleted file mode 100644 index aac4518..0000000 --- a/generated/dev/go/96_pgadmin_resource.go +++ /dev/null @@ -1,515 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: pgadmin -// Service ID: 96 - -var _ resource.Resource = &PgadminResource{} -var _ resource.ResourceWithModifyPlan = &PgadminResource{} -var _ resource.ResourceWithImportState = &PgadminResource{} - -type PgadminResource struct { - client *core.UniversalClient -} - -type PgadminModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Domain types.String `tfsdk:"domain"` - Login types.String `tfsdk:"login"` - Password types.String `tfsdk:"password"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewPgadminResource() resource.Resource { - return &PgadminResource{} -} - -func (r *PgadminResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_pgadmin" -} - -func (r *PgadminResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("pgadmin-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "login": schema.StringAttribute{Required: true, MarkdownDescription: "Логин для подключения к pgAdmin, в формате email (login@email.net) Не изменяется после создания."}, - "password": schema.StringAttribute{Required: true, MarkdownDescription: "Пароль для подключения к pgAdmin Не изменяется после создания.", Sensitive: true}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores). 1000Mili = 1 Ядро"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PgadminResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *PgadminModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Login.IsNull() && !plan.Login.IsUnknown() && !state.Login.IsNull() && !state.Login.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Login.ValueString(), state.Login.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Password.IsNull() && !plan.Password.IsUnknown() && !state.Password.IsNull() && !state.Password.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Password.ValueString(), state.Password.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить password", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Login.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "login is required.") - return - } - if config.Login.IsUnknown() { - return - } - if config.Password.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "password is required.") - return - } - if config.Password.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 164: resources_core.FormatString(config.Domain), - 165: resources_core.FormatInt64(config.ResourceCPU), - 166: resources_core.FormatInt64(config.ResourceMemory), - 167: resources_core.FormatInt64(config.ResourceDisk), - 169: resources_core.FormatString(config.ResourceRealm), - 170: resources_core.FormatString(config.Login), - 171: resources_core.FormatString(config.Password), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 96, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *PgadminResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Login.IsNull() || data.Login.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "login is required.") - return - } - if data.Password.IsNull() || data.Password.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "password is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 164: resources_core.FormatString(data.Domain), - 165: resources_core.FormatInt64(data.ResourceCPU), - 166: resources_core.FormatInt64(data.ResourceMemory), - 167: resources_core.FormatInt64(data.ResourceDisk), - 169: resources_core.FormatString(data.ResourceRealm), - 170: resources_core.FormatString(data.Login), - 171: resources_core.FormatString(data.Password), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 96, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PgadminResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 96, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *PgadminResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PgadminModel - var state PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 172: resources_core.FormatInt64(plan.ResourceCPU), - 173: resources_core.FormatInt64(plan.ResourceMemory), - 174: resources_core.FormatInt64(plan.ResourceDisk), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 96, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PgadminResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *PgadminResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *PgadminResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/97_nodered_resource.go b/generated/dev/go/97_nodered_resource.go deleted file mode 100644 index 7b00a75..0000000 --- a/generated/dev/go/97_nodered_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodered -// Service ID: 97 - -var _ resource.Resource = &NoderedResource{} -var _ resource.ResourceWithModifyPlan = &NoderedResource{} -var _ resource.ResourceWithImportState = &NoderedResource{} - -type NoderedResource struct { - client *core.UniversalClient -} - -type NoderedModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNoderedResource() resource.Resource { - return &NoderedResource{} -} - -func (r *NoderedResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodered" -} - -func (r *NoderedResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nodered-1"), MarkdownDescription: "Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NoderedResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NoderedModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 193: resources_core.FormatInt64(config.ResourceCPU), - 194: resources_core.FormatInt64(config.ResourceMemory), - 195: resources_core.FormatInt64(config.ResourceDisk), - 196: resources_core.FormatString(config.ResourceRealm), - 197: resources_core.FormatString(config.Domain), - 323: resources_core.FormatInt64(config.ResourceInstances), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 97, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NoderedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 193: resources_core.FormatInt64(data.ResourceCPU), - 194: resources_core.FormatInt64(data.ResourceMemory), - 195: resources_core.FormatInt64(data.ResourceDisk), - 196: resources_core.FormatString(data.ResourceRealm), - 197: resources_core.FormatString(data.Domain), - 323: resources_core.FormatInt64(data.ResourceInstances), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 97, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NoderedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 97, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NoderedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NoderedModel - var state NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 97, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NoderedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NoderedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NoderedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/98_http_resource.go b/generated/dev/go/98_http_resource.go deleted file mode 100644 index 5d287fa..0000000 --- a/generated/dev/go/98_http_resource.go +++ /dev/null @@ -1,509 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: http -// Service ID: 98 - -var _ resource.Resource = &HttpResource{} -var _ resource.ResourceWithModifyPlan = &HttpResource{} -var _ resource.ResourceWithImportState = &HttpResource{} - -type HttpResource struct { - client *core.UniversalClient -} - -type HttpModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Domain types.String `tfsdk:"domain"` - IngressAnnotations types.String `tfsdk:"ingress_annotations"` - JsonEnv types.String `tfsdk:"json_env"` - RegistryPath types.String `tfsdk:"registry_path"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewHttpResource() resource.Resource { - return &HttpResource{} -} - -func (r *HttpResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_http" -} - -func (r *HttpResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("http-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_annotations": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Формат ключ/значение
Поддерживает пока простые аннотации Не изменяется после создания."}, - "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Массив переменных, будут переданы в env приложения Не изменяется после создания."}, - "registry_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nexus-sa.tst.nubes.ru/docker-nubes/jolt"), MarkdownDescription: "Путь до image, который будет установлен как deployment Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili)"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *HttpResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *HttpModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.IngressAnnotations.IsNull() && !plan.IngressAnnotations.IsUnknown() && !state.IngressAnnotations.IsNull() && !state.IngressAnnotations.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IngressAnnotations.ValueString(), state.IngressAnnotations.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ingress_annotations", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить json_env", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RegistryPath.IsNull() && !plan.RegistryPath.IsUnknown() && !state.RegistryPath.IsNull() && !state.RegistryPath.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RegistryPath.ValueString(), state.RegistryPath.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить registry_path", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 212: resources_core.FormatString(config.Domain), - 213: resources_core.FormatInt64(config.ResourceCPU), - 214: resources_core.FormatInt64(config.ResourceMemory), - 215: resources_core.FormatInt64(config.ResourceInstances), - 216: resources_core.FormatString(config.ResourceRealm), - 217: resources_core.FormatString(config.RegistryPath), - 241: resources_core.FormatString(config.JsonEnv), - 782: resources_core.FormatString(config.IngressAnnotations), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 98, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 212: resources_core.FormatString(data.Domain), - 213: resources_core.FormatInt64(data.ResourceCPU), - 214: resources_core.FormatInt64(data.ResourceMemory), - 215: resources_core.FormatInt64(data.ResourceInstances), - 216: resources_core.FormatString(data.ResourceRealm), - 217: resources_core.FormatString(data.RegistryPath), - 241: resources_core.FormatString(data.JsonEnv), - 782: resources_core.FormatString(data.IngressAnnotations), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 98, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HttpResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 98, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *HttpResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HttpModel - var state HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 218: resources_core.FormatInt64(plan.ResourceCPU), - 219: resources_core.FormatInt64(plan.ResourceMemory), - 220: resources_core.FormatInt64(plan.ResourceInstances), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 98, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HttpResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *HttpResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *HttpResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/99_gitea_resource.go b/generated/dev/go/99_gitea_resource.go deleted file mode 100644 index 97139b1..0000000 --- a/generated/dev/go/99_gitea_resource.go +++ /dev/null @@ -1,579 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: gitea -// Service ID: 99 - -var _ resource.Resource = &GiteaResource{} -var _ resource.ResourceWithModifyPlan = &GiteaResource{} -var _ resource.ResourceWithImportState = &GiteaResource{} - -type GiteaResource struct { - client *core.UniversalClient -} - -type GiteaModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - IngressExtraAnnotations types.String `tfsdk:"ingress_extra_annotations"` - IniServerParams types.String `tfsdk:"ini_server_params"` - PsqlUid types.String `tfsdk:"psql_uid"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewGiteaResource() resource.Resource { - return &GiteaResource{} -} - -func (r *GiteaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_gitea" -} - -func (r *GiteaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("gitea-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_extra_annotations": schema.StringAttribute{Optional: true}, - "ini_server_params": schema.StringAttribute{Optional: true}, - "psql_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *GiteaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *GiteaModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.PsqlUid.ValueString(), state.PsqlUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить psql_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.PsqlUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") - return - } - if config.PsqlUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.PsqlUid.IsNull() && !config.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, config.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != config.PsqlUid.ValueString() { - config.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - params := map[int]string{ - 232: resources_core.FormatInt64(config.ResourceCPU), - 233: resources_core.FormatInt64(config.ResourceMemory), - 234: resources_core.FormatInt64(config.ResourceDisk), - 235: resources_core.FormatInt64(config.ResourceInstances), - 237: resources_core.FormatString(config.Domain), - 238: resources_core.FormatString(config.PsqlUid), - 818: resources_core.FormatString(config.IngressExtraAnnotations), - 821: resources_core.FormatString(config.IniServerParams), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 99, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *GiteaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.PsqlUid.IsNull() || data.PsqlUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalPsqlUid := data.PsqlUid - if !data.PsqlUid.IsNull() && !data.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, data.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedPsqlUid != "" && resolvedPsqlUid != data.PsqlUid.ValueString() { - data.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151, 153} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 232: resources_core.FormatInt64(data.ResourceCPU), - 233: resources_core.FormatInt64(data.ResourceMemory), - 234: resources_core.FormatInt64(data.ResourceDisk), - 235: resources_core.FormatInt64(data.ResourceInstances), - 237: resources_core.FormatString(data.Domain), - 238: resources_core.FormatString(data.PsqlUid), - 818: resources_core.FormatString(data.IngressExtraAnnotations), - 821: resources_core.FormatString(data.IniServerParams), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 99, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { - state.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalPsqlUid.IsNull() && !originalPsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - if strings.EqualFold(state.PsqlUid.ValueString(), originalPsqlUid.ValueString()) { - state.PsqlUid = originalPsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 99, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, newState.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != newState.PsqlUid.ValueString() { - newState.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() && !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { - if strings.EqualFold(newState.PsqlUid.ValueString(), state.PsqlUid.ValueString()) { - newState.PsqlUid = state.PsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *GiteaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan GiteaModel - var state GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IngressExtraAnnotations.IsNull() != state.IngressExtraAnnotations.IsNull() || plan.IngressExtraAnnotations.IsUnknown() != state.IngressExtraAnnotations.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IngressExtraAnnotations.IsNull() && !plan.IngressExtraAnnotations.IsUnknown() && !state.IngressExtraAnnotations.IsNull() && !state.IngressExtraAnnotations.IsUnknown() { - if !strings.EqualFold(plan.IngressExtraAnnotations.ValueString(), state.IngressExtraAnnotations.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IniServerParams.IsNull() != state.IniServerParams.IsNull() || plan.IniServerParams.IsUnknown() != state.IniServerParams.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IniServerParams.IsNull() && !plan.IniServerParams.IsUnknown() && !state.IniServerParams.IsNull() && !state.IniServerParams.IsUnknown() { - if !strings.EqualFold(plan.IniServerParams.ValueString(), state.IniServerParams.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 259: resources_core.FormatInt64(plan.ResourceCPU), - 260: resources_core.FormatInt64(plan.ResourceMemory), - 261: resources_core.FormatInt64(plan.ResourceDisk), - 262: resources_core.FormatInt64(plan.ResourceInstances), - 819: resources_core.FormatString(plan.IngressExtraAnnotations), - 820: resources_core.FormatString(plan.IniServerParams), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 99, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { - state.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - if strings.EqualFold(state.PsqlUid.ValueString(), plan.PsqlUid.ValueString()) { - state.PsqlUid = plan.PsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *GiteaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *GiteaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/dev/go/registry.go b/generated/dev/go/registry.go deleted file mode 100644 index 893cf0d..0000000 --- a/generated/dev/go/registry.go +++ /dev/null @@ -1,81 +0,0 @@ -package resources_gen - -import "github.com/hashicorp/terraform-plugin-framework/resource" - -// Code generated by tools/gen_v2. DO NOT EDIT. -func AllResources() []func() resource.Resource { - return []func() resource.Resource{ - NewAkhqResource, - NewClickhouseResource, - NewDnsrecordResource, - NewDnszoneResource, - NewDummyResource, - NewFlaskResource, - NewGiteaResource, - NewHarborResource, - NewHttpResource, - NewK8sGoResource, - NewK8sOpenbaoResource, - NewK8sSthutrvalClusterResource, - NewK8sVeleroResource, - NewK8sZitiControllerResource, - NewK8svalkeyResource, - NewKafkaResource, - NewLlmAiResource, - NewLuceeResource, - NewMariadbResource, - NewMongodbResource, - NewNextcloudResource, - NewNifiResource, - NewNodejsResource, - NewNoderedResource, - NewOpenwhiskResource, - NewPgadminResource, - NewPostgresResource, - NewRabbitmqResource, - NewRedisResource, - NewS3Resource, - NewS3bucketResource, - NewSupersetResource, - NewTemplateResource, - NewTenantResource, - NewValoTenantResource, - NewVappResource, - NewVcComplexResource, - NewVcMgmtSthutrvalClusterResource, - NewVcNatResource, - NewVcNsxtResource, - NewVcOrgResource, - NewVcOrgSaasResource, - NewVcVdcResource, - NewVcVdcGroupResource, - NewVcVmResource, - NewVcVmV2Resource, - NewVcVmV3Resource, - NewVcexternalipResource, - NewVmpostgreResource, - NewZonesV2Resource, - NewClickhouseDatabaseResource, - NewClickhouseUserResource, - NewK8sOpenbaoKvResource, - NewK8sOpenbaoPolicyResource, - NewK8sOpenbaoSecretsResource, - NewK8sSthutrvalClusterUserResource, - NewK8svalkeyUserResource, - NewKafkaTopicResource, - NewKafkaUserResource, - NewLlmAiSecretsResource, - NewLlmAiTeamResource, - NewMariadbDatabaseResource, - NewMariadbUserResource, - NewMongodbUserResource, - NewNextcloudBackupResource, - NewPostgresBackupResource, - NewPostgresDatabaseResource, - NewPostgresUserResource, - NewS3SubUserResource, - NewVcOrgUserResource, - NewVcVdcGroupVdcResource, - NewZonesV2RecordResource, - } -} diff --git a/generated/dev/resources_yaml/100_openwhisk.yaml b/generated/dev/resources_yaml/100_openwhisk.yaml deleted file mode 100644 index 1714f1d..0000000 --- a/generated/dev/resources_yaml/100_openwhisk.yaml +++ /dev/null @@ -1,54 +0,0 @@ -name: openwhisk -service_id: 100 -service_display_name: Serverless Openwhisk (openwhisk_tenant) -service_short_name: openwhisk -service_man: Добавление и удаления пользователя для работы с Openwhisk -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 41 - kind: instance - action: create - params: - - id: 64 - code: username - data_type: string - required: true - descr: Имя пользователя Openwhisk - - id: 281 - code: resourceRealm - data_type: string - required: true - func: getAvailableResourceRealms - descr: Платформа для развертывания - - name: delete - id: 42 - kind: instance - action: delete - params: - - id: 65 - code: username - data_type: string - required: true - descr: Имя пользователя Openwhisk diff --git a/generated/dev/resources_yaml/109_zones_v2.yaml b/generated/dev/resources_yaml/109_zones_v2.yaml deleted file mode 100644 index b17bfc4..0000000 --- a/generated/dev/resources_yaml/109_zones_v2.yaml +++ /dev/null @@ -1,281 +0,0 @@ -name: zones_v2 -service_id: 109 -service_display_name: Управление DNS -service_short_name: zonesV2 -service_man: '# Управление DNS

## Что делает сервис

Управляет DNS-зоной вашего домена в инфраструктуре `ngcloud.ru`.
Сервис помогает:

- создать DNS-зону;
- поддерживать записи в зоне (добавлять, удалять, массово менять);
- удалять зону;
- синхронизировать данные зоны с фактическим состоянием на DNS-серверах.

Сервис работает с авторитетными DNS-серверами зоны:

- `ns3.ngcloud.ru`
- `185.247.187.83`
- `2a10:aa80:0:5::3`
- `ns4.ngcloud.ru`
- `81.22.46.43`
- `2a10:aa80:0:6::3`

## Очень важно: что сделать у регистратора домена

Чтобы зона действительно начала обслуживаться, **нужно делегировать домен** у регистратора на авторитетные DNS-серверы:

- `ns3.ngcloud.ru`
- `ns4.ngcloud.ru`

В панели регистратора нужно заменить NS-серверы домена на значения выше.
Без этого записи в зоне могут быть созданы, но интернет не будет использовать их для вашего домена.

## Проверка после настройки

Проверить ответы от серверов зоны:

```bash
dig <ваш_домен> @ns3.ngcloud.ru
dig <ваш_домен> @ns4.ngcloud.ru
```

Проверить, что делегирование у регистратора применилось:

```bash
dig <ваш_домен> NS
```

В ответе должны быть `ns3.ngcloud.ru` и `ns4.ngcloud.ru`.
' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 254 - kind: instance - action: create - man: '# Операция `create` (создание DNS-зоны)

## Что делает операция

`create` создает новую DNS-зону на авторитетных серверах сервиса.
После создания зона закрепляется за пользователем, который запустил создание инстанса.

## Когда использовать

- подключаете домен к сервису впервые;
- нужна новая отдельная зона.

## Важное условие после создания

У регистратора домена нужно делегировать зону на:

- `ns3.ngcloud.ru`
- `185.247.187.83`
- `2a10:aa80:0:5::3`
- `ns4.ngcloud.ru`
- `81.22.46.43`
- `2a10:aa80:0:6::3`

Если этого не сделать, зона будет создана технически, но в интернете домен не начнет работать через эти DNS.

## Объяснение параметра `Типа зоны`

### `NATIVE`

Это основная (ведущая) зона в нашем сервисе.
Вы управляете записями здесь, и именно эта зона считается источником правды.

### `SLAVE`

Это копия зоны, которая подтягивает данные с внешнего DNS-сервера.
Такой режим нужен, когда истинный источник записей находится не в нашем сервисе, а на другом DNS.

## Что проверяет операция

- что такая зона еще не существует на целевых DNS;
- корректность входных параметров зоны;
- ограничения на служебные/запрещенные имена зон.' - params: - - id: 761 - code: zoneName - data_type: string - required: true - regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ - maxlength: 64 - minlength: 1 - descr: Имя зоны - sort: 20 - - id: 762 - code: zoneKind - data_type: string - required: true - default: NATIVE - value_list: - - NATIVE - - SLAVE - descr: Тип зоны - sort: 30 - - id: 763 - code: zoneMastersForSlave - data_type: string - required: false - descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов - man: Пример:
**8.8.8.8 8.8.4.4** - sort: 40 - - id: 764 - code: zoneRefresh - data_type: integer > 0 - required: true - default: "86400" - minvalue: 600 - descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов - sort: 50 - - id: 765 - code: zoneRetry - data_type: integer > 0 - required: true - default: "7200" - minvalue: 600 - descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов - sort: 60 - - id: 766 - code: zoneExpire - data_type: integer > 0 - required: true - default: "3600000" - minvalue: 3600 - descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным - sort: 70 - - id: 767 - code: zoneTTL - data_type: integer > 0 - required: true - default: "86400" - minvalue: 600 - descr: Время, в течение которого информация будет кешироваться другими DNS-серверами - sort: 80 - - id: 768 - code: resourceRealm - data_type: string - required: true - default: powerdns - value_list: - - powerdns - func: getAvailableResourceRealms - sort: 10 - - name: create_record - id: 260 - kind: subresource - action: create - subresource: record - man: '# Операция `create_record` (добавление одной записи)

## Что делает операция

`create_record` точечно добавляет одну DNS-запись в существующую зону.
Если аналогичная запись уже есть, итоговый набор дополняется в рамках логики типа/имени.

## Когда использовать

- нужно добавить одну новую запись без массовой переработки зоны;
- нужно быстро внести точечное изменение.
' - params: - - id: 777 - code: recordName - data_type: string - required: true - regex: ^([a-zA-Z0-9\-_]+\.)*[a-zA-Z0-9\-_]+$|^(\d{1,3}\.){3}\d{1,3}$|^.+$ - unique_scope: parent - maxlength: 64 - minlength: 1 - man: Имя зоны в конце проставляется автоматически - sort: 10 - - id: 778 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - maxvalue: 84000 - minvalue: 30 - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 20 - - id: 779 - code: recordType - data_type: string - required: true - default: A - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - sort: 30 - - id: 780 - code: recordInput - data_type: array-map-fixed - required: true - default: '[{}]' - man: Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - sort: 40 - sub_params: - - id: 240 - code: record - data_type: string - required: true - default: "" - is_modifiable: false - - name: delete - id: 255 - kind: instance - action: delete - man: '# Операция `delete` (удаление зоны)

## Что делает операция

`delete` удаляет DNS-зону целиком вместе со всеми записями.

## Когда использовать

- зона больше не нужна;
- домен выводится из эксплуатации в рамках сервиса.

## Важная логика

- удаляется вся зона, а не отдельные записи;
- перед удалением стоит проверить, что домен действительно не используется.
' - params: [] - - name: delete_record - id: 261 - kind: subresource - action: delete - subresource: record - man: '# Операция `delete_record` (удаление записей по имени)

## Что делает операция

`delete_record` удаляет все записи с указанным именем (`recordName`), независимо от типа (`A`, `TXT`, `CNAME` и т.д.).

## Когда использовать

- нужно полностью убрать конкретное имя из зоны;
- требуется быстро очистить все варианты записи для одного имени.

## Важная логика

- удаляются все типы записей для выбранного имени;
- если нужно удалить только один тип записи, лучше использовать `modify_record` с нужным целевым состоянием.
' - params: - - id: 781 - code: recordName - data_type: string - required: true - - name: modify - id: 256 - kind: instance - action: modify - man: '# Операция `modify` (массовое изменение записей зоны)

## Что делает операция

`modify` обновляет записи зоны целиком на основе переданного массива `records`.
Это операция "приведения к целевому состоянию": система берет входной набор и применяет его на DNS-серверах.

## Когда использовать

- нужно массово обновить зону;
- нужно заменить сразу несколько записей одним запуском;
- нужно синхронно привести зону к заранее подготовленному списку записей.

## Важная логика

- запись группируется по `(имя, тип, TTL)`;
- операция ориентирована на полное обновление набора, а не на "точечную мелкую правку".

Для точечных изменений одной записи обычно удобнее `create_record` или `modify_record`.
' - params: - - id: 769 - code: records - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 86 - code: recordName - data_type: string - required: true - default: "" - regex: ^([a-zA-Z0-9\-_]+\.)*[a-zA-Z0-9\-_]+$|^(\d{1,3}\.){3}\d{1,3}$|^.+$ - man: Имя зоны в конце проставляется автоматически - is_modifiable: false - - id: 88 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - is_modifiable: false - - id: 85 - code: recordType - data_type: string - required: true - default: A - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - - CAA - is_modifiable: false - - id: 87 - code: recordInput - data_type: string - required: true - default: "" - man: Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - is_modifiable: false - - name: modify_record - id: 262 - kind: subresource - action: modify - subresource: record - man: '# Операция `modify_record` (изменение конкретной записи)

## Что делает операция

`modify_record` заменяет записи с указанным именем одной новой записью заданного типа и значения.

## Когда использовать

- нужно исправить одну конкретную запись;
- нужно заменить старые значения записи на новое целевое значение.

## Важная логика

- существующие записи выбранного имени очищаются;
- затем добавляется одна новая запись с переданными параметрами.
' - params: - - id: 783 - code: recordName - data_type: string - required: true - sort: 10 - - id: 784 - code: recordTTL - data_type: integer > 0 - required: true - maxvalue: 84000 - minvalue: 30 - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 20 - - id: 785 - code: recordType - data_type: string - required: true - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - sort: 30 - - id: 786 - code: recordInput - data_type: array-map-fixed - required: true - default: '[{}]' - man: Заменяет все текущие записи конкретного типа
Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - sort: 40 - depends_on: recordName - sub_params: - - id: 241 - code: record - data_type: string - required: true - default: "" - is_modifiable: false - - name: reconcile - id: 259 - kind: action - action: reconcile - man: '# Операция `reconcile` (актуализация состояния зоны)

## Что делает операция

`reconcile` подтягивает актуальное фактическое состояние DNS-зоны и синхронизирует его с данными инстанса.
Операция техническая: она нужна для выравнивания состояния, а не для бизнес-изменений записей.

## Когда использовать

- записи менялись вручную на DNS-серверах;
- нужно обновить схему/состояние инстанса в личном кабинете;
- нужно импортировать текущие записи в управляемое состояние.

## Важная логика

- актуализируется список записей в данных инстанса;
- стандартно ничего "нового" для клиента не добавляет и не удаляет само по себе, а приводит данные к реальности.
' - params: [] diff --git a/generated/dev/resources_yaml/110_dnszone.yaml b/generated/dev/resources_yaml/110_dnszone.yaml deleted file mode 100644 index de8d32c..0000000 --- a/generated/dev/resources_yaml/110_dnszone.yaml +++ /dev/null @@ -1,97 +0,0 @@ -name: dnszone -service_id: 110 -service_display_name: DNS зона -service_short_name: dnszone -service_man: '## ![image](https://img.icons8.com/?size=50&id=41400&format=png) Необходимо делегировать зону у регистратора на Авторитетные DNS (Сервера зон)

ns3.ngcloud.ru
``` bash
185.247.187.83
2a10:aa80:0:5::3
```

ns4.ngcloud.ru
``` bash
81.22.46.43
2a10:aa80:0:6::3
```' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 43 - kind: instance - action: create - params: - - id: 66 - code: zoneEmail - data_type: string - required: true - descr: Контактный e-mail - - id: 67 - code: zoneName - data_type: string - required: true - regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ - descr: Имя зоны - man: Имя зоны - - id: 68 - code: zoneKind - data_type: string - required: true - default: NATIVE - value_list: - - NATIVE - - SLAVE - descr: Тип зоны - - id: 69 - code: zoneMastersForSlave - data_type: string - required: false - descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов - - id: 70 - code: zoneTimeToRefresh - data_type: string - required: true - default: "86400" - descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов - - id: 71 - code: zoneTimeToRetry - data_type: string - required: true - default: "7200" - descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов - - id: 72 - code: zoneTimeToExpire - data_type: string - required: true - default: "3600000" - descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным - - id: 73 - code: zoneTimeToLive - data_type: string - required: true - default: "86400" - descr: Время, в течение которого информация будет кешироваться другими DNS-серверами - - id: 284 - code: resourceRealm - data_type: string - required: true - default: powerdns - value_list: - - powerdns - func: getAvailableResourceRealms - descr: Платформа для развертывания - - name: delete - id: 44 - kind: instance - action: delete - params: [] diff --git a/generated/dev/resources_yaml/111_dnsrecord.yaml b/generated/dev/resources_yaml/111_dnsrecord.yaml deleted file mode 100644 index d87a3c1..0000000 --- a/generated/dev/resources_yaml/111_dnsrecord.yaml +++ /dev/null @@ -1,105 +0,0 @@ -name: dnsrecord -service_id: 111 -service_display_name: DNS запись -service_short_name: dnsrecord -service_man: Необходима DNS зона, в которой будет создана DNS запись.
Выберите
* Зону в которой будет создана запись
* Тип записи
* Имя записи
* Значение записи -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 45 - kind: instance - action: create - params: - - id: 75 - code: zoneUid - data_type: uuid - required: true - ref_svc_id: 109 - descr: UUID экземпляра услуги DNS-зоны - man: UUID экземпляра услуги DNS-зоны - sort: 10 - - id: 76 - code: recordType - data_type: string - required: true - value_list: - - A - - AAAA - - CNAME - - MX - - PTR - - SOA - - SRV - - TXT - - LUA - - NS - - SSHFP - descr: Тип DNS-записи - man: Тип DNS-записи - sort: 20 - - id: 77 - code: recordName - data_type: string - required: true - unique_scope: parent - descr: Имя записи. Имя зоны в конце проставляется автоматически - man: Имя записи. Имя зоны в конце проставляется автоматически - sort: 30 - - id: 78 - code: recordInput - data_type: string - required: true - descr: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - man: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - sort: 40 - is_modifiable: true - - id: 450 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - maxvalue: 604800 - minvalue: 30 - descr: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 50 - - name: delete - id: 46 - kind: instance - action: delete - params: [] - - name: modify - id: 90 - kind: instance - action: modify - params: - - id: 256 - code: recordInput - data_type: string - required: true - descr: Запись. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - - name: reconcile - id: 408 - kind: action - action: reconcile - params: [] diff --git a/generated/dev/resources_yaml/112_tenant.yaml b/generated/dev/resources_yaml/112_tenant.yaml deleted file mode 100644 index 89bc4a6..0000000 --- a/generated/dev/resources_yaml/112_tenant.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: tenant -service_id: 112 -service_display_name: Тенант в Grafana -service_short_name: tenant -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 84 - kind: instance - action: create - params: - - id: 239 - code: grafanaOrgName - data_type: string - required: true - regex: ^WZ[\d]+$ - descr: WZ-Код клиента - sort: 10 - - id: 240 - code: resourceRealm - data_type: string - required: true - default: grafana.ngcloud.ru - value_list: - - grafana.ngcloud.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 20 - - name: delete - id: 86 - kind: instance - action: delete - params: [] - - name: modify - id: 87 - kind: instance - action: modify - man: Просто удаляет все текущие датасоурсы и ставит их заново
Полезно, когда нужно "ресетнуть" / "добавить" - params: [] diff --git a/generated/dev/resources_yaml/113_vc_complex.yaml b/generated/dev/resources_yaml/113_vc_complex.yaml deleted file mode 100644 index 72e594e..0000000 --- a/generated/dev/resources_yaml/113_vc_complex.yaml +++ /dev/null @@ -1,158 +0,0 @@ -name: vc_complex -service_id: 113 -service_display_name: Быстрый старт -service_short_name: vcComplex -service_man: '# Создает окружение для ВМ.

## Создает сервисы:
**Организация**
**Виртуальный датацентр**
**Каталог**
**Сетевой шлюз периметра**

' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 107 - kind: instance - action: create - params: - - id: 332 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы - sort: 1 - - id: 334 - code: vappName - data_type: string - required: true - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: Имя vApp - sort: 10 - - id: 358 - code: vdcProviderGateway - data_type: string - required: true - default: sandbox-v1cl1-pvdc - descr: Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String - sort: 200 - - id: 359 - code: virtualServicesCount - required: true - default: "1" - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 - sort: 110 - - id: 360 - code: ipSpaceName - data_type: string - required: false - default: internet-ipv4-v1 - descr: Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT - sort: 130 - - id: 399 - code: storageConfig - data_type: json - required: true - default: '[{"name": "", "size": 1024}]' - descr: Описывается как массив объектов, где описывается имя политики и её размер - sort: 210 - - id: 400 - code: vdcNetworkPool - data_type: string - required: true - default: nsxt-sandbox-geneve-np - sort: 120 - - id: 401 - code: cpuGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - sort: 20 - - id: 402 - code: memGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - sort: 30 - - id: 403 - code: segroupName - data_type: string - required: false - default: SEGROUP-SANDBOX-CL1-SHARED-01 - descr: Наименование segroup, в котором будут выделяться пулы на AVI - sort: 100 - - id: 404 - code: needExternalAddressSNAT - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Выделяет внешний IP на Edge. Используется для SNAT всего трафика - sort: 40 - - id: 405 - code: needEnableAVI - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Включает AVI в Cloud Director - sort: 50 - - id: 566 - code: cpuAllocated - data_type: string - required: true - default: "1" - minvalue: 1 - descr: шт - sort: 21 - - id: 567 - code: memAllocated - data_type: integer > 0 - required: true - default: "10" - minvalue: 1 - descr: gb - sort: 31 - - id: 568 - code: vIPCount - data_type: integer > 0 - required: true - default: "0" - maxvalue: 20 - minvalue: 0 - descr: шт - sort: 131 diff --git a/generated/dev/resources_yaml/115_mariadb.yaml b/generated/dev/resources_yaml/115_mariadb.yaml deleted file mode 100644 index 5de6abc..0000000 --- a/generated/dev/resources_yaml/115_mariadb.yaml +++ /dev/null @@ -1,458 +0,0 @@ -name: mariadb -service_id: 115 -service_display_name: Mariadb -service_short_name: mariadb -service_man: 'Mariadb кластер. Ресурсная платформа: k8s. Автоматизация: k8s operator.
Автоматическое резервное копирование, ручное восстановление.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 138 - kind: instance - action: create - params: - - id: 877 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 242 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 878 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 244 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 245 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - man: Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать - is_modifiable: false - - id: 246 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 243 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 879 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 247 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 248 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 880 - code: mariadbConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 249 - code: version - data_type: string - required: true - default: 9.4.0 - value_list: - - 8.4.0 - - 9.4.0 - is_modifiable: false - - id: 881 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - is_modifiable: true - sub_params: - - id: 250 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 251 - code: retain - data_type: integer > 0 - required: true - default: "720" - is_modifiable: false - - id: 252 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 882 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 253 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 254 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 255 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 256 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - name: create_database - id: 242 - kind: subresource - action: create - subresource: database - params: - - id: 734 - code: dbName - data_type: string - required: true - default: db-dxinlvxx - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: 'Пример: `db01`' - - name: create_user - id: 243 - kind: subresource - action: create - subresource: user - params: - - id: 735 - code: username - data_type: string - required: true - default: userz2hsgoyo - regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ - maxlength: 62 - minlength: 2 - descr: 'Пример: `myusername`' - sort: 10 - - id: 736 - code: password - data_type: string - required: true - default: mhs3keq0 - maxlength: 62 - minlength: 8 - descr: Секрет будет записан в хранилище секретов и не будет отображаться публично - sort: 20 - is_sensitive: true - - id: 737 - code: dbName - data_type: array-map-fixed - required: true - default: '[{}]' - descr: Необходимо указать массив существующих баз - man: Если базы не существует - при выполнении операции вернётся ошибка - sort: 30 - sub_params: - - id: 54 - code: dbname - data_type: string - required: true - default: "" - is_modifiable: false - - id: 738 - code: accessHosts - data_type: string - required: true - default: '%' - descr: 'Пример: `%`' - sort: 40 - - name: delete - id: 139 - kind: instance - action: delete - params: - - id: 456 - code: deleteS3Bucket - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Также удалить технический S3 Bucket с бэкапами? - sort: 10 - - name: delete_database - id: 248 - kind: subresource - action: delete - subresource: database - params: - - id: 745 - code: dbName - data_type: string - required: true - descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ - - name: delete_user - id: 247 - kind: subresource - action: delete - subresource: user - params: - - id: 744 - code: username - data_type: string - required: true - descr: Имя пользователя, созданное через операцию `create_user` - - name: modify - id: 142 - kind: instance - action: modify - params: - - id: 883 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 257 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 258 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 259 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - man: Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать - is_modifiable: false - - id: 260 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 884 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 261 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 262 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 885 - code: mariadbConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 263 - code: version - data_type: string - required: true - default: "" - value_list: - - 8.4.0 - - 9.4.0 - is_modifiable: false - - id: 886 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 264 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 265 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 266 - code: schedule - data_type: string - required: true - default: "" - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 887 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 267 - code: enabled - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 268 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 269 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 270 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - name: reconcile - id: 287 - kind: action - action: reconcile - params: [] - - name: resume - id: 141 - kind: instance - action: resume - params: [] - - name: suspend - id: 140 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/116_kafka.yaml b/generated/dev/resources_yaml/116_kafka.yaml deleted file mode 100644 index 54f2f69..0000000 --- a/generated/dev/resources_yaml/116_kafka.yaml +++ /dev/null @@ -1,335 +0,0 @@ -name: kafka -service_id: 116 -service_display_name: ApacheKafka -service_short_name: kafka -service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Идентификатор организации
Уникальный идентификатор созданной организации. Используется для всех дальнейших API-операций.

### Статус организации
Отражает текущее состояние: active, suspended, modifying, deleting.

### Параметры сети
Выходные данные при наличии ipSpaces: список выделенных внешних IP, имя ip_space, количество IP.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)**;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к этой организации.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.

' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 148 - kind: instance - action: create - params: - - id: 1084 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 579 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1085 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 580 - code: cpu - data_type: integer > 0 - required: true - default: "2000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 581 - code: memory - data_type: integer > 0 - required: true - default: "2048" - is_modifiable: false - - id: 582 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 583 - code: disk - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 1086 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 584 - code: allocationExternal - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - is_modifiable: false - - id: 585 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: create_topic - id: 165 - kind: subresource - action: create - subresource: topic - man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' - params: - - id: 523 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`' - sort: 10 - - id: 524 - code: partitions - data_type: integer > 0 - required: true - descr: Число партиций можно увеличить, но уменьшить их нельзя. - - id: 525 - code: replicas - data_type: integer > 0 - required: true - descr: Количество реплик не может привышать количество реплик брокера - sort: 20 - - name: create_user - id: 168 - kind: subresource - action: create - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 530 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - id: 531 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' - - id: 532 - code: operations - data_type: array - required: true - value_list: - - Create - - Describe - - Read - - Write - descr: 'Пример: `Create, Describe, Read, Write`' - - id: 533 - code: group - data_type: string - required: true - default: '*' - descr: 'Пример: `order-consumers` или `*`' - - id: 534 - code: accessHosts - data_type: string - required: true - default: '*' - descr: 'Пример: `192.168.1.0/24` или `*`' - - name: delete - id: 149 - kind: instance - action: delete - params: [] - - name: delete_topic - id: 167 - kind: subresource - action: delete - subresource: topic - params: - - id: 529 - code: nameTopic - data_type: string - required: true - descr: Необходимо выбрать топик, который необходимо удалить - - name: delete_user - id: 170 - kind: subresource - action: delete - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 540 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - name: modify - id: 162 - kind: instance - action: modify - params: - - id: 1087 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 586 - code: cpu - data_type: integer > 0 - required: true - default: "2000" - is_modifiable: false - - id: 587 - code: memory - data_type: integer > 0 - required: true - default: "2048" - is_modifiable: false - - id: 588 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 589 - code: disk - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 1088 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 590 - code: allocationExternal - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - is_modifiable: false - - id: 591 - code: masterAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - name: modify_topic - id: 166 - kind: subresource - action: modify - subresource: topic - man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' - params: - - id: 526 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`' - sort: 10 - - id: 527 - code: partitions - required: false - descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` - sort: 30 - - id: 528 - code: replicas - required: false - descr: Количество партиций ≤ (число брокеров). Пример `2` - sort: 20 - - name: modify_user - id: 169 - kind: subresource - action: modify - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 535 - code: nameTopic - data_type: string - required: false - default: '*' - descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' - - id: 536 - code: operations - data_type: array - required: false - value_list: - - Create - - Describe - - Read - - Write - descr: 'Пример: `Create, Describe, Read, Write`' - - id: 537 - code: group - data_type: string - required: false - default: '*' - descr: 'Пример: `order-consumers` или `*`' - - id: 538 - code: accessHosts - data_type: string - required: false - default: '*' - descr: 'Пример: `192.168.1.0/24` или `*`' - - id: 539 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - sort: 10 - - name: resume - id: 161 - kind: instance - action: resume - params: [] - - name: suspend - id: 160 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/119_akhq.yaml b/generated/dev/resources_yaml/119_akhq.yaml deleted file mode 100644 index 4a1cc9d..0000000 --- a/generated/dev/resources_yaml/119_akhq.yaml +++ /dev/null @@ -1,107 +0,0 @@ -name: akhq -service_id: 119 -service_display_name: Akhq -service_short_name: Akhq -service_man: '# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
AKHQ — это веб-интерфейс для работы с кластерами Apache Kafka.
Позволяет просматривать топики, consumer group''ы, сообщения, ACL, схему безопасности, состояние брокеров и метаданные.
Поддерживает работу с несколькими кластерами Kafka одновременно, включая внутренние и внешние (TLS) подключения.

## Доступные операции
- **create** — Создание ресурса AKHQ
- **suspend** — Остановка ресурса
- **resume** — Возобновление работы ресурса
- **modify** — Модификация настроек
- **delete** — Удаление ресурса

---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Кластер Kafka к которому нужно настроить подключение**
Для использования AKHQ необходимо предварительно развернуть сервис Kafka. Пример: `kafka-external`.

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов для работы Akhq. Пример `2`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `512`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Имя домена:**
Dns Имя по которому будет происходить обращение к сервису. Указать необходимо уникальное имя до . ( то есть если необходимо получить dns ui) Пример: `kafka-external`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**connectionUrl**
Это полное доменное имя (FQDN), по которому будет доступен интерфейс AKHQ. После развёртывания подождите несколько минут — UI станет доступен по HTTPS, как только все сервисы полностью запустятся.

**Учетные данные**
Здесь представлены поля для ввода логина и пароля, с помощью которых вы сможете авторизоваться в сервисе.

---
## 5. Дополнительная информация
Учётные записи, созданные для работы с AKHQ, предоставляют доступ только для просмотра данных. Для управления Kafka, пожалуйста, используйте возможности личного кабинета.
---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 175 - kind: instance - action: create - params: - - id: 550 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Выбрать платформу из списка - sort: 10 - - id: 551 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - minvalue: 512 - descr: 'Пример: `1024`' - sort: 20 - - id: 552 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - minvalue: 500 - descr: 'Пример: `1000`' - sort: 30 - - id: 553 - code: domain - data_type: string - required: true - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 40 - - id: 554 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - minvalue: 1 - sort: 11 - - id: 555 - code: kafkaUid - data_type: uuid - required: true - ref_svc_id: 116 - descr: Выбрать кластер из списка - sort: 5 - is_modifiable: true - - name: delete - id: 176 - kind: instance - action: delete - params: [] - - name: modify - id: 180 - kind: instance - action: modify - params: - - id: 565 - code: kafkaUid - data_type: uuid - required: true - ref_svc_id: 116 - descr: Выбрать кластер из списка - - name: resume - id: 178 - kind: instance - action: resume - params: [] - - name: suspend - id: 177 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/120_clickhouse.yaml b/generated/dev/resources_yaml/120_clickhouse.yaml deleted file mode 100644 index 0f3b88e..0000000 --- a/generated/dev/resources_yaml/120_clickhouse.yaml +++ /dev/null @@ -1,570 +0,0 @@ -name: clickhouse -service_id: 120 -service_display_name: ClickHouse -service_short_name: clickhouse -service_man: '# Инструкция по управлению кластерами и пользователями ClickHouse

---

## Общая информация

ClickHouse — это колоночная система управления базами данных, предназначенная для обработки аналитических запросов в реальном времени. Платформа позволяет создавать, удалять, приостанавливать и возобновлять кластеры ClickHouse, а также управлять базами данных и пользователями.

Кластер создаётся через оператор Altinity (`https://github.com/Altinity/clickhouse-operator`) в ресурсной платформе типа `k8s`

Кластеры могут создаваться шардированными с произвольным количеством реплик. Для обеспечения отказоустойчивости и координации обязательно включение параметра `Создать ClickHouse-Keeper`.

Кластера создаются с обязательным бекапом. Для этого необходимо предварительно создать услугу `S3 Object Storage`.
---

## Доступные операции

* **create** — Создание кластера
* **delete** — Удаление кластера
* **suspend** — Приостановка работы кластера
* **resume** — Возобновление работы кластера
* **create_database** — Создание базы данных
* **delete_database** — Удаление базы данных
* **create_user** — Создание пользователя и настройка прав доступа к базе данных
* **delete_user** — Удаление пользователя

## Процесс создания и первого подключения

1. Выполнить операцию `create` - будет создан кластер
2. Выполнить операцию `create_database` - будет создана база данных
3. Выполнить операцию `create_user` - будет создан пользователь (выбрать доступ подключения к БД, созданной в п.2)

## Примеры подключения

### Подключение через HTTP-интерфейс

```bash
# Проверка доступности кластера
curl "http://<username>:<password>@<host>:8123/" -d "SELECT 1"

# Выполнение запроса
curl "http://<username>:<password>@<host>:8123/" -d "SHOW DATABASES"
```

## Простые кейсы
*Создать БД и обычной таблицы*
```clickhouse
CREATE TABLE database.table
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

INSERT INTO database.table SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM database.table;
```
Базы и таблицы не реплицируются без специального ключа (`ON CLUSTER ''clickhousek8s''`)
В случае создания в стиле, описанным выше, необходимо будет выполнять действия на всех шардах/репликах


*Создание БД и шардированной таблицы*
```clickhouse
CREATE TABLE database.localUsers
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

CREATE TABLE database.shardedTable
AS localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', id);

INSERT INTO shardedTable SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM shardedTable;
```
Важный момент - в данном примере таблицы придётся также выполнять на всех нодах ClickHouse

*Создание реплицируемой и шардированной таблицы*
```clickhouse
CREATE TABLE IF NOT EXISTS database.localUsers ON cluster ''clickhousek8s''
(
user_id UInt64,
name String,
email String
)
ENGINE = ReplicatedMergeTree(
''/clickhouse/tables/{shard}/localUsers'',
''{replica}''
)
ORDER BY user_id;

CREATE TABLE IF NOT EXISTS database.users ON cluster ''clickhousek8s''
AS database.localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', rand());

INSERT INTO database.users VALUES (1, ''Alice'', ''alice@example.com'');
INSERT INTO database.users VALUES (2, ''Bob'', ''bob@example.com'');
INSERT INTO database.users VALUES (3, ''Charlie'', ''charlie@example.com'');
INSERT INTO database.users VALUES (4, ''David'', ''david@example.com'');
INSERT INTO database.users VALUES (5, ''Gosha'', ''gosha@example.com'');
INSERT INTO database.users VALUES (6, ''Eve'', ''eve@example.com'');
INSERT INTO database.users VALUES (7, ''Frank'', ''frank@example.com'');
INSERT INTO database.users VALUES (8, ''Grace'', ''grace@example.com'');
INSERT INTO database.users VALUES (9, ''Hannah'', ''hannah@example.com'');
INSERT INTO database.users VALUES (10, ''Ivan'', ''ivan@example.com'');

SELECT * FROM database.localUsers;
SELECT * FROM database.users;

SELECT COUNT(*) AS total_users FROM database.users;
SELECT COUNT(*) AS total_users_local FROM database.localUsers;
```' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 181 - kind: instance - action: create - man: Операция создания кластера ClickHouse

Для подключения к кластеру также необходимо создать базу данных через операцию `create_database` и создание пользователя через операцию `create_user` - params: - - id: 837 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 194 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 838 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 196 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 197 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 199 - code: shards - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - - "3" - man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' - is_modifiable: false - - id: 198 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 195 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 839 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 200 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 201 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 840 - code: clickhouseKeeperConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 202 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 203 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 204 - code: replicas - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "1" - - "3" - man: Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут - is_modifiable: false - - id: 841 - code: clickhouseConfiguration - data_type: map-fixed - required: true - sort: 50 - is_modifiable: true - sub_params: - - id: 205 - code: version - data_type: string - required: true - default: "25.10" - value_list: - - "25.10" - is_modifiable: false - - id: 842 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 206 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 207 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 208 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 843 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 80 - is_modifiable: true - sub_params: - - id: 209 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 210 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 211 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 212 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 851 - code: clickhouseFilesConfiguration - data_type: array-map-fixed - required: false - man: 'Формат:
- path: somethings3.xml
xmlConfig: ''<clickhouse>......</clickhouse>''' - sort: 60 - is_modifiable: true - sub_params: - - id: 234 - code: path - data_type: string - required: true - default: something.xml - regex: ^[a-zA-Z0-9]+\.xml$ - is_modifiable: false - - id: 235 - code: xmlConfig - data_type: string - required: true - default: '<clickhouse></clickhouse>' - man: Предварительно xml необходимо отформатировать через
```
$ tr -d '\n \t' < input.xml > output.xml
```

Позже будет исправлено - is_modifiable: false - is_sensitive: true - - name: create_database - id: 186 - kind: subresource - action: create - subresource: database - man: База данных создаётся на всех репликах кластера ClickHouse - params: - - id: 600 - code: dbName - data_type: string - required: true - default: db01 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: 'Пример: `db01`' - sort: 10 - - name: create_user - id: 185 - kind: subresource - action: create - subresource: user - params: - - id: 596 - code: username - data_type: string - required: true - default: myusername - regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ - maxlength: 62 - minlength: 2 - descr: 'Пример: `myusername`' - sort: 10 - - id: 598 - code: dbName - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 30 - sub_params: - - id: 233 - code: dbName - data_type: string - required: true - default: "" - is_modifiable: false - - id: 599 - code: accessHosts - data_type: string - required: true - default: ::/0 - descr: 'Пример: `::/0`' - sort: 40 - - name: delete - id: 184 - kind: instance - action: delete - man: Удаление возможно только после остановки сервиса через операцию `suspend`
После выполнения операции необходимо выждать определенное кол-во времени - params: [] - - name: delete_database - id: 188 - kind: subresource - action: delete - subresource: database - man: Необратимое действие!!!
Трижды подумай, заблокируй телефон тимлида и не выполняй это действие - params: - - id: 602 - code: dbName - data_type: string - required: true - descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ - - name: delete_user - id: 187 - kind: subresource - action: delete - subresource: user - params: - - id: 601 - code: username - required: true - descr: Имя пользователя, созданное через операцию `create_user` - - name: modify - id: 189 - kind: instance - action: modify - params: - - id: 844 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 213 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 214 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 215 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 216 - code: shards - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - - "3" - man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' - is_modifiable: false - - id: 217 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 845 - code: clickhouseKeeperConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 220 - code: replicas - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "1" - - "3" - man: Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут - is_modifiable: false - - id: 218 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 219 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 846 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 221 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 222 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 847 - code: clickhouseConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 223 - code: version - data_type: string - required: true - default: "" - value_list: - - "25.10" - is_modifiable: false - - id: 848 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 224 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 225 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 226 - code: schedule - data_type: string - required: true - default: "" - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 849 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - sub_params: - - id: 227 - code: enabled - data_type: boolean - required: true - default: false,true - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 228 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 229 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 230 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 850 - code: clickhouseFilesConfiguration - data_type: array-map-fixed - required: false - man: 'Формат:
- path: somethings3.xml
xmlConfig: ''<clickhouse>......</clickhouse>''' - sort: 50 - sub_params: - - id: 232 - code: xmlConfig - data_type: string - required: true - default: '<clickhouse></clickhouse>' - man: Предварительно xml необходимо отформатировать через
```
$ tr -d '\n \t' < input.xml > output.xml
```

Позже будет исправлено - is_modifiable: false - is_sensitive: true - - id: 231 - code: path - data_type: string - required: true - default: something.xml - regex: ^[a-zA-Z0-9]+\.xml$ - is_modifiable: false - - name: reconcile - id: 276 - kind: action - action: reconcile - params: [] - - name: resume - id: 183 - kind: instance - action: resume - params: [] - - name: suspend - id: 182 - kind: instance - action: suspend - man: Операция приводит к удалению всех подов
Persistent Volume остаются нетронутыми - params: [] diff --git a/generated/dev/resources_yaml/12_s3.yaml b/generated/dev/resources_yaml/12_s3.yaml deleted file mode 100644 index e1665b7..0000000 --- a/generated/dev/resources_yaml/12_s3.yaml +++ /dev/null @@ -1,130 +0,0 @@ -name: s3 -service_id: 12 -service_display_name: S3 Object Storage -service_short_name: s3 -service_man: Руководство доступно по ссылке:
https://docs.s3-website.msk-1.ngcloud.ru/ -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 1 - kind: instance - action: create - params: - - id: 163 - code: resourceRealm - required: true - default: ceph.tst.nubes.ru - value_list: - - ceph.tst.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: кластер S3 для подключения - sort: 20 - - id: 320 - code: displayName - data_type: string - required: true - regex: ^[a-zA-Z0-9_]{1,61}[a-zA-Z0-9]$ - descr: Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента - sort: 10 - - name: create_sub_user - id: 6 - kind: subresource - action: create - subresource: sub_user - params: - - id: 60 - code: subUserName - data_type: string - required: true - - id: 61 - code: subUserAccess - data_type: string - required: true - value_list: - - read - - write - - readwrite - - full - - name: delete - id: 5 - kind: instance - action: delete - params: [] - - name: delete_sub_user - id: 7 - kind: subresource - action: delete - subresource: sub_user - params: - - id: 63 - code: subUserName - data_type: string - required: true - descr: subUserName. По идее услуга уже создана - должен передаваться из ЛК - - name: modify - id: 4 - kind: instance - action: modify - man: При первичном вызове операции услуга Trial прекращает действие - params: - - id: 50 - code: maxSizeGbPerUser - data_type: integer > 0 - required: false - maxvalue: 50000 - minvalue: 1 - descr: maxSizeGb. Передаём руками - man: maxSizeGb. Передаём руками - - id: 51 - code: maxObjectsPerBucket - data_type: integer > 0 - required: false - maxvalue: 1e+07 - minvalue: 1 - descr: Квота на бакет. Кол-во объектов. Передаём руками - man: Квота на бакет. Кол-во объектов. Передаём руками - - id: 52 - code: maxBucketsPerUser - data_type: integer > 0 - required: false - maxvalue: 1000 - minvalue: 1 - descr: Квота на пользователя. Количество бакетов пользователя. Передаём руками - man: Квота на пользователя. Количество бакетов пользователя. Передаём руками - - name: reconcile - id: 407 - kind: action - action: reconcile - params: [] - - name: resume - id: 3 - kind: instance - action: resume - params: [] - - name: suspend - id: 2 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/13_s3bucket.yaml b/generated/dev/resources_yaml/13_s3bucket.yaml deleted file mode 100644 index 68e506c..0000000 --- a/generated/dev/resources_yaml/13_s3bucket.yaml +++ /dev/null @@ -1,90 +0,0 @@ -name: s3bucket -service_id: 13 -service_display_name: S3 бакет -service_short_name: s3bucket -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 51 - kind: instance - action: create - params: - - id: 124 - code: s3UserUid - data_type: uuid - required: true - ref_svc_id: 12 - descr: UID Корневой услуги S3. На основе него будет создан бакет. - - id: 125 - code: bucketName - data_type: string - required: true - descr: Имя бакета s3 - - id: 126 - code: maxSize - required: true - default: "-1" - descr: Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes - man: Максимальный размер бакета s3. Если поставить 0, будет безлимит. Размер указывается в Bytes - - id: 127 - code: readAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. - - id: 128 - code: listAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. - - id: 129 - code: corsAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS - - id: 130 - code: placement - data_type: string - required: true - default: HOT - value_list: - - HOT - - COLD - descr: Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения - - name: delete - id: 52 - kind: instance - action: delete - params: [] diff --git a/generated/dev/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml b/generated/dev/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml deleted file mode 100644 index a4140c8..0000000 --- a/generated/dev/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml +++ /dev/null @@ -1,176 +0,0 @@ -name: vc_mgmt_sthutrval_cluster -service_id: 148 -service_display_name: Менеджмент Kubernetes кластер Штурвал -service_short_name: vcMgmtSthutrvalCluster -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 258 - kind: instance - action: create - params: - - id: 771 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 91 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - descr: Облако в котором будет развёрнут тенант - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - is_modifiable: false - - id: 89 - code: clusterName - data_type: string - required: true - default: example-e5uamzll - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 772 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 90 - code: appVersion - data_type: string - required: true - default: 2.13.1 - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - id: 773 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 93 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - is_modifiable: false - - id: 94 - code: count - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 92 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана - is_modifiable: false - - id: 774 - code: workerConfiguration - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 40 - sub_params: - - id: 98 - code: count - data_type: integer > 0 - required: true - default: "2" - is_modifiable: false - - id: 95 - code: groupName - data_type: string - required: true - default: workers-nmn0sns4 - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 96 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана - is_modifiable: false - - id: 97 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - is_modifiable: false - - id: 775 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 100 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: 'Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**' - is_modifiable: false - - id: 101 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - id: 102 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: 'Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**' - is_modifiable: false - - id: 99 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false diff --git a/generated/dev/resources_yaml/149_valo_tenant.yaml b/generated/dev/resources_yaml/149_valo_tenant.yaml deleted file mode 100644 index 387929e..0000000 --- a/generated/dev/resources_yaml/149_valo_tenant.yaml +++ /dev/null @@ -1,249 +0,0 @@ -name: valo_tenant -service_id: 149 -service_display_name: VALO Cloud -service_short_name: valoTenant -service_man: '# Инструкция по развертыванию Тенант Valo через платформу

## 1. Общая информация
Тенант Valo — логический изолированный экземпляр сервиса VALO Cloud, предоставляющий защищённое облачное хранилище, синхронизацию данных и доступ к документам с различных устройств. При создании тенант автоматически разворачивается в тестовом режиме сроком на 2 месяца. Перевод в промышленную эксплуатацию выполняется отдельной операцией.

### Доступные операции

* **create** — создание нового тенанта в тестовом режиме сроком на 2 месяца.
* **modify** — изменение настроек тенанта, включая перевод из ТЕСТ в ПРОД.
* **suspend** — выключение (приостановка работы) тенанта.
* **resume** — включение ранее приостановленного тенанта.
* **delete** — удаление тенанта; доступно только после выполнения операции suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создан тенант.
Пример значения: `valo.ngcloud.ru`.

### DNS-Префикс
Описание: Будет добавлено в зону valo.ngcloud.ru
Пример значения: `mysimpledomain`.

### Параметры хранилища
Описание: Сколько места будет выделено под использование в тенанте. Данные хранятся в S3. Размер указывается в Gigabytes
Пример значения: `10`.

### Настройки тенанта
Описание: Ряд конфигурации Valo-тенанта, такие как настройки пользователя и доступного функционала
Пример значения: `опционально в рамках настройки`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Достаточно развернуть тенант в ТЕСТ. Срок предоставляется в 2 месяца. По истечению срока тенант автоматически переводится в `suspend` на стороне ресурсной платформы

### Промышленное окружение
Для разблокировки используйте операцию `modify`

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Статус тенанта
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация
Тенант всегда создаётся в режиме ТЕСТ и не может быть сразу переведён в ПРОД без операции modify.
Удаление тенанта возможно только после его выключения с помощью операции suspend.
Срок тестового режима составляет 2 месяца с момента создания.
Операции suspend и resume используются для временного управления доступностью сервиса без потери конфигурации.
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 202 - kind: instance - action: create - params: - - id: 655 - code: resourceRealm - data_type: string - required: true - default: valo.ngcloud.ru - value_list: - - valo.ngcloud.ru - func: getAvailableResourceRealms - sort: 10 - - id: 656 - code: domain - data_type: string - required: true - regex: ^[a-z0-9](?:[a-z0-9-]{3,61}[a-z0-9])?$ - maxlength: 61 - minlength: 3 - descr: Будет добавлено в зону valo.ngcloud.ru - sort: 20 - - id: 657 - code: valoOptions - data_type: map-fixed - required: true - default: '{}' - sort: 40 - is_modifiable: true - sub_params: - - id: 28 - code: userCount - data_type: integer > 0 - required: true - default: "10" - is_modifiable: false - - id: 20 - code: requestApproveMaxPeriod - data_type: integer > 0 - required: true - default: "2" - descr: Максимальное значение периода согласования (в днях) - is_modifiable: false - - id: 21 - code: ffOnlineEditor - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 22 - code: ffWatermark - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 23 - code: ffUserImport - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 24 - code: ffThemes - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 25 - code: ffPublicLink - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 26 - code: ffLogging - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 658 - code: s3Storage - data_type: integer > 0 - required: true - default: "10" - maxvalue: 999 - minvalue: 1 - descr: Параметр указывается в `Gb` - man: Данные хранятся в отдельном экземпляре S3 - sort: 30 - is_modifiable: true - - name: delete - id: 206 - kind: instance - action: delete - params: [] - - name: modify - id: 203 - kind: instance - action: modify - params: - - id: 659 - code: valoOptions - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 33 - code: ffUserImport - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 34 - code: ffThemes - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 35 - code: ffPublicLink - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 36 - code: ffLogging - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 29 - code: userCount - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 30 - code: requestApproveMaxPeriod - data_type: integer > 0 - required: true - default: "" - descr: Максимальное значение периода согласования (в днях) - is_modifiable: false - - id: 31 - code: ffOnlineEditor - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 32 - code: ffWatermark - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 660 - code: s3Storage - data_type: integer > 0 - required: true - descr: Параметр указывается в `Gb` - man: Данные хранятся в отдельном экземпляре S3 - sort: 20 - - name: resume - id: 205 - kind: instance - action: resume - params: [] - - name: suspend - id: 204 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/150_k8s_sthutrval_cluster.yaml b/generated/dev/resources_yaml/150_k8s_sthutrval_cluster.yaml deleted file mode 100644 index bef9995..0000000 --- a/generated/dev/resources_yaml/150_k8s_sthutrval_cluster.yaml +++ /dev/null @@ -1,371 +0,0 @@ -name: k8s_sthutrval_cluster -service_id: 150 -service_display_name: Kubernetes кластер Штурвал -service_short_name: k8sSthutrvalCluster -service_man: Перед разворачиванием убедиться, что в облаке есть 2 свободных невыделенных IP в пуле -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 108 - kind: instance - action: create - params: - - id: 750 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 59 - code: clusterName - data_type: string - required: true - default: example-vlzeyhxy - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 57 - code: vdcUid - data_type: string - required: true - default: "" - is_modifiable: false - - id: 58 - code: nsxtUid - data_type: string - required: true - default: "" - is_modifiable: false - - id: 751 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 62 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 63 - code: count - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 61 - code: sizingPolicy - data_type: string - required: true - default: '[]' - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 752 - code: workerConfiguration - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 40 - is_modifiable: true - sub_params: - - id: 64 - code: groupName - data_type: string - required: true - default: workers-r4xzlgs2 - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 65 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 66 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 67 - code: count - data_type: integer > 0 - required: true - default: "2" - is_modifiable: false - - id: 753 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 50 - is_modifiable: true - sub_params: - - id: 70 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - id: 71 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 68 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false - - id: 69 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 755 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 72 - code: appVersion - data_type: string - required: true - default: 2.13.1 - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - name: create_user - id: 281 - kind: subresource - action: create - subresource: user - params: - - id: 869 - code: admins - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 10 - sub_params: - - id: 236 - code: email - data_type: string - required: true - default: "" - man: Пользователи будут добавлены, только если они зарегистрированы в личном кабинете - is_modifiable: false - - name: delete - id: 109 - kind: instance - action: delete - params: [] - - name: delete_user - id: 282 - kind: subresource - action: delete - subresource: user - params: - - id: 870 - code: admins - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 237 - code: email - data_type: string - required: true - default: "" - is_modifiable: false - - name: modify - id: 110 - kind: instance - action: modify - man: При изменении кол-ва узлов, удаления группы, если в этой группе есть поды на которых есть PV с named-дисками, удаляться ноды сами НЕ БУДУТ.
Убедитесь, что они будут смигрированы на другие ноды в процессе прежде чем группа узлов/узлы будут удалены - params: - - id: 756 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 73 - code: appVersion - data_type: string - required: true - default: "" - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - id: 757 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 74 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 75 - code: sizingDisk - data_type: integer > 0 - required: true - default: "" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 76 - code: count - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 758 - code: workerConfiguration - data_type: array-map-fixed - required: true - sort: 30 - sub_params: - - id: 77 - code: groupName - data_type: string - required: true - default: workers-tfu2jila - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 78 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 79 - code: sizingDisk - data_type: integer > 0 - required: true - default: "" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 80 - code: count - data_type: integer >= 0 - required: true - default: "" - is_modifiable: false - - id: 759 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 83 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - id: 84 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 81 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false - - id: 82 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: reconcile - id: 253 - kind: action - action: reconcile - params: [] - - name: resume - id: 174 - kind: instance - action: resume - params: [] - - name: suspend - id: 173 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/151_k8s_openbao.yaml b/generated/dev/resources_yaml/151_k8s_openbao.yaml deleted file mode 100644 index 7339d78..0000000 --- a/generated/dev/resources_yaml/151_k8s_openbao.yaml +++ /dev/null @@ -1,389 +0,0 @@ -name: k8s_openbao -service_id: 151 -service_display_name: Vault -service_short_name: k8sOpenbao -service_man: '# Инструкция по развертыванию Хранилища секретов через платформу

## 1. Общая информация
Хранилище секретов — сервис централизованного и защищённого хранения чувствительных данных (секретов, ключей, конфигураций). Предназначен для использования приложениями и сервисами в Kubernetes с управлением доступом на основе ролей.
В качестве приложения используется открытый проект OpenBao с поддержкой кластерного режима RAFT.

### Доступные операции
**create** — Создание хранилища секретов и инициализация кластера.
**delete** — Удаление хранилища секретов с полной очисткой всех связанных сущностей.
**modify** — Изменение параметров развернутого хранилища секретов.
**suspend** — Приостановка работы кластера без удаления Persistent Volume.
**resume** — Возобновление работы кластера с автоматическим unseal всех нод.
**restart** — Поочередный перезапуск нод с последующим подключением в кластер.
**create_kv** — Создание Key/Value хранилища версии V2.
**delete_kv** — Удаление Key/Value хранилища со всеми содержащимися секретами.
**create_secrets** — Создание секрета внутри Key/Value хранилища.
**delete_secrets** — Удаление секрета из Key/Value хранилища.
**update_secrets** — Обновление значения секрета.
**recovery_secrets** — Откат секрета к указанной версии.
**create_role** — Создание роли для управления Key/Value хранилищем или секретами.
**update_role** — Обновление параметров роли.
**delete_role** — Удаление роли доступа.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: Кластер Kubernetes, в котором будет развернуто приложение.
Формат значения: имя Kubernetes-кластера.

### Имя домена
Описание: Доменное имя сервиса. При указании полного FQDN выполняется проверка DNS A-записи или добавляется А-запись в техническую зону.
Формат значения: secrets.domain.com / mysecretdomain.

### Кол-во реплик
Описание: Количество узлов в кластере.
Формат значения: целое число.

### Квота ядра пода
Описание: Лимит CPU для одного узла. Указывается в milicores (1000 milicores = 1 CPU).
Формат значения: целое число.

### Квота памяти пода
Описание: Лимит оперативной памяти для одного узла. Указывается в megabytes.
Формат значения: целое число.

### Размер диска под хранение секретов
Описание: Объем диска для хранения данных. Фактический объем равен значению, умноженному на количество реплик. Указывается в Gigabytes
Формат значения: целое число.

### Версия OpenBao
Описание: Версия OpenBao для развертывания сервиса.
Формат значения: выбор из предоставленного списка версий.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Кол-во реплик: 2.
Квота ядра пода: 200.
Квота памяти пода: 256.
Размер диска под хранение секретов: 1.
Версия OpenBao: актуальная стабильная версия.

### Промышленное окружение
Кол-во реплик: от 3.
Квота ядра пода: от 1000.
Квота памяти пода: от 1024.
Размер диска под хранение секретов: от 10.
Версия OpenBao: актуальная стабильная версия.

---

## 4. Выходные параметры
Настройки внутри хранилища (kv/secrets/roles) — конфигурации Key/Value, секретов и ролей, созданные соответствующими операциями.
Настройки самого хранилище (сайзинг, объемы) — параметры ресурсов и хранилища данных, примененные к сервису.
Статус кластера (running / suspended) — текущее состояние работы кластера.
url для подключения к веб — адрес веб-интерфейса сервиса.

---

## 5. Дополнительная информация
При удалении хранилища все данные и настройки безвозвратно удаляются.
При использовании доменного имени требуется корректная настройка DNS.
Операции управления секретами и ролями выполняются только после успешного создания хранилища.
Удаление кластера производится через операцию `suspend`, а удаление становится доступно через 14 дней после остановки кластера
Правило выше не относится к операции `delete_kv` / `delete_secrets` / `delete_role`' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 210 - kind: instance - action: create - params: - - id: 663 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Кластер Kubernetes в котором будет развёрнуто приложение - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - sort: 0 - - id: 664 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 8000 - minvalue: 250 - descr: 'Пример: `500`' - man: Указывается в Milicores
`1000` Mili == `1` Ядро - sort: 30 - is_modifiable: true - - id: 665 - code: resourceInstances - data_type: integer > 0 - required: true - default: "3" - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `3`' - sort: 20 - is_modifiable: true - - id: 666 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 256 - descr: 'Пример: `2048`' - man: Указывается в Megabytes - sort: 40 - is_modifiable: true - - id: 667 - code: resourceDisk - data_type: integer > 0 - required: true - default: "10" - maxvalue: 999 - minvalue: 1 - descr: 'Пример: `10`' - man: Указывается в Gigabytes - sort: 50 - is_modifiable: true - - id: 668 - code: appVersion - data_type: string - required: true - default: 2.5.2 - value_list: - - 2.5.2 - descr: Выбрать из списка - sort: 60 - is_modifiable: true - - id: 669 - code: domain - data_type: string - required: true - default: bao-pasczlik - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - name: create_kv - id: 212 - kind: subresource - action: create - subresource: kv - params: - - id: 675 - code: kvName - data_type: string - required: true - default: kvname-ldnwybqu - regex: ^[a-zA-Z0-9-]{1,24}$ - man: Будет создан `v2 тип движка`

Уникальное в рамках услуги - sort: 10 - - name: create_policy - id: 237 - kind: subresource - action: create - subresource: policy - params: - - id: 728 - code: policySettings - data_type: array-map-fixed - required: true - sort: 20 - sub_params: - - id: 50 - code: capabilities - data_type: json - required: true - default: '["read", "list"]' - man: 'Пример: `["create", "read", "update", "delete", "list", "sudo"]`
Только чтение: `["list", "read"]`
Изменение: `["read", "list", "create", "update", "delete"]`' - is_modifiable: false - - id: 49 - code: path - data_type: string - required: true - default: "" - man: 'Пример 1: `mykv/*`
Пример 2: `mykv/path/*`
Пример 3: `mykv/path/to/secret`
' - is_modifiable: false - - id: 729 - code: policyName - required: true - default: policy-re1w8vbp - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - man: Сделано для дальнейшего удобного использования - sort: 10 - - name: create_secrets - id: 214 - kind: subresource - action: create - subresource: secrets - params: - - id: 677 - code: kvName - data_type: string - required: true - descr: Выберите из списка - man: Если список пустой, создайте kv через операцию `create_kv` - sort: 10 - - id: 678 - code: secretPath - data_type: string - required: true - default: path/to/mysecret - regex: ^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$ - descr: 'Пример: `path` или `road/to/path`' - sort: 20 - - id: 679 - code: secretData - data_type: map - required: true - descr: Хотя бы один секрет необходимо добавить - man: Данные не сохраняются в ЛК, в дальнейшем при обновлении секретов это необходимо учитывать - sort: 30 - - name: delete - id: 231 - kind: instance - action: delete - params: [] - - name: delete_kv - id: 213 - kind: subresource - action: delete - subresource: kv - man: '**ОСТОРОЖНО**
Удаление KV подразумевает удаление ВСЕХ secrets внутри этой KV' - params: - - id: 676 - code: kvName - data_type: string - required: true - descr: Выберите из списка - sort: 10 - - name: delete_policy - id: 239 - kind: subresource - action: delete - subresource: policy - params: - - id: 732 - code: policyName - data_type: string - required: true - - name: delete_secrets - id: 233 - kind: subresource - action: delete - subresource: secrets - params: - - id: 726 - code: kvName - data_type: string - required: true - descr: Выберите из списка - sort: 10 - - id: 727 - code: secretPath - data_type: string - required: true - descr: Выберите из списка - sort: 20 - depends_on: kvName - - name: modify - id: 211 - kind: instance - action: modify - params: - - id: 670 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `3`' - sort: 10 - - id: 671 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 8000 - minvalue: 250 - descr: 'Пример: `500`' - man: Указывается в Milicores
`1000` Mili == `1` Ядро - sort: 20 - - id: 672 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 256 - descr: 'Пример: `2048`' - man: Указывается в Megabytes - sort: 30 - - id: 673 - code: resourceDisk - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: 'Пример: `10`' - man: Указывается в Gigabytes - sort: 40 - - id: 674 - code: appVersion - data_type: string - required: true - value_list: - - 2.5.2 - descr: Выбрать из списка - sort: 50 - - name: reconcile - id: 236 - kind: action - action: reconcile - params: [] - - name: recovery_secrets - id: 232 - kind: subresource - action: recovery - subresource: secrets - man: Данная операция подразумевает откат на конкретную версию в секрете.

**Это не восстановление удалённого секрета** - params: - - id: 723 - code: kvName - data_type: string - required: true - descr: Выберите из списка - sort: 10 - - id: 724 - code: secretPath - data_type: string - required: true - descr: Выберите из списка - sort: 20 - depends_on: kvName - - id: 725 - code: secretVersion - data_type: string - required: true - depends_on: kvName,secretPath - - name: restart - id: 234 - kind: action - action: restart - params: [] - - name: resume - id: 216 - kind: instance - action: resume - params: [] - - name: suspend - id: 215 - kind: instance - action: suspend - params: [] - - name: update_policy - id: 238 - kind: subresource - action: update - subresource: policy - params: - - id: 730 - code: policyName - data_type: string - required: true - sort: 10 - - id: 731 - code: policySettings - data_type: array-map-fixed - required: true - descr: Производится полная перезапись политики! - sort: 20 - sub_params: - - id: 52 - code: capabilities - data_type: string - required: true - default: "" - is_modifiable: false - - id: 51 - code: path - data_type: string - required: true - default: "" - is_modifiable: false - - name: update_secrets - id: 230 - kind: subresource - action: update - subresource: secrets - params: - - id: 719 - code: kvName - data_type: string - required: true - descr: Выберите из списка - sort: 10 - - id: 720 - code: secretPath - data_type: string - required: true - descr: Выберите из списка - man: Предварительно необходимо выбрать параметр `Имя Key/Value хранилища` - sort: 20 - depends_on: kvName - - id: 721 - code: needFullUpdateSecret - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Если выбрать `true`, будет создана новая версия секрета `с нуля`
Если выбрать `false`, будет создана новая версия секрета с данными прошлой ревизии, а переданные данные в операции либо будут модифицированы, либо будут добавлены - sort: 30 - - id: 722 - code: secretData - data_type: map - required: true - descr: Хотя бы один секрет необходимо добавить - sort: 40 diff --git a/generated/dev/resources_yaml/153_nifi.yaml b/generated/dev/resources_yaml/153_nifi.yaml deleted file mode 100644 index 7eb6ebd..0000000 --- a/generated/dev/resources_yaml/153_nifi.yaml +++ /dev/null @@ -1,119 +0,0 @@ -name: nifi -service_id: 153 -service_display_name: Nifi -service_short_name: nifi -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 229 - kind: instance - action: create - params: - - id: 711 - code: domain - data_type: string - required: true - default: nifi-lvu4xj7j - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 712 - code: resourceCPU - data_type: integer > 0 - required: true - default: "2000" - maxvalue: 8000 - minvalue: 2000 - descr: 'Пример: `2000`' - man: Указывается в Milicores
`1000` Mili == `1` Ядро - sort: 30 - - id: 713 - code: resourceMemory - data_type: integer > 0 - required: true - default: "4096" - maxvalue: 16384 - minvalue: 2048 - descr: 'Пример: `2048`' - man: Указывается в Megabytes - sort: 40 - - id: 714 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - maxvalue: 1 - minvalue: 1 - descr: 'Пример: `1`' - sort: 20 - - id: 715 - code: resourceDisk - data_type: integer > 0 - required: true - default: "10" - maxvalue: 999 - minvalue: 1 - descr: 'Пример: `10`' - man: Указывается в Gigabytes - sort: 50 - - id: 716 - code: appVersion - data_type: string - required: true - default: 2.6.0 - value_list: - - 2.6.0 - descr: Выбрать из списка - sort: 60 - - id: 717 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Кластер Kubernetes в котором будет развёрнуто приложение - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - sort: 0 - - id: 718 - code: jdbcDrivers - data_type: array-map-fixed - required: true - man: 'Пример: `https://jdbc.postgresql.org/download/postgresql-42.7.8.jar`' - sub_params: - - id: 48 - code: path - data_type: string - required: true - default: https://jdbc.postgresql.org/download/postgresql-42.7.8.jar - is_modifiable: false - - name: delete - id: 235 - kind: instance - action: delete - params: [] diff --git a/generated/dev/resources_yaml/163_llm_ai.yaml b/generated/dev/resources_yaml/163_llm_ai.yaml deleted file mode 100644 index 66efe62..0000000 --- a/generated/dev/resources_yaml/163_llm_ai.yaml +++ /dev/null @@ -1,232 +0,0 @@ -name: llm_ai -service_id: 163 -service_display_name: LLM -service_short_name: llmAi -service_man: '## 1. Общая информация

LLMaaS (Large Language Model as a Service) — это сервис для доступа к языковым моделям через OpenAI-совместимое API.
Платформа позволяет создавать организации (тенанты), команды, выпускать и управлять API-ключами, устанавливать лимиты на использование.

---

## Доступные операции

- **create** — Создание организации в LLMaaS
- **delete** — Удаление организации

- **create_team** — Создание команды внутри организации
- **modify_team** — Редактирование команды (модели, RPM, TPM)
- **delete_team** — Удаление команды

- **create_secrets** — Создание API-ключа
- **update_secrets** — Обновление параметров API-ключа
- **delete_secrets** — Удаление API-ключа

---

## 2. Параметры создания организации

Ниже перечислены параметры, которые необходимо указать при создании организации:

* **Платформа для развертывания**
Выбирается автоматически, изменять не нужно.

* **Период бюджета**
Срок жизни организации в днях.
Пример: `30`


---

## 3. Параметры создания команды

* **Имя команды**
Уникальное имя команды в рамках организации.
Пример: `production`

* **Модель**
Название модели, доступной для команды.
Пример: `gpt-oss-120b`

* **Лимит запросов в минуту (RPM)**
Максимальное количество запросов к API в минуту.
Пример: `60`

* **Лимит токенов в минуту (TPM)**
Максимальное количество токенов (входящих + исходящих) в минуту.
Пример: `100000`

---

## 4. Параметры редактирования команды

Для существующей команды можно изменить:

* **Модель**
Новая модель для команды.
Пример: `gpt-oss-20b`

* **Лимит запросов в минуту (RPM)**
Новое значение RPM.
Пример: `120`

* **Лимит токенов в минуту (TPM)**
Новое значение TPM.
Пример: `200000`

---

## 5. Параметры создания API-ключа

Ниже перечислены параметры, которые необходимо указать при создании API-ключа:

* **Имя ключа**
Уникальное имя ключа в рамках организации.
Пример: `prod-key`

* **Команда**
Имя команды для привязки ключа. Если не указано — ключ привязывается к команде `main`.
Пример: `production`

* **Модель**
Название модели, доступной для данного ключа.
Пример: `gpt-oss-120b`

* **Лимит запросов в минуту (RPM)**
Максимальное количество запросов к API в минуту.
Пример: `60`

* **Лимит токенов в минуту (TPM)**
Максимальное количество токенов (входящих + исходящих) в минуту.
Пример: `100000`

**Важно:** Лимиты API-ключа не могут превышать лимиты команды, к которой он привязан.
При превышении операция будет отклонена с ошибкой.
Перед созданием ключа убедитесь, что `RPM <= team.RPM` и `TPM <= team.TPM`.


---

## 6. Параметры обновления API-ключа

Для уже существующего ключа можно изменить параметры. Все параметры являются опциональными:

* **Имя ключа**
Имя обновляемого ключа.
Пример: `prod-key`

* **Модель**
Новое название модели.
Пример: `gpt-oss-20b`

* **Лимит запросов в минуту (RPM)**
Новое значение RPM.
Пример: `120`

* **Лимит токенов в минуту (TPM)**
Новое значение TPM.
Пример: `200000`

**Важно:** При обновлении лимитов API-ключа новые значения не могут превышать лимиты команды.
Если указать значения выше командных, операция завершится ошибкой.
Указывайте `RPM <= team.RPM` и `TPM <= team.TPM`.

Команда определяется автоматически по стейту услуги — изменить привязку к другой команде через эту операцию нельзя.

---

## 7. Параметры удаления API-ключа

* **Команда**
Имя команды, к которой привязан ключ.
Пример: `production`

* **Имя ключа**
Имя удаляемого ключа.
Пример: `prod-key`

---

## 8. Параметры удаления команды

* **Имя команды**
Имя удаляемой команды.
Пример: `production`

---

## 9. Параметры удаления организации

Удаление организации не требует дополнительных параметров — удаляется вся организация со всеми командами и ключами.

---

## 10. Выходные параметры

После успешного создания организации платформа возвращает:

* **connectionUrl**
URL для доступа к inference API (OpenAI-совместимый).
Пример: `https://api.aillm.ru`

---


## 12. Пример использования API

После получения API-ключа клиент может обращаться к LLMaaS напрямую:

**Запрос к chat completions:**

```bash
curl https://api.aillm.ru/v1/chat/completions \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d ''{
"model": "gpt-oss-120b",
"messages": [
{"role": "user", "content": "Привет!"}
]
}''
```

**Получение списка доступных моделей:**

```bash
curl https://api.aillm.ru/v1/models \
-H "Authorization: Bearer sk-..."
```
' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 271 - kind: instance - action: create - params: - - id: 826 - code: resourceRealm - data_type: string - required: true - default: llmAi.aillm.ru - value_list: - - llmAi.aillm.ru - func: getAvailableResourceRealms - - name: create_secrets - id: 274 - kind: subresource - action: create - subresource: secrets - params: - - id: 833 - code: models - data_type: array - required: true - sort: 20 - depends_on: teamName - - id: 834 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - minvalue: 1 - - id: 835 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - minvalue: 1 - - id: 857 - code: keyName - data_type: string - required: true - sort: 10 - - id: 859 - code: teamName - data_type: string - required: true - sort: 15 - - id: 1083 - code: variablesKeys - data_type: array-map-fixed - required: true - sub_params: - - id: 573 - code: models - data_type: string - required: true - default: "" - value_list: - - '{}' - is_modifiable: false - - id: 575 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - is_modifiable: false - - id: 576 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - is_modifiable: false - - id: 577 - code: count - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 571 - code: teamName - data_type: string - required: true - default: "" - is_modifiable: false - - id: 572 - code: keyName - data_type: string - required: true - default: "" - is_modifiable: false - - name: create_team - id: 278 - kind: subresource - action: create - subresource: team - params: - - id: 858 - code: teamName - data_type: string - required: true - - id: 860 - code: models - data_type: array - required: true - - id: 861 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - - id: 862 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - - name: delete - id: 273 - kind: instance - action: delete - params: [] - - name: delete_secrets - id: 275 - kind: subresource - action: delete - subresource: secrets - params: - - id: 836 - code: keyName - data_type: string - required: true - depends_on: teamName - - id: 876 - code: teamName - data_type: string - required: true - sort: 10 - - name: delete_team - id: 280 - kind: subresource - action: delete - subresource: team - params: - - id: 868 - code: teamName - data_type: string - required: true - - name: modify_team - id: 279 - kind: subresource - action: modify - subresource: team - params: - - id: 864 - code: teamName - data_type: string - required: true - - id: 865 - code: models - data_type: array - required: true - depends_on: teamName - - id: 873 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - minvalue: 1 - - id: 874 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - minvalue: 1 - - name: update_secrets - id: 277 - kind: subresource - action: update - subresource: secrets - man: Данная операция предназначена для управления лимитами и моделями - params: - - id: 852 - code: keyName - data_type: string - required: true - depends_on: teamName - - id: 853 - code: models - data_type: array - required: true - depends_on: teamName - - id: 854 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - - id: 855 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - - id: 875 - code: teamName - data_type: string - required: true - sort: 5 diff --git a/generated/dev/resources_yaml/175_k8s_go.yaml b/generated/dev/resources_yaml/175_k8s_go.yaml deleted file mode 100644 index f5415b3..0000000 --- a/generated/dev/resources_yaml/175_k8s_go.yaml +++ /dev/null @@ -1,225 +0,0 @@ -name: k8s_go -service_id: 175 -service_display_name: Go -service_short_name: k8sGo -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 385 - kind: instance - action: create - params: - - id: 1046 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 477 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1047 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 478 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 479 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 480 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1048 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 481 - code: domain - data_type: string - required: true - default: lucee-utovgtao - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1049 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 484 - code: version - data_type: string - required: true - default: "6.0" - value_list: - - "5.4" - - "6.0" - is_modifiable: false - - id: 482 - code: gitPath - data_type: string - required: true - default: https://github.com/xahys/testlucee - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 483 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1050 - code: jsonEnv - data_type: map - required: false - default: '{}' - sort: 50 - is_modifiable: true - - name: delete - id: 384 - kind: instance - action: delete - params: [] - - name: modify - id: 388 - kind: instance - action: modify - params: - - id: 1051 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 485 - code: cpu - data_type: string - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 486 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 487 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1052 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 490 - code: version - data_type: string - required: true - default: "" - value_list: - - "6.0" - - "5.4" - is_modifiable: false - - id: 488 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 489 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1053 - code: jsonEnv - data_type: map - required: false - sort: 30 - - name: reconcile - id: 389 - kind: action - action: reconcile - params: [] - - name: restart - id: 382 - kind: action - action: restart - params: [] - - name: resume - id: 387 - kind: instance - action: resume - params: [] - - name: suspend - id: 386 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/19_vc_org.yaml b/generated/dev/resources_yaml/19_vc_org.yaml deleted file mode 100644 index 001435f..0000000 --- a/generated/dev/resources_yaml/19_vc_org.yaml +++ /dev/null @@ -1,140 +0,0 @@ -name: vc_org -service_id: 19 -service_display_name: Организация в Cloud Director -service_short_name: vcOrg -service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Url организации
Для быстрого доступа в тенант

### Статус организации
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)** привязанный к этой организации;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к vdc выше.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 136 - kind: instance - action: create - man: '`IaaS` - Доступ в Keycloak предоставляется пользователю, который создаёт услугу
`SaaS` - Без доступа к Keycloak' - params: - - id: 418 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Облако в котором будет развёрнут тенант - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - sort: 10 - - id: 556 - code: organizationType - data_type: string - required: true - default: iaas - value_list: - - iaas - - saas - man: Доступ к организации предоставляется только при выборе `iaas` - sort: 20 - - name: create_user - id: 283 - kind: subresource - action: create - subresource: user - params: - - id: 871 - code: admins - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 10 - sub_params: - - id: 238 - code: email - data_type: string - required: true - default: "" - man: Пользователи будут добавлены, только если они зарегистрированы в личном кабинете - is_modifiable: false - - name: delete - id: 137 - kind: instance - action: delete - man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого тенанта**

Если существуют инстансы VDC, Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' - params: [] - - name: delete_user - id: 284 - kind: subresource - action: delete - subresource: user - params: - - id: 872 - code: admins - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 239 - code: email - data_type: string - required: true - default: "" - is_modifiable: false - - name: modify - id: 207 - kind: instance - action: modify - man: Для возможности изменения параметров предварительно необходимо создать **Виртуальный датацентр (VDC)**, потом **Сетевой шлюз периметра (Edge Gateway)**
Также при удалении ipSpaces убедитесь, что все внешние IP удалены. В случае если хоть одна привязка vIP есть, данный ipSpace удалён **не будет** - params: - - id: 662 - code: vIPConfigure - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 39 - code: name - data_type: string - required: true - default: "" - is_modifiable: false - - id: 40 - code: count - data_type: integer > 0 - required: true - default: "" - descr: 'Пример: `1`' - is_modifiable: false - - name: reconcile - id: 249 - kind: action - action: reconcile - params: [] - - name: resume - id: 144 - kind: instance - action: resume - params: [] - - name: suspend - id: 143 - kind: instance - action: suspend - man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри тенанта - params: [] diff --git a/generated/dev/resources_yaml/1_dummy.yaml b/generated/dev/resources_yaml/1_dummy.yaml deleted file mode 100644 index 3292ca5..0000000 --- a/generated/dev/resources_yaml/1_dummy.yaml +++ /dev/null @@ -1,339 +0,0 @@ -name: dummy -service_id: 1 -service_display_name: Болванка -service_short_name: dummy -service_man: '## Болванка для тестов.

А это просто много букв, чтобы посмотреть, как ведет себя текст при переполнении контейнера.


``` json
{
"iamServiceUrl": "https://auth-api-test.ngcloud.ru/api/v1/user",
"idpResponse": {
"prefix": {
"charset": "",
"status_text": "Request Time-out",
"text": true,
"errordetail": "",
"filecontent": "Connection Timeout",
"responseheader": {},
"header": "",
"statuscode": "408 Request Time-out",
"status_code": 408,
"mimetype": "Unable to determine MIME type of file."
}
}
}
```' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 18 - kind: instance - action: create - man: Это мануал к операции CREATE сервиса Болванка - params: - - id: 198 - code: durationMs - data_type: integer >= 0 - required: true - default: "0" - descr: перед завершением подождать (sleep) столько миллисекунд - man: перед завершением подождать (sleep) столько миллисекунд - sort: 20 - is_modifiable: true - - id: 199 - code: failAtStart - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, завершить джоб, ничего не записывая в cmdb - man: '**Операция не будет завершена**
Она будет в постоянном статусе PENDING, однако в Jenkins Job завершится с ошибкой' - sort: 30 - is_modifiable: true - - id: 200 - code: failInProgress - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - man: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - sort: 40 - is_modifiable: true - - id: 201 - code: whereFail - data_type: integer > 0 - required: false - default: "1" - value_list: - - "1" - - "2" - - "3" - descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - sort: 50 - is_modifiable: true - - id: 242 - code: resourceRealm - data_type: string - required: true - default: dummy - value_list: - - dummy - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы для подключения - sort: 10 - - id: 286 - code: bodymessage - data_type: string - required: false - descr: Дата, уходящая в Vault - man: Дата, уходящая в Vault - sort: 60 - is_modifiable: true - - id: 321 - code: mapExample - data_type: map - required: false - is_modifiable: true - sub_params: - - id: 1 - code: subparam1 - data_type: string - required: false - default: "" - descr: это для администратора - man: это для пользователя - is_modifiable: false - - id: 3 - code: secret - data_type: string - required: false - default: "" - descr: это секрет - man: значение параметра маскируется. Write Only - is_modifiable: false - is_sensitive: true - - id: 2 - code: subparam2 - data_type: string - required: false - default: "" - is_modifiable: false - - id: 322 - code: jsonExample - data_type: json - required: false - descr: Для тестирования интерфейса - man: Для тестирования интерфейса. Сюда можно писать корректный json - is_modifiable: true - - id: 396 - code: nestedRefExample - required: false - - id: 647 - code: mapFixed - data_type: map-fixed - required: false - descr: Новый тип поля - sub_params: - - id: 13 - code: bol1 - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Булевский, обязательный - man: Булевский, обязательный. Проверить, как воспринимается снятый флаг - is_modifiable: false - - id: 14 - code: minStr1 - data_type: string - required: true - default: abracadabra - value_list: - - abracadabra - descr: не меньше 10 символов - is_modifiable: false - - id: 10 - code: param1 - data_type: integer > 0 - required: true - default: "1" - descr: Integer, secret - man: integer > 0, sensitive data (secret) - is_modifiable: false - is_sensitive: true - - id: 11 - code: param2 - data_type: string - required: true - default: "1" - value_list: - - '["1"' - - '"2"' - - '"3"]' - descr: Дескрипшен - man: Выбор из списка 1,2,3 - is_modifiable: false - - id: 12 - code: param3 - data_type: integer > 0 - required: true - default: "1" - descr: целое положительное - man: int > 0, обязательный - is_modifiable: false - - id: 654 - code: arrayMapFixedExample - data_type: array-map-fixed - required: false - descr: Array of map-fixed - sub_params: - - id: 18 - code: bol1 - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - is_modifiable: false - - id: 19 - code: minStr1 - data_type: string - required: true - default: "" - value_list: - - abracadabra - descr: не меньше 10 символов - is_modifiable: false - - id: 15 - code: param1 - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - is_sensitive: true - - id: 16 - code: param2 - data_type: string - required: true - default: "" - value_list: - - '["1"' - - '"2"' - - '"3"]' - is_modifiable: false - - id: 17 - code: param3 - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 863 - code: arr - data_type: array - required: true - value_list: - - test - - "2" - - val1 - - name: delete - id: 71 - kind: instance - action: delete - params: [] - - name: modify - id: 92 - kind: instance - action: modify - params: - - id: 287 - code: durationMs - data_type: integer >= 0 - required: true - default: "0" - descr: перед завершением подождать (sleep) столько миллисекунд - man: перед завершением подождать (sleep) столько миллисекунд - sort: 10 - - id: 288 - code: failAtStart - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, завершить джоб, ничего не записывая в cmdb - sort: 20 - - id: 289 - code: failInProgress - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - sort: 30 - - id: 290 - code: whereFail - data_type: integer > 0 - required: false - value_list: - - "1" - - "2" - - "3" - descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - sort: 40 - - id: 291 - code: bodymessage - required: false - descr: Дата, уходящая в Vault - man: Дата, уходящая в Vault - sort: 50 - - id: 373 - code: mapExample - data_type: map - required: false - default: '{}' - sub_params: - - id: 4 - code: secret - data_type: string - required: true - default: "" - is_modifiable: false - is_sensitive: true - - id: 374 - code: jsonExample - required: false - - name: reconcile - id: 257 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 240 - kind: action - action: redeploy - params: [] - - name: resume - id: 94 - kind: instance - action: resume - params: [] - - name: suspend - id: 93 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/20_vc_org_saas.yaml b/generated/dev/resources_yaml/20_vc_org_saas.yaml deleted file mode 100644 index 12a3c8b..0000000 --- a/generated/dev/resources_yaml/20_vc_org_saas.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: vc_org_saas -service_id: 20 -service_display_name: Организация [DEPRECATED] -service_short_name: vcOrgSaas -service_man: 'Верхнеуровневая сущность создаваемая для Заказчика.
Внутри Организация вы можете создать: Виртуальные датацентры, Каталоги.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 8 - kind: instance - action: create - man: Создание организации в Cloud Director
Id в клауд директоре создаётся на основе contragentCode - params: - - id: 185 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы - sort: 30 - - name: delete - id: 23 - kind: instance - action: delete - params: [] - - name: resume - id: 119 - kind: instance - action: resume - params: [] - - name: suspend - id: 118 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/21_vc_vdc.yaml b/generated/dev/resources_yaml/21_vc_vdc.yaml deleted file mode 100644 index 44c3cbb..0000000 --- a/generated/dev/resources_yaml/21_vc_vdc.yaml +++ /dev/null @@ -1,169 +0,0 @@ -name: vc_vdc -service_id: 21 -service_display_name: Виртуальный датацентр (vDC) -service_short_name: vc_vdc -service_man: '# Инструкция по развертыванию Виртуальный датацентр (vDC) через платформу

## 1. Общая информация

**Виртуальный датацентр (vDC)** — это логическая изолированная среда в VMware Cloud Director, предназначенная для размещения и управления виртуальными машинами, сетевой инфраструктурой и ресурсами хранения. Сервис обеспечивает выделение вычислительных ресурсов под конкретную организацию и предоставляет гибкие параметры масштабирования, контроля потребления и разделения ресурсов.

### Доступные операции

**create** — Создание виртуального датацентра (vDC) с заданными параметрами CPU, RAM, Storage и сетевой конфигурацией.
**delete** — Удаление vDC. Требуется предварительный перевод vDC в состояние *suspend*. Полное удаление возможно через 14 дней.
**modify** — Внесение изменений в параметры ресурсов (CPU/RAM/Storage), выделенных для vDC.
**suspend** — Заморозка vDC. Новые ресурсы или изменения внутри vDC становятся невозможными, работа ВМ не останавливается.
**resume** — Разморозка vDC. Восстановление возможности управления сущностями внутри vDC.

---

## 2. Параметры развертывания

Ниже перечислены входные параметры операции **create**.

### UUID Организации
Уникальный идентификатор организации в Cloud Director. Перед созданием vDC требуется предварительное создание услугу `Организация в Cloud Director`.

### Provider VDC в Cloud Director
Свойство ресурсной платформы, определяющее, какой пул физических ресурсов будет использоваться.

### Network Pool в Cloud Director
Определяет возможности сетевой конфигурации, включая создание Edge Gateway и выход во внешние сети.

### Гарантированная доля ЯДЕР
Гарантированный процент вычислительных ресурсов (CPU), закрепленный за vDC. Значение устанавливается разово при создании.

### Гарантированная доля ОЗУ
Гарантированный процент оперативной памяти, закрепленный за vDC. Также задаётся один раз при создании.

### Сколько аллоцировать на VDC ЯДЕР
Количество виртуальных ядер, выделяемых в рамках vDC.

### Сколько аллоцировать на VDC ОЗУ
Количество оперативной памяти в GB, выделяемой для vDC.

### Конфигурация Storage
Набор дисковых политик и их объёмов, выделяемых для vDC. Указывается имя политики и требуемый размер (GB).

---

## 3. Рекомендованные характеристики

### Тестовое окружение

- Количество ядер CPU: 10–20
- Аллоцируемая RAM: 16–64 GB
- Гарантированная доля CPU: 20%
- Гарантированная доля RAM: 20%
- Storage:
- Политика `SATA`
- Объём: 100–300 GB

### Промышленное окружение

- Количество ядер CPU: 16–128
- Аллоцируемая RAM: 64–512 GB
- Гарантированная доля CPU: 50%
- Гарантированная доля RAM: 50%
- Storage:
- Политики уровня `SSD`
- Объём: 1–10 TB, выбор несколько политик под разные нагрузки

---

## 4. Выходные параметры

Выходные параметры формируются операцией **create** (и используются другими операциями):

### Имя vDC
Уникальный идентификатор созданного виртуального датацентра. Используется в дальнейших операциях (modify, suspend, delete).

### Параметры выделенных ресурсов
Подтверждение итогового объёма CPU/RAM/Storage, выделенных vDC. Используются для учёта и контроля потребления.

---

## 5. Дополнительная информация

- Перед созданием vDC необходимо обеспечить наличие корректно созданной организации и доступных пула ресурсов ресурсной платформы.
- Гарантированная доля CPU и RAM задаётся **однократно при создании vDC**. После развертывания изменить эти значения невозможно; для изменения необходимо пересоздать vDC.
- Удаление vDC возможно только через **14 дней после перевода в состояние *suspend***. Удаление очищает все связанные сущности (ВМ, сети, каталоги), поэтому необходимо убедиться, что данные перенесены.
- Платформа Cloud Director использует концепцию изолированных организационных ресурсов, поэтому корректная настройка Provider VDC и Network Pool является обязательной при создании vDC.
- При изменении параметров (modify) изменяются только показатели CPU, RAM и Storage. Сетевые параметры и гарантированные доли остаются неизменными.
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 9 - kind: instance - action: create - params: - - id: 30 - code: organizationUid - data_type: uuid - required: true - ref_svc_id: 19 - man: Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` - sort: 10 - - id: 335 - code: providerVdc - data_type: string - required: true - man: Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
- sort: 30 - depends_on: organizationUid,networkProvider - - id: 361 - code: storageConfig - data_type: array-map-fixed - required: true - default: '[{}]' - man: Первая политика в списке указывается как "По умолчанию" - sort: 100 - is_modifiable: true - sub_params: - - id: 41 - code: name - data_type: string - required: true - default: "" - descr: getKeyListFromStruct(getResourceRealmConfig(getStringFromStruct(getInstanceState(params.organizationUid), "params.resourceRealm")), concat("vcd.hardware.providerVdcs['",params.providerVdc,"'].storage")) - is_modifiable: false - - id: 42 - code: size - data_type: integer > 0 - required: true - default: "2000" - is_modifiable: false - - id: 397 - code: cpuGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - descr: '% резервирования' - man: Свойство нельзя изменить после создания - sort: 60 - - id: 557 - code: cpuAllocated - data_type: integer > 0 - required: true - default: "80" - maxvalue: 1000 - minvalue: 1 - descr: шт - sort: 80 - is_modifiable: true - - id: 558 - code: memAllocated - data_type: integer > 0 - required: true - default: "200" - maxvalue: 4096 - minvalue: 1 - descr: gb - sort: 90 - is_modifiable: true - - id: 746 - code: networkProvider - data_type: string - required: true - sort: 20 - depends_on: organizationUid - - name: delete - id: 24 - kind: instance - action: delete - man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого VDC**

Если существуют инстансы Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' - params: [] - - name: modify - id: 112 - kind: instance - action: modify - params: - - id: 560 - code: cpuAllocated - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: шт - sort: 10 - - id: 561 - code: memAllocated - data_type: integer > 0 - required: true - maxvalue: 1024 - minvalue: 1 - descr: gb - sort: 20 - - id: 562 - code: storageConfig - data_type: array-map-fixed - required: true - man: Первая политика в списке указывается как "По умолчанию" - sort: 30 - sub_params: - - id: 43 - code: name - data_type: string - required: true - default: "" - is_modifiable: false - - id: 44 - code: size - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - name: reconcile - id: 250 - kind: action - action: reconcile - params: [] - - name: resume - id: 172 - kind: instance - action: resume - params: [] - - name: suspend - id: 171 - kind: instance - action: suspend - man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри VDC - params: [] diff --git a/generated/dev/resources_yaml/22_vc_nsxt.yaml b/generated/dev/resources_yaml/22_vc_nsxt.yaml deleted file mode 100644 index 158f59e..0000000 --- a/generated/dev/resources_yaml/22_vc_nsxt.yaml +++ /dev/null @@ -1,139 +0,0 @@ -name: vc_nsxt -service_id: 22 -service_display_name: Сетевой шлюз периметра (Edge) -service_short_name: vc_nsxt -service_man: '# Инструкция по развертыванию Сетевой шлюз периметра (Edge) через платформу

## 1. Общая информация

Сервис **Сетевой шлюз периметра (Edge)** предназначен для создания периметрового сетевого шлюза в рамках одного виртуального датацентра (vDC) или группы виртуальных датацентров (groupvDC).
При создании автоматически разворачивается routed-сеть с адресным пространством `10.10.102.0/24`.
Сервис обеспечивает сетевую изоляцию, маршрутизацию, а также может включать функциональность балансировщика нагрузки AVI (ALB) для последующей интеграции, включая поддержку кластеров Штурвал.

### Доступные операции

**create** — Создание нового Edge с привязкой к vDC или groupvDC и развёртыванием routed-сети.
**delete** — Удаление ранее созданного Edge. Недоступно при наличии зависимых услуг (vApp, VM, Кластер Штурвал).
**modify** — Изменение параметров и сетевых настроек существующего Edge.

---

## 2. Параметры развертывания

Ниже приведены параметры операции **create**.

### Тип родительской услуги
Определяет контекст размещения Edge.
Допустимые значения: `vdc`, `groupvdc`.
Использование:
- При выборе `vdc` обязателен параметр **UUID VDC**.
- При выборе `groupvdc` обязателен параметр **UUID Группы VDC**.

### UUID VDC
Идентификатор виртуального датацентра.
Необходимо предварительно создать vDC через услугу «Виртуальный датацентр (vDC)».
Указывается только при выборе родительского типа `vdc`.

### UUID Группы VDC
Идентификатор группы виртуальных датацентров.
Создаётся через услугу «Группа виртуальных датацентров (groupvDC)».
Используется при выборе родительского типа `groupvdc`.

### Включить ALB
Флаг активации AVI Load Balancer в Cloud Director.
Пример значения: `true`/`false`.
Нужен для развёртывания кластера Штурвал и возможности создания Virtual Services.

### Наименование segroup
Имя сервиса групп (segroup) на уровне ресурсной платформы.
Обязательно при включённом ALB.
Определяет группу, в которой будут выделяться пулы AVI для Virtual Services.

### virtualServicesCount
Количество резервируемых виртуальных сервисов (Virtual Services) на AVI.
Типичное значение: 1–10 в зависимости от нагрузки.

---

## 3. Рекомендованные характеристики

### Тестовое окружение (Test)

- Тип родительской услуги: `vdc`
- virtualServicesCount: 1–2
- ALB: выключен по умолчанию (включать только при необходимости тестирования Штурвала)
- segroup: задаётся только при включённом ALB
- VDC гарантии CPU/RAM: минимальные (поскольку изменить их после создания нельзя)

### Промышленное окружение (Production)

- Тип родительской услуги: `groupvdc` при работе с распределёнными нагрузками или `vdc` для локализованных проектов
- virtualServicesCount: 3–10, исходя из предполагаемого количества публикаций
- ALB: включён, если требуется высокая доступность сервисов или используется кластер Штурвал
- segroup: рекомендуется использовать выделенную группу под проект
- VDC гарантии CPU/RAM: повышенные, с учётом того, что изменить их после создания невозможно без пересоздания vDC

---

## 4. Выходные параметры

### Имя Edge
Уникальное название созданного сетевого шлюза, используется для ссылок и дальнейших операций.

### Имя routed-сети
Автоматически созданная routed-сеть с подсетью `10.10.102.0/24`, используется для подключения ресурсов.

### Параметры Edge
Набор параметров, указанных пользователем при создании: тип услуги, ALB, segroup, virtualServicesCount и др.
Используются в операциях modify и для анализа состояния сервиса.

---

## 5. Дополнительная информация

- В рамках сервиса **один раз при создании задаётся гарантированная доля CPU/RAM vDC**. Изменить её невозможно — требуется пересоздание vDC.
- Удаление Edge недоступно при наличии зависимых ресурсов (vApp, VM, кластер Штурвал).
- При использовании ALB необходимо корректно указывать segroup, чтобы обеспечить корректное выделение пулов AVI.
- При выборе режима `groupvdc` следует учитывать распределение нагрузки между несколькими vDC.
- SNAT для всех ресурсов внутри Edge можно активировать через операцию modify (параметры выделения VIP и ipSpace).

' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 10 - kind: instance - action: create - params: - - id: 8 - code: vdcUid - data_type: string - required: false - ref_svc_id: 21 - descr: UUID Услуги `Виртуальный датацентр (vDC)` - man: 'Необходимо, если выбран тип подключаемой VDC: `vdc`' - sort: 10 - - id: 340 - code: needEnableAVI - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Включение Load Balancer - man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать - sort: 30 - is_modifiable: true - - id: 341 - code: virtualServicesCount - data_type: integer > 0 - required: false - default: "1" - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые **резервируются** на AVI - man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` - sort: 50 - is_modifiable: true - - id: 621 - code: vdcType - data_type: string - required: true - default: vdc - value_list: - - vdc - - vdcGroup - descr: Тип родительской услуги - man: При выборе vdc обеспечивает сетевую доступность только в рамках этого vdc
При выборе groupvdc обеспечивает сетевую доступность между всеми vdc, которые включены в groupvdc - sort: 0 - - id: 622 - code: vdcGroupUid - data_type: string - required: false - ref_svc_id: 29 - descr: UUID Услуги `Группа виртуальных датацентров (groupvDC)` - man: 'Необходимо, если выбран тип подключаемой VDC: `groupvdc`' - sort: 20 - - id: 825 - code: qosProfile - required: false - descr: Параметр пока не работает
Должен возвращать поле из ресурсной платформы типа vc из .vcd.hardware.gatewayQoSProfiles

Как по идее должен работать.
Нужно заполнить или CFS vdcUid, или vdcGroupUid
Идеально конечно проверять `if (params.vdcType == "vdc" && params.vdcUid != "")` или `if (params.vdcType == "vdcGroup" && params.vdcGroupUid != "")`

Далее надо пойти по стейту vdc -> org -> resPlatform, взять gatewayQoSProfiles
Или пойти по стейту vdcgroup -> vdc -> org -> resPlatform, взять gatewayQoSProfiles

Регулярку могу написать (Виталя) - man: if (vdcUid != '") {
наборфункций1
}

elif (vdcGroupUid != "") {
наборфункций2
}

else {
return "Необходимо выбрать vdc или vdcgroup"
} - sort: 60 - depends_on: vdcUid,vdcGroupUid - is_modifiable: true - - name: delete - id: 25 - kind: instance - action: delete - man: Невозможно удалить если routed-сеть в Edge связана с:
- vApp
- VM
- Kubernetes-кластер Штурвал

Запустить операцию возможно, но будет ошибка - params: [] - - name: modify - id: 111 - kind: instance - action: modify - man: Для создания SNAT Правила убедитесь, что в организации есть свободные IP
Редактировать кол-во свободных IP можно в услуге `Организация в Cloud Director` -> `modify` - params: - - id: 368 - code: needEnableAVI - data_type: boolean - required: false - value_list: - - "false" - - "true" - descr: Включение Load Balancer - man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать - sort: 10 - - id: 369 - code: virtualServicesCount - data_type: integer > 0 - required: false - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые выделяются на AVI - man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` - sort: 30 - - id: 372 - code: ipSpaceName - data_type: string - required: false - descr: Имя ip Space для внешнего IP - man: Необходимо указывать, если включён параметр `Выделить VIP для SNAT` - sort: 50 - - id: 856 - code: qosProfile - data_type: string - required: false - sort: 40 - - name: reconcile - id: 252 - kind: action - action: reconcile - params: [] diff --git a/generated/dev/resources_yaml/23_vc_vm.yaml b/generated/dev/resources_yaml/23_vc_vm.yaml deleted file mode 100644 index 470873d..0000000 --- a/generated/dev/resources_yaml/23_vc_vm.yaml +++ /dev/null @@ -1,153 +0,0 @@ -name: vc_vm -service_id: 23 -service_display_name: VM в Cloud Director (старый формат) (vc_vm) -service_short_name: vc_vm -service_man: Виртуальная машина без каких-либо особенностей.
Данная услуга запускает vc_ip для выдачи белого адреса и подключения к ВМ -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 26 - kind: instance - action: create - params: - - id: 33 - code: vappUid - data_type: uuid - required: true - ref_svc_id: 26 - descr: InstanceUid vApp. vApp должен быть создан - man: InstanceUid vApp. vApp должен быть создан - sort: 10 - - id: 35 - code: vmName - data_type: string - required: true - default: vm0001 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: Имя ВМ. Не поддерживает спец-символы - man: Имя ВМ. Не поддерживает спец-символы - sort: 20 - - id: 36 - code: vmCpu - data_type: string - required: true - default: "2" - descr: CPU - man: CPU - sort: 30 - is_modifiable: true - - id: 37 - code: vmRam - data_type: string - required: true - default: "2" - descr: RAM (GB) - man: RAM (GB) - sort: 40 - is_modifiable: true - - id: 38 - code: vmDisksize - data_type: string - required: true - default: "10" - descr: Размер диска под сервис (GB) - man: Размер диска под сервис (GB) - sort: 50 - is_modifiable: true - - id: 176 - code: templateName - data_type: string - required: true - default: RockyLinux_9-16G - value_list: - - RockyLinux_9-16G - - OracleLinux_9-16G - - Ubuntu_24-20G - - Ubuntu_22-20G - descr: ОС - man: ОС - sort: 60 - - id: 177 - code: accessIpList - data_type: string - required: true - default: 79.111.119.78/32 185.247.187.1/32 - descr: Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов - man: IP адреса в CIDR формате через пробел для настройки аксес листов - sort: 70 - is_modifiable: true - - id: 406 - code: ipSpaceName - data_type: string - required: true - descr: Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) - - name: delete - id: 29 - kind: instance - action: delete - params: [] - - name: modify - id: 68 - kind: instance - action: modify - man: Операция автоматически останавливает ВМ. - params: - - id: 186 - code: vmCpu - data_type: integer > 0 - required: false - descr: CPU - man: CPU - sort: 10 - - id: 187 - code: vmRam - data_type: integer > 0 - required: false - descr: RAM - man: RAM - sort: 20 - - id: 188 - code: vmDisksize - data_type: integer > 0 - required: false - descr: Размер диска под сервис (GB) - man: Размер диска под сервис (GB) - sort: 30 - - id: 189 - code: accessIpList - data_type: string - required: false - descr: IP адреса в CIDR формате через пробел для настройки аксес листов - man: IP адреса в CIDR формате через пробел для настройки аксес листов - sort: 40 - - name: resume - id: 28 - kind: instance - action: resume - params: [] - - name: suspend - id: 27 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/24_vc_nat.yaml b/generated/dev/resources_yaml/24_vc_nat.yaml deleted file mode 100644 index 3e9bfb8..0000000 --- a/generated/dev/resources_yaml/24_vc_nat.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: vc_nat -service_id: 24 -service_display_name: DEPRECATED Правила маршрутизации для VM (vc_nat) -service_short_name: vcNat -service_man: Правила маршрутизации в Cloud Director. Требует наличие сетей, ВМ
DEPRECATED -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: [] diff --git a/generated/dev/resources_yaml/25_vcexternalip.yaml b/generated/dev/resources_yaml/25_vcexternalip.yaml deleted file mode 100644 index 3a91400..0000000 --- a/generated/dev/resources_yaml/25_vcexternalip.yaml +++ /dev/null @@ -1,165 +0,0 @@ -name: vcexternalip -service_id: 25 -service_display_name: Публичные IP адреса -service_short_name: vcexternalip -service_man: '**Обычно эта услуга разворачивается автоматически при развёртывании более сложных услуг**
Вручную создавать можно, однако услуга требует много параметров, о которых можно просто не знать

Резервировать публичные IP адреса можно только после создания NGcloud Organization, NGcloud Виртуальный датацентр, NGcloud Каталог, Сетевой шлюз периметра.
Вы можете назначить IP адрес для существующего экземпляра сервиса.' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 56 - kind: instance - action: create - params: - - id: 140 - code: serviceUid - data_type: string - required: true - sort: 20 - is_modifiable: true - - id: 141 - code: dnatCreate - data_type: boolean - required: true - default: "true" - descr: Необходимо создать DNAT-правило. - sort: 40 - is_modifiable: true - - id: 142 - code: internalPortAccess - data_type: string - required: true - default: "5432" - descr: Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
- sort: 60 - is_modifiable: true - - id: 143 - code: snatCreate - data_type: boolean - required: false - descr: Необходимо создать SNAT-правило.
- sort: 50 - is_modifiable: true - - id: 144 - code: internalAddrAccess - data_type: string - required: true - descr: Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true - sort: 70 - is_modifiable: true - - id: 324 - code: fromServiceNamespace - data_type: string - required: true - descr: Namespace составной услуги (для отображения правил FW) - sort: 80 - - id: 325 - code: fromServiceCloudEdgeName - data_type: string - required: false - descr: Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 90 - - id: 326 - code: fromServiceCloudVdcName - required: false - descr: Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 100 - - id: 327 - code: fromServiceCloudOrgName - data_type: string - required: false - descr: Org составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 110 - - id: 328 - code: fromServiceCloudVmwareUrl - data_type: string - required: false - descr: Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 120 - - id: 336 - code: ipSpaceName - data_type: string - required: true - descr: Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1) - sort: 30 - is_modifiable: true - - id: 488 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 200 - - id: 624 - code: fromServiceCloudEdgeScope - data_type: string - required: false - sort: 130 - - id: 626 - code: fromServiceVdcGroupName - data_type: string - required: false - sort: 140 - - name: delete - id: 57 - kind: instance - action: delete - params: [] - - name: modify - id: 197 - kind: instance - action: modify - params: - - id: 627 - code: serviceUid - data_type: string - required: true - sort: 20 - - id: 628 - code: ipSpaceName - data_type: string - required: true - sort: 30 - - id: 629 - code: dnatCreate - data_type: boolean - required: true - sort: 40 - - id: 630 - code: snatCreate - data_type: boolean - required: true - sort: 50 - - id: 631 - code: internalPortAccess - data_type: string - required: true - sort: 60 - - id: 632 - code: internalAddrAccess - data_type: string - required: true - sort: 70 diff --git a/generated/dev/resources_yaml/26_vapp.yaml b/generated/dev/resources_yaml/26_vapp.yaml deleted file mode 100644 index 79f2836..0000000 --- a/generated/dev/resources_yaml/26_vapp.yaml +++ /dev/null @@ -1,79 +0,0 @@ -name: vapp -service_id: 26 -service_display_name: Виртуальный каталог ВМ (vApp) -service_short_name: vapp -service_man: '# Инструкция по развертыванию Виртуальный каталог ВМ (vApp) через платформу

## 1. Общая информация

**Виртуальный каталог ВМ (vApp)** — это логическая каталог, объединённая в единый управляемый объект. Сервис предназначен для совместного развертывания, управления жизненным циклом и сетевого взаимодействия набора связанных ВМ. Основные функции включают:
- централизованное управление включением, выключением и приостановкой группы ВМ;
- унифицированный сетевой доступ через привязанный Edge-шлюз;

### Доступные операции

**create** — Создание нового vApp с привязкой к заданным услугам VDC и Edge
**delete** — Удаление существующего vApp. Требуется предварительный перевод vApp в состояние *suspend*. Полное удаление возможно через 14 дней.
**suspend** — Остановка vApp (выключение всех ВМ внутри каталога)
**resume** — Запуск vApp (включение всех ВМ внутри каталога)

---

## 2. Параметры развертывания

Параметры берутся из операции **create**.

### UUID Услуги VDC
Уникальный идентификатор услуги «Виртуальный датацентр (vDC)».
Используется для определения вычислительной инфраструктуры, в которой будет развернут vApp.

### UUID Услуги Edge
Уникальный идентификатор услуги «Сетевой шлюз периметра (Edge)».
Обеспечивает маршрутизацию и сетевое подключение. Может быть привязан к группе VDC, поэтому выбирается отдельно.

### Имя vApp
Строгое имя, соответствующее маске формирования DNS-имени.
Используется как часть в DNS-имени при создании виртуальных машин.

---

## 3. Рекомендованные характеристики
Так как является промежуточным этапом для создания ВМ, все характеристики описываются в услуге `Виртуальная машина`

---

## 4. Выходные параметры

### Параметры vApp
Заданные при создании vApp

---

## 5. Дополнительная информация

- Привязка к VDC и Edge осуществляется раздельно, поскольку один Edge может обслуживать несколько VDC (через услугу `Группа виртуальных датацентров (groupvDC)`). Это гарантирует возможность развернуть ВМ в единой сети, но в разных виртуальных датацентрах.
- Перед удалением vApp необходимо вручную перевести его в состояние **suspend**. Удаление возможно только спустя 14 дней после этого шага.
- Операция delete удаляет весь vApp полностью, включая все виртуальные машины, связанные с ним.
- Рекомендуется заранее планировать сетевые связи и назначения DNS-имён для обеспечения корректной работы сервисов, развернутых внутри vApp.
- При проектировании инфраструктуры следует учитывать зависимости между Edge-шлюзом, VDC и размещаемыми ВМ, чтобы избежать ограничений при масштабировании и интеграции.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 69 - kind: instance - action: create - params: - - id: 190 - code: nsxtUid - data_type: uuid - required: true - ref_svc_id: 22 - descr: UUID Услуги `Сетевой шлюз периметра (Edge)` - man: К какой сети может быть подключен виртуальный каталог - sort: 20 - - id: 191 - code: vappName - data_type: string - required: true - default: 9pv2jybu - regex: ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$ - minlength: 3 - descr: Уникальное в рамках организации - man: Имеет строгую маску `^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$`

Используется в формировании DNS-Имени (для виртуальных машин) - sort: 30 - - id: 623 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - descr: UUID Услуги `Виртуальный датацентр (vDC)` - man: В каких ресурсах будет создан виртуальный каталог - sort: 10 - - name: delete - id: 70 - kind: instance - action: delete - params: [] - - name: reconcile - id: 285 - kind: action - action: reconcile - params: [] - - name: resume - id: 209 - kind: instance - action: resume - params: [] - - name: suspend - id: 208 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/27_vc_vm_v2.yaml b/generated/dev/resources_yaml/27_vc_vm_v2.yaml deleted file mode 100644 index 2b1d235..0000000 --- a/generated/dev/resources_yaml/27_vc_vm_v2.yaml +++ /dev/null @@ -1,71 +0,0 @@ -name: vc_vm_v2 -service_id: 27 -service_display_name: VM в Cloud Director (vc_vmV2) -service_short_name: vc_vm_v2 -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 100 - kind: instance - action: create - params: - - id: 301 - code: serverListJson - data_type: json - required: true - default: '[{}]' - maxlength: 65536 - minlength: 10 - descr: Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) - sort: 20 - is_modifiable: true - - name: delete - id: 101 - kind: instance - action: delete - params: [] - - name: modify - id: 104 - kind: instance - action: modify - params: - - id: 302 - code: serverListJson - data_type: json - required: true - default: '[{}]' - maxlength: 65536 - minlength: 10 - descr: Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) - sort: 10 - - name: resume - id: 102 - kind: instance - action: resume - params: [] - - name: suspend - id: 103 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/28_vc_vm_v3.yaml b/generated/dev/resources_yaml/28_vc_vm_v3.yaml deleted file mode 100644 index f05d808..0000000 --- a/generated/dev/resources_yaml/28_vc_vm_v3.yaml +++ /dev/null @@ -1,278 +0,0 @@ -name: vc_vm_v3 -service_id: 28 -service_display_name: Виртуальная машина -service_short_name: vc_vm_v3 -service_man: '# Документация по разворачиванию ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\ложь, определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed" (В нем не будет работать сбор метрик zabbix).

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 132 - kind: instance - action: create - man: '# Документация по использованию услуги развёртывания ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\лож определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed".

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' - params: - - id: 407 - code: vappUid - data_type: uuid - required: true - ref_svc_id: 26 - sort: 10 - - id: 408 - code: vmName - data_type: string - required: true - default: web01 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - sort: 20 - - id: 409 - code: vmCpu - data_type: integer > 0 - required: true - default: "1" - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 30 - is_modifiable: true - - id: 410 - code: vmRam - data_type: integer > 0 - required: true - default: "1" - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 40 - is_modifiable: true - - id: 411 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 50 - is_modifiable: true - - id: 412 - code: ipSpaceName - data_type: string - required: true - sort: 60 - depends_on: vappUid - is_modifiable: true - - id: 413 - code: accessIpList - data_type: json - required: false - regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 70 - is_modifiable: true - - id: 414 - code: imageVm - data_type: string - required: true - sort: 90 - depends_on: vappUid - - id: 415 - code: cloudInit - data_type: yaml - required: false - descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. - sort: 200 - - id: 416 - code: userLogin - required: true - default: myuser - descr: Учётка SSH - sort: 180 - - id: 417 - code: userPublicKey - data_type: string - required: true - descr: Публичная часть SSH-Ключа для пользователя - sort: 190 - - id: 448 - code: accessPortList - data_type: string - required: true - default: '["22"]' - descr: 'По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX' - sort: 80 - is_modifiable: true - - name: delete - id: 133 - kind: instance - action: delete - params: [] - - name: modify - id: 159 - kind: instance - action: modify - params: - - id: 493 - code: vmCpu - data_type: integer > 0 - required: false - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 20 - - id: 494 - code: vmRam - data_type: integer > 0 - required: false - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 30 - - id: 495 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 40 - - id: 496 - code: ipSpaceName - data_type: string - required: false - value_list: - - no-needed - - internet-antiddos-v1 - - internet-no-antiddos-v1 - - internet-ipv4-v1 - descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev - sort: 50 - - id: 497 - code: accessIpList - data_type: json - required: false - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 60 - - id: 498 - code: accessPortList - data_type: json - required: false - descr: Формат массива
По каким портам открыть доступ к ВМ.
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX - sort: 70 - - name: reconcile - id: 286 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 164 - kind: action - action: redeploy - params: - - id: 509 - code: vmName - data_type: string - required: true - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - sort: 10 - - id: 510 - code: vmCpu - data_type: integer > 0 - required: true - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 20 - - id: 511 - code: vmRam - data_type: integer > 0 - required: true - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 30 - - id: 512 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 40 - - id: 513 - code: ipSpaceName - data_type: string - required: false - value_list: - - no-needed - - internet-antiddos-v1 - - internet-no-antiddos-v1 - - internet-ipv4-v1 - descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev - sort: 50 - - id: 514 - code: accessIpList - data_type: json - required: false - regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 60 - - id: 515 - code: imageVm - data_type: string - required: true - value_list: - - RockyLinux_9-16G-cloudinit - - Ubuntu_22-20G - - Debian_13-20G - sort: 80 - - id: 516 - code: userLogin - data_type: string - required: true - descr: Учётка SSH - sort: 180 - - id: 517 - code: userPublicKey - data_type: string - required: true - descr: Публичная часть SSH-Ключа для пользователя - sort: 190 - - id: 518 - code: cloudInit - data_type: yaml - required: false - descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. - sort: 200 - - id: 520 - code: accessPortList - data_type: json - required: false - descr: По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX - sort: 70 - - name: resume - id: 135 - kind: instance - action: resume - params: [] - - name: suspend - id: 134 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/29_vc_vdc_group.yaml b/generated/dev/resources_yaml/29_vc_vdc_group.yaml deleted file mode 100644 index 21afd35..0000000 --- a/generated/dev/resources_yaml/29_vc_vdc_group.yaml +++ /dev/null @@ -1,74 +0,0 @@ -name: vc_vdc_group -service_id: 29 -service_display_name: Группа датацентров -service_short_name: vcVdcGroup -service_man: '# Инструкция по развертыванию Группа виртуальных датацентров (groupvDC) через платформу

## 1. Общая информация

Сервис **Группа виртуальных датацентров (groupvDC)** предназначен для логического объединения нескольких виртуальных датацентров (vDC) в единую группу. Это позволяет растягивать SDN между разными vDC и централизованно управлять сетевой связностью. Управление инфраструктурой осуществляется через Cloud Director.
Сервис применяется в случаях, когда требуется обеспечить единое сетевое пространство для распределенных виртуальных датацентров.

### Доступные операции

**create** — Создание новой группы виртуальных датацентров.
**delete** — Удаление существующей группы vDC.
**add_vdc** — Добавление дополнительного виртуального датацентра в группу.
**remove_vdc** — Удаление ранее добавленного виртуального датацентра из группы.

---

## 2. Параметры развертывания

Параметры операции **create**:

### UUID VDC
Идентификатор виртуального датацентра, который будет являться первичным в группе.
Описание: требуется предварительно создать виртуальный датацентр с помощью услуги `Виртуальный датацентр (vDC)`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Для тестового окружения создавать группу VDC нет необходимости, так как это создаёт дополнительный слой абстракции в Cloud Director

### Промышленное окружение
- Для промышленного окружения, если в тенанте существуют разные VDC, создание groupvdc позволит снизить сетевые издержки (за счёт общей сети)

---

## 4. Выходные параметры

### Параметры groupvdc
Набор параметров, указанных пользователем при создании

---

## 5. Дополнительная информация
- Группа vDC используется для растягивания SDN между несколькими виртуальными датацентрами. Управление всеми сетевыми элементами внутри группы выполняется Cloud Director.
- Удалить первичный vDC (тот, что был указан при операции **create**) невозможно. Для его удаления необходимо удалить всю группу и создать новую.
- Перед добавлением любого vDC в группу он должен быть предварительно развернут через сервис `Виртуальный датацентр (vDC)`.
- Сети создаются не в этой услуге, а в услуге `Сетевой шлюз периметра (Edge)`. В ней необходимо будет выбрать тип `groupvdc`' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: add_vdc - id: 192 - kind: subresource - action: add - subresource: vdc - man: '**Не нужно** выбирать UUID VDC, который уже был добавлен в группу' - params: - - id: 619 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - sort: 10 - - name: create - id: 191 - kind: instance - action: create - man: '**VDC Группа** нужна для объединения всех VDC в организации в одну логическую группу.
Это позволит, к примеру, создать **общую сеть** для всех VDC в организации

- Для создания группы VDC необходимо указать **инстанс** услуги VDC (`Виртуальный датацентр`)
- **Важный момент** - удалить эту VDC из группы нельзя. Если такая потребность появится, необходимо будет **ПОЛНОСТЬЮ** удалить группу VDC и уже потом создать новую' - params: - - id: 618 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - sort: 10 - - name: delete - id: 195 - kind: instance - action: delete - params: [] - - name: reconcile - id: 251 - kind: action - action: reconcile - params: [] - - name: remove_vdc - id: 193 - kind: subresource - action: remove - subresource: vdc - params: - - id: 620 - code: vdcName - data_type: string - required: true - sort: 10 diff --git a/generated/dev/resources_yaml/2_template.yaml b/generated/dev/resources_yaml/2_template.yaml deleted file mode 100644 index 8d6d783..0000000 --- a/generated/dev/resources_yaml/2_template.yaml +++ /dev/null @@ -1,222 +0,0 @@ -name: template -service_id: 2 -service_display_name: Темплейт k8s -service_short_name: template -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 74 - kind: instance - action: create - params: - - id: 813 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 175 - code: disk - data_type: integer > 0 - required: true - default: "5" - is_modifiable: false - - id: 173 - code: memory - data_type: integer > 0 - required: true - default: "128" - is_modifiable: false - - id: 174 - code: replicas - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 172 - code: cpu - data_type: integer > 0 - required: true - default: "100" - is_modifiable: false - - id: 814 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 176 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - is_modifiable: false - - id: 815 - code: accessConfiguration - data_type: map-fixed - required: true - is_modifiable: true - sub_params: - - id: 182 - code: slaveAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - id: 177 - code: type - data_type: string - required: true - default: ingress - value_list: - - ingress - - service - is_modifiable: false - - id: 178 - code: domain - data_type: string - required: true - default: abracadabra-mgonny3n - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 179 - code: masterIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 180 - code: masterAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - id: 181 - code: slaveIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - name: delete - id: 75 - kind: instance - action: delete - params: [] - - name: modify - id: 76 - kind: instance - action: modify - params: - - id: 816 - code: clusterConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 183 - code: cpu - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 185 - code: disk - data_type: integer >= 0 - required: true - default: "" - is_modifiable: false - - id: 184 - code: memory - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 186 - code: replicas - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 817 - code: accessConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 191 - code: masterAccessList - data_type: json - required: true - default: "" - is_modifiable: false - - id: 190 - code: slaveIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 192 - code: slaveAccessList - data_type: json - required: true - default: "" - is_modifiable: false - - id: 187 - code: type - data_type: string - required: true - default: "" - value_list: - - ingress - - service - is_modifiable: false - - id: 188 - code: domain - data_type: string - required: true - default: "" - is_modifiable: false - - id: 189 - code: masterIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - name: reconcile - id: 298 - kind: action - action: reconcile - params: [] - - name: resume - id: 98 - kind: instance - action: resume - params: [] - - name: suspend - id: 97 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/32_vmpostgre.yaml b/generated/dev/resources_yaml/32_vmpostgre.yaml deleted file mode 100644 index d4734a8..0000000 --- a/generated/dev/resources_yaml/32_vmpostgre.yaml +++ /dev/null @@ -1,71 +0,0 @@ -name: vmpostgre -service_id: 32 -service_display_name: vc_vm_postgresql_std -service_short_name: vmpostgre -service_man: Сервис настройки PostgreSQL для виртуальной машины.
Требует наличия ВМ -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 32 - kind: instance - action: create - params: - - id: 45 - code: vmUid - data_type: uuid - required: true - ref_svc_id: 23 - descr: InstanceUid ВМ. ВМ должна быть создана
- - id: 46 - code: userDb - data_type: string - required: true - default: servicedb - descr: Наименование Базы данных
- - id: 47 - code: userLogin - data_type: string - required: true - default: serviceuser - descr: Пользователь базы данных
- - id: 48 - code: userPassword - data_type: string - required: true - default: fj29gm48fm!d374mc9 - descr: Пароль от доступа к Базе данных
- - id: 278 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - - name: delete - id: 33 - kind: instance - action: delete - params: [] diff --git a/generated/dev/resources_yaml/50_nextcloud.yaml b/generated/dev/resources_yaml/50_nextcloud.yaml deleted file mode 100644 index 5697b22..0000000 --- a/generated/dev/resources_yaml/50_nextcloud.yaml +++ /dev/null @@ -1,485 +0,0 @@ -name: nextcloud -service_id: 50 -service_display_name: Nextcloud -service_short_name: nextcloud -service_man: 'Ресурсная платформа: k8s. Автоматизация: helm chart' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 16 - kind: instance - action: create - man: Для установки необходимо:
- ресурсная платформа, где разворачивается Nextcloud, обязана совпадать с ресурсными платформами, где развёрнут Redis и PostgreSQL - params: - - id: 1055 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 492 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 493 - code: psqlEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 494 - code: redisEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 495 - code: psqlUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета` - is_modifiable: false - - id: 496 - code: redisUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета` - is_modifiable: false - - id: 1056 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 497 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 498 - code: memory - data_type: integer > 0 - required: true - default: "1024" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 499 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - is_modifiable: false - - id: 500 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 1057 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 501 - code: domain - data_type: string - required: true - default: nextcloud-xcq3u70l - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1058 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 502 - code: version - data_type: string - required: true - default: 33.0.4 - value_list: - - 33.0.4 - is_modifiable: false - - id: 503 - code: documentServer - data_type: string - required: true - default: collabora - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - id: 549 - code: logLevel - data_type: string - required: true - default: INFO - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - name: create_backup - id: 404 - kind: subresource - action: create - subresource: backup - man: Перед запуском обязательно надо перевести экземпляр Nextcloud в статус 'suspended' - params: - - id: 1077 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 578 - code: veleroUid - data_type: string - required: true - default: "" - man: Предварительно необходимо создать инстанс услуги `Velero`
Из этого инстанса будут взяты точки восстановления
Если восстановление производится на другую ресурсную платформу, важно чтобы предварительно все компоненты связующие были либо перенесены, либо были доступны снаружи - is_modifiable: false - - name: delete - id: 17 - kind: instance - action: delete - man: DEV-Ветка удаления Nextcloud - params: [] - - name: modify - id: 67 - kind: instance - action: modify - params: - - id: 1059 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 504 - code: cpu - data_type: integer > 0 - required: true - default: "" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 505 - code: memory - data_type: integer > 0 - required: true - default: "" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 506 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - is_modifiable: false - - id: 507 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 1060 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 508 - code: version - data_type: string - required: true - default: "" - value_list: - - 33.0.4 - is_modifiable: false - - id: 509 - code: documentServer - data_type: string - required: true - default: "" - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - id: 550 - code: logLevel - data_type: string - required: true - default: "" - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - name: reconcile - id: 390 - kind: action - action: reconcile - params: [] - - name: recovery - id: 406 - kind: action - action: recovery - params: - - id: 1078 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 559 - code: recoveryPoint - data_type: string - required: true - default: "" - is_modifiable: false - - id: 557 - code: psqlEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 558 - code: redisEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 560 - code: psqlUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета`
Если откат производится на ту же версию Nextcloud, в теории возможно развернуться в существующую базу

Предпочтительно создать отдельный инстанс PostgreSQL, который был восстановлен также через Recovery - is_modifiable: false - - id: 561 - code: redisUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета` - is_modifiable: false - - id: 556 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 570 - code: veleroUid - data_type: string - required: true - default: "" - man: Из этого инстанса будут взяты точки восстановления
Если восстановление производится на другую ресурсную платформу, важно чтобы предварительно все компоненты связующие были либо перенесены, либо были доступны снаружи - is_modifiable: false - - id: 1079 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 562 - code: cpu - data_type: integer > 0 - required: true - default: "" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 563 - code: memory - data_type: integer > 0 - required: true - default: "" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 564 - code: replicas - data_type: integer > 0 - required: true - default: 1,2 - is_modifiable: false - - id: 565 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 1080 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 566 - code: domain - data_type: string - required: true - default: nextcloud-fsvgmnwj - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1081 - code: appConfiguration - data_type: map-fixed - required: true - man: Версия Nextcloud будет взята из бэкапа - sort: 40 - sub_params: - - id: 568 - code: logLevel - data_type: string - required: true - default: NONE - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - id: 569 - code: version - data_type: string - required: false - default: "" - man: Версия Nextcloud будет исправлена на ту версию, которая указана в бэкапе - is_modifiable: false - - id: 567 - code: documentServer - data_type: string - required: true - default: collabora - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - name: restart - id: 66 - kind: action - action: restart - params: [] - - name: resume - id: 129 - kind: instance - action: resume - params: [] - - name: suspend - id: 128 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/81_superset.yaml b/generated/dev/resources_yaml/81_superset.yaml deleted file mode 100644 index f5b8020..0000000 --- a/generated/dev/resources_yaml/81_superset.yaml +++ /dev/null @@ -1,107 +0,0 @@ -name: superset -service_id: 81 -service_display_name: Apache Superset -service_short_name: superset -service_man: 'Apache Superset как сервис.
Ресурсная платформа: k8s.
Автоматизация: k8s operator.
Авторизация: 2FA keycloak (для доменного логина @mgmt.nubes.ru)' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 12 - kind: instance - action: create - man: DEV-Ветка сборки Superset - params: - - id: 14 - code: domain - data_type: string - required: true - default: superset-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 20 - code: emails - data_type: string - required: true - default: testuser1 - descr: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел - man: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел - sort: 70 - - id: 96 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро - man: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро - sort: 30 - - id: 97 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 40 - - id: 98 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - - id: 109 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 227 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 60 - - name: delete - id: 13 - kind: instance - action: delete - man: DEV-Ветка удаления Superset - params: [] diff --git a/generated/dev/resources_yaml/82_harbor.yaml b/generated/dev/resources_yaml/82_harbor.yaml deleted file mode 100644 index 45e6d7d..0000000 --- a/generated/dev/resources_yaml/82_harbor.yaml +++ /dev/null @@ -1,129 +0,0 @@ -name: harbor -service_id: 82 -service_display_name: Container Registry -service_short_name: harbor -service_man: 'Расширенный реестр для управления Docker-образами с enterprise-функциями безопасности, репликации и контроля доступа.
Container Registry позволяет хранить, развертывать и управлять Docker-образами контейнеров в инфраструктуре NGcloud.
Сервис можно использовать в процессе разработки контейнеров. Например, вы можете настроить систему автоматизации на работу с реестром в Container Registry в рамках процесса непрерывной интеграции. Контейнеры идеально подходят для запуска небольших, независимых и легко изменяемых сервисов.
Чтобы начать работу, создайте реестр и загрузите в него Docker-образ
Авторизация: keycloak' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 14 - kind: instance - action: create - man: DEV-Ветка сборки Harbor - params: - - id: 16 - code: domain - data_type: string - required: true - default: harbor-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 20 - - id: 110 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 30 - - id: 228 - code: resourceCPU - data_type: integer > 0 - required: true - default: "1000" - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 40 - is_modifiable: true - - id: 229 - code: resourceMemory - data_type: integer > 0 - required: true - default: "1024" - maxvalue: 8192 - minvalue: 512 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 50 - is_modifiable: true - - id: 230 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 60 - is_modifiable: true - - id: 231 - code: s3Uid - data_type: uuid - required: true - ref_svc_id: 12 - descr: UUID экземпляра услуги S3 - man: UUID экземпляра услуги S3 - sort: 10 - - name: delete - id: 15 - kind: instance - action: delete - man: DEV-Ветка удаления Harbor - params: [] - - name: modify - id: 270 - kind: instance - action: modify - params: - - id: 822 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - - id: 823 - code: resourceCPU - data_type: integer > 0 - required: false - maxvalue: 4000 - minvalue: 500 - - id: 824 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 512 - - name: reconcile - id: 405 - kind: action - action: reconcile - params: [] diff --git a/generated/dev/resources_yaml/86_k8s_velero.yaml b/generated/dev/resources_yaml/86_k8s_velero.yaml deleted file mode 100644 index d252840..0000000 --- a/generated/dev/resources_yaml/86_k8s_velero.yaml +++ /dev/null @@ -1,197 +0,0 @@ -name: k8s_velero -service_id: 86 -service_display_name: Velero -service_short_name: k8sVelero -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 332 - kind: instance - action: create - params: - - id: 962 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 336 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 337 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - - "3" - is_modifiable: false - - id: 334 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 963 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 338 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - is_modifiable: false - - id: 964 - code: nodeAgentConfiguration - data_type: map-fixed - required: true - man: Реплик выставляется автоматически столько, сколько Worker-узлов в кластере - sort: 30 - is_modifiable: true - sub_params: - - id: 551 - code: cpu - data_type: integer > 0 - required: true - default: "2000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 552 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 1076 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 555 - code: s3BucketUid - data_type: uuid - required: true - default: "" - man: Экземпляр S3-Бакета обязателен
Учётная запись будет создана автоматически, если её нету - is_modifiable: false - - name: delete - id: 331 - kind: instance - action: delete - params: - - id: 1082 - code: deleteS3Bucket - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - sort: 10 - - name: modify - id: 334 - kind: instance - action: modify - params: - - id: 965 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 345 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 347 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 348 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - - "3" - is_modifiable: false - - id: 966 - code: nodeAgentConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 553 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 554 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - name: reconcile - id: 336 - kind: action - action: reconcile - params: [] - - name: resume - id: 335 - kind: instance - action: resume - params: [] - - name: suspend - id: 333 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/87_k8svalkey.yaml b/generated/dev/resources_yaml/87_k8svalkey.yaml deleted file mode 100644 index b3b7ca2..0000000 --- a/generated/dev/resources_yaml/87_k8svalkey.yaml +++ /dev/null @@ -1,275 +0,0 @@ -name: k8svalkey -service_id: 87 -service_display_name: Valkey -service_short_name: k8svalkey -service_man: Valkey-кластер.
Пока в статусе EXPEREMENTAL -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 396 - kind: instance - action: create - params: - - id: 1066 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 527 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1067 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 528 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 530 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 531 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 544 - code: shards - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - - "3" - - "4" - - "5" - is_modifiable: false - - id: 529 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 1068 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 533 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 532 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 1071 - code: valkeyConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 545 - code: version - data_type: string - required: true - default: 0.1.0 - value_list: - - 0.1.0 - is_modifiable: false - - id: 1072 - code: valkeyConf - data_type: array-map-fixed - required: false - sort: 50 - sub_params: - - id: 546 - code: paramName - data_type: string - required: true - default: "" - is_modifiable: false - - id: 547 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://github.com/valkey-io/valkey-operator) - is_modifiable: false - - name: create_user - id: 401 - kind: subresource - action: create - subresource: user - params: - - id: 1073 - code: username - data_type: string - required: true - sort: 10 - - id: 1074 - code: permissions - data_type: string - required: true - sort: 20 - - name: delete - id: 395 - kind: instance - action: delete - params: [] - - name: delete_user - id: 402 - kind: subresource - action: delete - subresource: user - params: - - id: 1075 - code: username - data_type: string - required: true - - name: modify - id: 400 - kind: instance - action: modify - params: - - id: 1069 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 548 - code: shards - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - - "3" - - "4" - - "5" - is_modifiable: false - - id: 537 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 536 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 538 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 539 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1070 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 541 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 540 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: reconcile - id: 397 - kind: action - action: reconcile - params: [] - - name: resume - id: 399 - kind: instance - action: resume - params: [] - - name: suspend - id: 398 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/88_k8s_ziti_controller.yaml b/generated/dev/resources_yaml/88_k8s_ziti_controller.yaml deleted file mode 100644 index 4e98398..0000000 --- a/generated/dev/resources_yaml/88_k8s_ziti_controller.yaml +++ /dev/null @@ -1,93 +0,0 @@ -name: k8s_ziti_controller -service_id: 88 -service_display_name: k8sZitiController -service_short_name: k8sZitiController -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 99 - kind: instance - action: create - params: - - id: 294 - code: resourceCPU - data_type: integer > 0 - required: true - default: "300" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (milicores) - sort: 30 - - id: 295 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (MBytes) - sort: 40 - - id: 296 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - - id: 297 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 60 - - id: 298 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 20 - - id: 299 - code: domain - data_type: string - required: true - default: k8sZitiController-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - name: delete - id: 105 - kind: instance - action: delete - params: [] diff --git a/generated/dev/resources_yaml/89_flask.yaml b/generated/dev/resources_yaml/89_flask.yaml deleted file mode 100644 index 2fd1dba..0000000 --- a/generated/dev/resources_yaml/89_flask.yaml +++ /dev/null @@ -1,240 +0,0 @@ -name: flask -service_id: 89 -service_display_name: Web-сервер с фреймворком Flask -service_short_name: flask -service_man: '# Инструкция по развертыванию Web-сервер с фреймворком Flask через платформу

## 1. Общая информация

Сервис **Web-сервер с фреймворком Flask** предназначен для развертывания веб-приложений на базе Flask.

### Доступные операции

create — включить сервис, развернуть веб-приложение с указанными параметрами.
delete — удалить сервис, только после suspend.
modify — изменить настройки сервиса и переразвернуть.
suspend — выключить веб-приложение, остановить обслуживание.
resume — включить веб-приложение после suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Имя домена
Описание: доменное имя для доступа к веб-приложению. Если указан FQDN, происходит проверка DNS на валидность А-записи. А-запись должна указывать на балансировщик ресурсной платформы. Если указано имя в рамках одного октета, оно будет добавлено в публичную DNS-зону.
Пример значения: строка, допустимые FQDN или имя в одном октете. Пример: example.com или myapp.

### Платформа для развертывания
Описание: кластер Kubernetes, в котором будет развернуто приложение.
Пример значения: идентификатор кластера Kubernetes.

### Квота ядра пода
Описание: количество CPU, выделяемое на под, в Milicores.
Пример значения: целое число, например 500 для 0.5 ядра.

### Квота памяти пода
Описание: количество памяти, выделяемое на под, в Megabytes.
Пример значения: целое число, например 512 для 512 MB.

### Кол-во реплик пода
Описание: количество инстансов пода для масштабирования.
Пример значения: целое число, шт, например 2.

### URL Проекта (gitpath)
Описание: полный HTTPS путь до проекта в Git-репозитории.
Пример значения: URL в формате https://github.com/myuser/myproject.git. Доступ к репозиторию должен быть публичным.

### URL HealthCheck
Описание: путь для проверки здоровья приложения внутри пода.
Пример значения: строка, путь, например /api/v1/health.

### Переменные окружения пода
Описание: структура данных, которая будет передана как переменные окружения в под.
Пример значения: ключ-значение, например {"ENV": "production"}.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Квота ядра пода: 300m (0.3 ядра)
- Квота памяти пода: 256 MB
- Кол-во реплик пода: 1

### Промышленное окружение
- Квота ядра пода: от 1000m (1 ядро) в зависимости от нагрузки
- Квота памяти пода: от 1024 MB (1 GB) с учетом потребностей приложения
- Кол-во реплик пода: от 2 для обеспечения отказоустойчивости

---

## 4. Выходные параметры
Операция create не возвращает явных выходных параметров. Успешное выполнение операции приводит к развертыванию сервиса, доступного по указанному домену.

---

## 5. Дополнительная информация
- В корне проекта должен находиться файл requirements.txt со списком зависимостей Python.
- Исходный код веб-приложения должен располагаться в директории site.
- Запуск веб-сервера осуществляется командой python app.py.
- Используется версия Python: python:3.12-slim.
- Git-репозиторий должен быть публично доступен для платформы.
- Операция delete возможна только после suspend.
- Операция modify приводит к переразвертыванию с новыми параметрами.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 376 - kind: instance - action: create - params: - - id: 1037 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 460 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1038 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 463 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 461 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 462 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 1039 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 464 - code: domain - data_type: string - required: true - default: flask-y0lfavjf - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1040 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 467 - code: version - data_type: string - required: true - default: "3.12" - value_list: - - "3.12" - is_modifiable: false - - id: 465 - code: gitPath - data_type: string - required: true - default: https://github.com/Foxyhhd/Baldurs-Gate-test.git - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 466 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1041 - code: jsonEnv - data_type: map - required: false - default: '{}' - sort: 50 - is_modifiable: true - - name: delete - id: 379 - kind: instance - action: delete - params: [] - - name: modify - id: 375 - kind: instance - action: modify - params: - - id: 1042 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 468 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 469 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 470 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1043 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 473 - code: version - data_type: string - required: true - default: "" - value_list: - - "3.12" - is_modifiable: false - - id: 471 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 472 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1044 - code: jsonEnv - data_type: map - required: false - sort: 30 - - name: reconcile - id: 381 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 380 - kind: action - action: redeploy - params: - - id: 1045 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 474 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: resume - id: 378 - kind: instance - action: resume - params: [] - - name: suspend - id: 377 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/90_postgres.yaml b/generated/dev/resources_yaml/90_postgres.yaml deleted file mode 100644 index c4d040e..0000000 --- a/generated/dev/resources_yaml/90_postgres.yaml +++ /dev/null @@ -1,920 +0,0 @@ -name: postgres -service_id: 90 -service_display_name: PostgreSQL -service_short_name: postgres -service_man: '# Инструкция по развертыванию PostgreSQL через платформу

---
## 1. Общая информация

PostgreSQL — это надёжная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая расширенный набор функций, транзакции и хранение сложных типов данных. Развёртывание полностью автоматизировано платформой на базе Zalando Postgres Operator с использованием Kubernetes.

## Доступные операции

- **create** — Создание кластера
- **delete** — Удаление кластера
- **modify** — Модификация кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера
- **restart** — Перезапуск кластера
- **recovery** — Восстановление из резервной копии

- **create_user** — Создание пользователя
- **delete_user** — Удаление пользователя

- **create_database** — Создание базы данных
- **delete_database** — Удаление базы данных


---
## 2. Параметры развертывания кластера

Ниже перечислены параметры, которые необходимо указать при создании ресурса PostgreSQL:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество инстансов (реплик) в кластере PostgreSQL. Пример: `2` (master + slave).

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

* **Выделяет внешний IP для доступа к slave:**
Флаг для выделения внешнего IP для slave (реплики чтения). Работает, если slave присутствует в кластере.

* **Имя ipSpace для публикации внешнего IP slave:**
Если включен параметр для slave, то поле необходимо заполнить.

* **UUID экземпляра услуги S3:**
UUID экземпляра S3 для хранения резервных копий.

* **Версия PostgreSQL:**
Версия PostgreSQL для развертывания. Пример: `16`

* **Расписание бэкапов (ext_BACKUP_SCHEDULE):**
Необязательный. Cron-выражение для времени запуска бэкапов. По умолчанию: `0 0 * * *` (ежедневно в полночь).

* **Количество сохраняемых бэкапов (ext_BACKUP_NUM_TO_RETAIN):**
Необязательный. Количество сохраняемых бэкапов. По умолчанию: `7`

* **PgPooler (master / slave):**
Флаги включения PgBouncer для подключений на запись и чтение. При включении создаётся отдельный под.

* **Разрешить подключение без SSL (allowNoSSL):**
По умолчанию подключение с sslmode=require. При `true` разрешается подключение без SSL.

* **Автоскейлинг PV (autoScale):**
Включает автоскейлинг тома. Требует настройки autoScalePercentage, autoScaleTechWindow, autoScaleQuotaGb.

* **Дополнительные параметры PostgreSQL (jsonParameters):**
JSON с переменными конфигурации PostgreSQL. Документация: https://www.postgresql.org/docs/current/runtime-config.html

---
## 3. Выходные параметры

После успешного развертывания ресурса PostgreSQL платформа возвращает ряд параметров.

#### externalConnect

Внешние эндпоинты для подключения к кластеру:
- **master** — для операций записи (host: `write.${namespace}`, port: 5432)
- **slave** — для операций чтения (host: `read.${namespace}`, port: 5432)

Пример ответа платформы:

```json
{
"slave": {
"ip": "",
"fqdn": "",
"uuid": "",
"isExternal": false
},
"master": {
"ip": "<ip>",
"fqdn": "write.<namespace>.<cluster-domain>",
"uuid": "<uuid>",
"isExternal": false
}
}
```

Поля: `ip` — внешний IP; `fqdn` — полное имя хоста; `uuid` — идентификатор; `isExternal` — признак внешнего доступа.

#### internalConnect

Внутренние эндпоинты (FQDN) для подключения из других сервисов внутри той же сети Kubernetes.

Пример ответа платформы:

```json
{
"slave": "",
"master": {
"slave": "",
"master": {
"slave": "",
"master": "postgresqlk8s-master.<namespace>.svc.cluster.local"
}
}
}
```

Для подключения используйте значение `master.master.master` (host для записи) или соответствующее значение slave (host для чтения).

Для подключения используется порт **5432**.

---
## 4. Управление пользователями

### create_user — Создание пользователя

* **Имя пользователя (username):**
Имя создаваемого пользователя. Используется для подключения к базе и настройки прав.
Пример: `myapp_user`

* **Роль (role):**
Тип роли пользователя. Допустимые значения:
- `app_user` — базовые права для DML-операций (SELECT, INSERT, UPDATE, DELETE)
- `ddl_user` — расширенные права: createdb, createrole (управление базами и ролями)

Защищённые имена, которые нельзя использовать: `admin`, `postgres`, `standby`.

### delete_user — Удаление пользователя

* **Имя пользователя (username):**
Имя удаляемого пользователя. Пример: `myapp_user`

Пользователя нельзя удалить, если он является владельцем базы данных. Сначала удалите или передайте владение базами данных другому пользователю.

---
## 5. Управление базами данных

### create_database — Создание базы данных

* **Имя базы данных (dbName):**
Название создаваемой базы данных. Пример: `myapp_db`

* **Владелец базы данных (dbOwner):**
Имя пользователя-владельца базы. Должен существовать в кластере (создан через create_user). Пример: `myapp_user`

### delete_database — Удаление базы данных

* **Имя базы данных (dbName):**
Название удаляемой базы данных. Пример: `myapp_db`
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 19 - kind: instance - action: create - man: руководство - params: - - id: 788 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 105 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 106 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 107 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 104 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 789 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 108 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 790 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 109 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 110 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 111 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 112 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 791 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 113 - code: version - data_type: string - required: true - default: "17" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 116 - code: sslRequired - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 114 - code: poolerMaster - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 115 - code: poolerSlave - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 792 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 50 - is_modifiable: true - sub_params: - - id: 118 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 117 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 793 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - is_modifiable: true - sub_params: - - id: 119 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 120 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 121 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 794 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 125 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 122 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 123 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 124 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 1094 - code: mtlsConfiguration - data_type: map-fixed - required: true - default: '{}' - sort: 80 - is_modifiable: true - sub_params: - - id: 609 - code: type - data_type: string - required: true - default: "off" - value_list: - - "off" - - manual - - auto - man: '`off` - Настройка MTLS производиться не будет
`manual` - После создания кластера в полях данных будет запрос на создание сертификата. **Вам необходимо будет выписать сертификат через вашу CA** и уже после включить через операцию **modify** режим MTLS
`auto` - Сертификаты будут созданы через Cert-Manager

Режимы **auto** и **manual** требует включить настройку `Включить sslMode` - **true**
MTLS в режиме **auto** будет работать сразу, в режиме **manual** только после того как будет предоставлен **CA** и **Сертификат сервера**' - is_modifiable: false - - id: 610 - code: durationCA - data_type: integer > 0 - required: true - default: "175200" - man: Указывается в **часах**
Необходимо настраивать, когда параметр `Режим настройки` - **auto** - is_modifiable: false - - id: 611 - code: durationServer - data_type: integer > 0 - required: true - default: "87600" - man: Указывается в **часах**
Не может быть больше чем время жизни СА
Обновляется **раз в год**, если выставлено значение **более 2 лет**
Обновляется **раз в пол года**, если выставлено значение **менее 2 лет**
Необходимо настраивать, когда параметр `Режим настройки` - **auto** - is_modifiable: false - - id: 612 - code: certCA - data_type: string - required: false - default: "" - is_modifiable: false - - id: 613 - code: certServer - data_type: string - required: false - default: "" - is_modifiable: false - - name: create_backup - id: 264 - kind: subresource - action: create - subresource: backup - params: [] - - name: create_database - id: 245 - kind: subresource - action: create - subresource: database - params: - - id: 741 - code: dbName - data_type: string - required: true - regex: ^[A-Za-z0-9]+$ - - id: 742 - code: dbOwner - data_type: string - required: true - - name: create_user - id: 241 - kind: subresource - action: create - subresource: user - params: - - id: 733 - code: username - data_type: string - required: true - regex: ^[A-Za-z0-9]+$ - maxlength: 62 - minlength: 2 - sort: 10 - - id: 740 - code: role - data_type: string - required: true - value_list: - - app_user - - ddl_user - descr: Выбрать роль из списка - man: '`app_user` — стандартный пользователь с базовым набором прав
`ddl_user` — пользователь с расширенными правами, включая управление ролями и базами данных' - sort: 20 - - id: 1096 - code: mtlsAccess - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Подключение пользователя без ввода паролей
Политика применяется **ко всем базам** внутри Postgresql

RBAC на базы настраивать вручную - sort: 30 - - name: delete - id: 20 - kind: instance - action: delete - params: - - id: 457 - code: deleteS3Bucket - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Также удалить технический S3 Bucket с бэкапами? - sort: 10 - - name: delete_database - id: 246 - kind: subresource - action: delete - subresource: database - params: - - id: 743 - code: dbName - data_type: string - required: true - - name: delete_user - id: 244 - kind: subresource - action: delete - subresource: user - params: - - id: 739 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - name: modify - id: 48 - kind: instance - action: modify - params: - - id: 795 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 128 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 129 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 126 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 127 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 796 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 130 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 131 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 132 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 133 - code: slaveAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 797 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 137 - code: poolerSlave - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 134 - code: version - data_type: string - required: true - default: "" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 135 - code: sslRequired - data_type: boolean - required: true - default: false,true - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 136 - code: poolerMaster - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 798 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 40 - sub_params: - - id: 271 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 272 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 799 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 193 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 138 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 139 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 800 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 140 - code: enabled - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 141 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 142 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 143 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 1095 - code: mtlsConfiguration - data_type: map-fixed - required: true - sort: 70 - sub_params: - - id: 618 - code: certServer - data_type: string - required: false - default: "" - man: Предварительно сертификат можно получить командой
`cat server.crt | base64 -w0` - is_modifiable: false - - id: 614 - code: type - data_type: string - required: true - default: "" - value_list: - - "off" - - manual - - auto - man: '`off` - Настройка MTLS производиться не будет
`manual` - После создания кластера в полях данных будет запрос на создание сертификата. **Вам необходимо будет выписать сертификат через вашу CA** и уже после включить через операцию **modify** режим MTLS
`auto` - Сертификаты будут созданы через Cert-Manager

Режимы **auto** и **manual** требует включить настройку `Включить sslMode` - **true**
MTLS в режиме **auto** будет работать сразу, в режиме **manual** только после того как будет предоставлен **CA** и **Сертификат сервера**' - is_modifiable: false - - id: 615 - code: durationCA - data_type: integer > 0 - required: true - default: "" - man: Указывается в **часах**
Необходимо настраивать, когда параметр `Режим настройки` - **auto** - is_modifiable: false - - id: 616 - code: durationServer - data_type: integer > 0 - required: true - default: "" - man: Указывается в **часах**
Не может быть больше чем время жизни СА
Обновляется **раз в год**, если выставлено значение **более 2 лет**
Обновляется **раз в пол года**, если выставлено значение **менее 2 лет**
Необходимо настраивать, когда параметр `Режим настройки` - **auto** - is_modifiable: false - - id: 617 - code: certCA - data_type: string - required: false - default: "" - man: Предварительно сертификат можно получить командой
`cat ca.crt | base64 -w0` - is_modifiable: false - - name: reconcile - id: 263 - kind: action - action: reconcile - params: [] - - name: recovery - id: 47 - kind: action - action: recovery - params: - - id: 801 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 144 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 166 - code: recoveryPoint - data_type: string - required: true - default: "" - is_modifiable: false - - id: 802 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 146 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 147 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 148 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 145 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 803 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 149 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 150 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 151 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 152 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 804 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 153 - code: version - data_type: string - required: true - default: "17" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 154 - code: sslRequired - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 155 - code: poolerMaster - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 156 - code: poolerSlave - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 805 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 50 - sub_params: - - id: 475 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 476 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 806 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 161 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 159 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 160 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 807 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - sub_params: - - id: 162 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 163 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 164 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 165 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - name: restart - id: 54 - kind: action - action: restart - man: ВНИМАНИЕ - Рестарт предполагает УДАЛЕНИЕ подов.
Может быть полная поломка. Крайне осторожно запускать при работе - params: [] - - name: resume - id: 114 - kind: instance - action: resume - params: [] - - name: suspend - id: 115 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/91_redis.yaml b/generated/dev/resources_yaml/91_redis.yaml deleted file mode 100644 index 8178693..0000000 --- a/generated/dev/resources_yaml/91_redis.yaml +++ /dev/null @@ -1,220 +0,0 @@ -name: redis -service_id: 91 -service_display_name: Redis -service_short_name: redis -service_man: 'Redis кластер. Под кеши.
Ресурсная платформа: k8s.
Автоматизация: k8s operator
port 6379' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 21 - kind: instance - action: create - params: - - id: 1061 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 510 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1062 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 511 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 512 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 513 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 514 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 1063 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 515 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 516 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 517 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 518 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: delete - id: 22 - kind: instance - action: delete - params: [] - - name: modify - id: 394 - kind: instance - action: modify - params: - - id: 1064 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 521 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 522 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 519 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 520 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 1065 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 523 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 524 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 525 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 526 - code: slaveAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: reconcile - id: 391 - kind: action - action: reconcile - params: [] - - name: resume - id: 393 - kind: instance - action: resume - params: [] - - name: suspend - id: 392 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/92_mongodb.yaml b/generated/dev/resources_yaml/92_mongodb.yaml deleted file mode 100644 index 5dd7b23..0000000 --- a/generated/dev/resources_yaml/92_mongodb.yaml +++ /dev/null @@ -1,239 +0,0 @@ -name: mongodb -service_id: 92 -service_display_name: MongoDB -service_short_name: mongodb -service_man: '# Инструкция по развертыванию MongoDB через платформу

## 1. Общая информация

MongoDB — документоориентированная распределённая база данных, обеспечивающая высокую доступность, горизонтальное масштабирование и гибкую модель хранения.

### Доступные операции с кластером

* **create** — создание базы
* **delete** — удаление базы данных
* **suspend** — остановка базы данных
* **resume** — запуск базы данных
* **modify** — изменение базы данных

### Доступные операции с пользователями

* **create_user** — создание пользователя
* **modify_user** — модификация пользователя
* **delete_user** — удаление пользователя

---

## 2. Параметры развертывания базы данных

Эти параметры необходимо указать при создании базы данных.

### Платформа для развертывания

Среда, в которой будет развернут кластер. Выбирается из предложенного списка.

### Количество узлов

Количество реплик в кластере MongoDB.
Оптимально выбирать нечётное значение, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

> **Примечание:** уменьшение количества узлов после создания кластера не рекомендуется.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных в гигабайтах.
Пример: `20`

### Имя ipSpace для публикации внешнего IP

Имя ipSpace, через который будет выделен внешний IP для доступа к master-узлу.
Укажите `no-needed`, если внешний адрес не требуется.
Пример: `internet-no-antiddos-v1`

### Список разрешённых IP-адресов

Список IP-адресов или подсетей, с которых разрешён доступ к кластеру.
Если оставить пустым — доступ открыт для всех источников.
Пример: `["10.0.0.0/24", "192.168.1.10/32"]`

---

## 3. Параметры модификации базы данных

Все параметры, которые вы указываете при модификации, являются необязательными — система автоматически подставит ранее сохранённые значения, и при необходимости вы можете изменить их на нужные.

### Количество узлов

Количество реплик в кластере MongoDB.
Оптимально выбирать нечётное значение, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

> **Примечание:** уменьшение количества узлов после создания кластера не рекомендуется.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных указывается в гигабайтах. Его можно увеличить при необходимости, однако уменьшить уже выделенный размер невозможно.
Пример: `20`

### Имя ipSpace для публикации внешнего IP

Имя ipSpace, через который будет выделен внешний IP для доступа к master-узлу.
Укажите `no-needed`, если внешний адрес не требуется.
Пример: `internet-no-antiddos-v1`

### Список разрешённых IP-адресов

Список IP-адресов или подсетей, с которых разрешён доступ к кластеру.
Если оставить пустым — доступ открыт для всех источников.
Пример: `["10.0.0.0/24", "192.168.1.10/32"]`

---

## 3. Параметры создания пользователя

### Имя пользователя

Указывается имя пользователя (любой регистр).
Пример: `readApp`

### Имя базы данных

Название базы, в которой будет создан пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read** — доступ только на чтение
* **readWrite** — чтение и изменение данных
* **dbOwner** — полный контроль над базой данных

---

## 4. Параметры модификации пользователя

### Имя пользователя

Необходимо выбрать существующего пользователя.
Если список пуст, требуется предварительно создать пользователя.

### Имя базы данных

База данных, к которой относится пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read**
* **readWrite**
* **dbOwner**

---

## 5. Параметры удаления пользователя

### Имя пользователя

Выберите пользователя из списка для удаления.

---

## 6. Выходные параметры

После успешного развертывания базы данных платформа возвращает ключевые параметры.

### internalConnect

Внутренний эндпоинт для подключения к кластеру из сервисов, работающих в той же сети.
Используется для внутренних интеграций и системных сервисов.

### externalConnect

Внешний FQDN для подключения к кластеру извне.
Порт подключения — **27017**.

---

## 7. Пример подключения

Перед подключением необходимо установить **mongosh**.

Команда подключения к базе данных:

```
mongosh "mongodb://<username>:<password>@<endpoint_db>:27017/<dbName>"
```

Где:

* `<username>` — имя пользователя
* `<password>` — пароль
* `<endpoint_db>` — internalConnect или externalConnect
* `<dbName>` — имя базы данных

## 8. Дополнительная информация

Сейчас соединение с базой работает без шифрования, и доступ к кластеру осуществляется только по логину и паролю.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 37 - kind: instance - action: create - params: - - id: 1089 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 594 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - descr: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1090 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 596 - code: memory - data_type: integer > 0 - required: true - default: "1024" - is_modifiable: false - - id: 597 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 598 - code: disk - data_type: integer > 0 - required: true - default: "5" - man: Указывается в Gb - is_modifiable: false - - id: 595 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - is_modifiable: false - - id: 1091 - code: accessConfiguration - data_type: map-fixed - required: true - is_modifiable: true - sub_params: - - id: 600 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 599 - code: masterIpSpace - data_type: string - required: true - default: "" - descr: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - name: create_user - id: 198 - kind: subresource - action: create - subresource: user - params: - - id: 635 - code: username - data_type: string - required: true - - id: 636 - code: dbName - data_type: string - required: true - - id: 643 - code: role - data_type: string - required: true - value_list: - - dbOwner - - read - - readWrite - - name: delete - id: 190 - kind: instance - action: delete - params: [] - - name: delete_user - id: 199 - kind: subresource - action: delete - subresource: user - params: - - id: 644 - code: username - data_type: string - required: true - - name: modify - id: 201 - kind: instance - action: modify - params: - - id: 1092 - code: clusterConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 601 - code: cpu - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 602 - code: memory - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 603 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 604 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в Gb - is_modifiable: false - - id: 1093 - code: accessConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 605 - code: masterIpSpace - data_type: string - required: true - default: "" - descr: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 606 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: modify_user - id: 200 - kind: subresource - action: modify - subresource: user - params: - - id: 640 - code: username - data_type: string - required: true - - id: 641 - code: dbName - data_type: string - required: true - - id: 642 - code: role - data_type: string - required: true - value_list: - - dbOwner - - read - - readWrite - - name: reconcile - id: 409 - kind: action - action: reconcile - params: [] - - name: resume - id: 196 - kind: instance - action: resume - params: [] - - name: suspend - id: 194 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/93_rabbitmq.yaml b/generated/dev/resources_yaml/93_rabbitmq.yaml deleted file mode 100644 index ddebf91..0000000 --- a/generated/dev/resources_yaml/93_rabbitmq.yaml +++ /dev/null @@ -1,168 +0,0 @@ -name: rabbitmq -service_id: 93 -service_display_name: RabbitMQ -service_short_name: rabbitmq -service_man: '# Инструкция по развертыванию RabbitMQ через платформу

---
## 1. Общая информация
RabbitMQ — это брокер сообщений с открытым исходным кодом, который обеспечивает надёжную доставку сообщений между приложениями.
Позволяет надёжно передавать сообщения между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **modify** — Изменение параметров кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса RabbitMQ:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик в кластере RabbitMQ. При значении больше 1 создаётся кластер с репликацией для обеспечения высокой доступности.
Пример: `1` (single node) или `3` (кластер с репликацией)

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных RabbitMQ в гигабайтах. Пример: `50`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-узлу. При включении создаётся Service типа LoadBalancer и настраивается VIP на порту 5672.
Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`


---
## 3. Модификация кластера
Операция **modify** позволяет изменить параметры существующего кластера RabbitMQ.

**Изменяемые параметры:**
* **Квота (megabyte) памяти пода** — можно увеличить или уменьшить
* **Квота (millicore) ядра пода** — можно увеличить или уменьшить
* **Размер (gigabyte) диска для базы данных** — можно только увеличить (уменьшение не поддерживается)
* **Количество узлов в сети** — можно только увеличить (уменьшение не поддерживается)
* **Выделяет внешний IP для доступа к master** — можно включить или выключить
* **Имя ipSpace для публикации внешнего IP master** — можно изменить при включенном внешнем IP

**Ограничения:**
* Уменьшение размера диска не поддерживается
* Уменьшение количества реплик не поддерживается

---
## 4. Выходные параметры
После успешного развертывания ресурса RabbitMQ платформа возвращает ряд параметров в разделе "Поля состояния (сгенерированные значения)":

**clusterDomain**
Домен кластера Kubernetes, в котором развернут RabbitMQ.
Пример: `cluster.local`

**inernalConnect**
Внутренние эндпоинты для подключения к кластеру RabbitMQ из других сервисов, развернутых внутри той же сети.
Содержит:
* **master** — FQDN для подключения к кластеру. Пример: `rabbitmqk8s.{namespace}.svc.{clusterDomain}`

**externalConnect**
Внешние эндпоинты для подключения к кластеру RabbitMQ извне.
Содержит объекты с полями:
* **admin** — доступ к веб-интерфейсу управления:
* `fqdn` — полное доменное имя для веб-интерфейса. Пример: `admin.{namespace}.{domain}`
* `ip` — IP-адрес
* **master** — доступ к AMQP протоколу (если включен внешний IP):
* `fqdn` — полное доменное имя для AMQP. Пример: `write.{namespace}.{domain}`
* `ip` — IP-адрес

**monitoring**
Информация о дашбордах мониторинга:
* **allDashboards** — URL для доступа ко всем дашбордам Grafana

Для подключения используются следующие порты:

* **5672** — AMQP протокол (внутренний и внешний доступ, если включен внешний IP)
* **15672** — веб-интерфейс управления (доступен через Ingress по HTTPS)

**Учётные данные**
Данные для подключения к RabbitMQ доступны в разделе "Учётные данные":
* **adminUser** — имя пользователя администратора
* **adminPass** — пароль администратора


---
## 5. Примеры подключения через консоль (rabbitmqadmin)

### Работа с очередями через rabbitmqadmin

Для работы с RabbitMQ через консоль используется утилита `rabbitmqadmin`. Установите её или используйте образ с предустановленной утилитой.

Задайте переменные окружения с учётными данными:

```bash
export RABBITMQ_USERNAME=<adminUser>
export RABBITMQ_PASSWORD=<adminPass>
export RABBIT_HOST=<host>
```

Где:
* `<adminUser>` и `<adminPass>` — учётные данные из раздела "Учётные данные"
* `<host>` — адрес для подключения:
* Для внешнего подключения: `externalConnect.admin.fqdn` или `externalConnect.master.fqdn`

**Создание очереди:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
declare queue name=healthcheck durable=false
```

**Листинг очередей:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
list queues
```

**Отправка сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
publish exchange=amq.default routing_key=healthcheck payload="ping-$(date +%s)"
```

**Чтение сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
get queue=healthcheck ackmode=ack_requeue_false
```

---
## 6. Дополнительная информация
Для подключения к кластеру RabbitMQ используйте полученные эндпоинты из раздела "Поля состояния (сгенерированные значения)" и учётные данные из раздела "Учётные данные".

Веб-интерфейс управления доступен по адресу, указанному в параметре `externalConnect.admin.fqdn` через HTTPS.

Для внутреннего подключения используйте адрес из `inernalConnect.master` на порту 5672.

---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 38 - kind: instance - action: create - params: - - id: 116 - code: resourceCPU - data_type: integer > 0 - required: true - default: "1000" - maxvalue: 4000 - minvalue: 500 - descr: 'Пример: `2000`' - sort: 20 - is_modifiable: true - - id: 117 - code: resourceMemory - data_type: integer > 0 - required: true - default: "1124" - maxvalue: 8192 - minvalue: 512 - descr: 'Пример: `2048`' - sort: 19 - is_modifiable: true - - id: 118 - code: resourceDisk - data_type: integer > 0 - required: true - default: "30" - maxvalue: 999 - minvalue: 5 - descr: 'Пример: `20`' - sort: 40 - is_modifiable: true - - id: 119 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Выбрать платформу из списка - sort: 10 - - id: 224 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `1`' - sort: 50 - is_modifiable: true - - id: 225 - code: masterAccessList - data_type: json - required: true - default: '[]' - descr: Список IP-адресов, с которых разрешён доступ. Если оставить пустым — доступ открыт всем. - man: 'Передаётся как массив строк. Пример: ["192.168.0.0", "192.168.0.1"]' - sort: 70 - is_modifiable: true - - id: 543 - code: ipSpaceNameMaster - data_type: string - required: true - descr: 'Пример: `internet-no-antiddos-v1`' - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - sort: 60 - depends_on: resourceRealm - is_modifiable: true - - name: delete - id: 39 - kind: instance - action: delete - params: [] - - name: modify - id: 106 - kind: instance - action: modify - params: - - id: 304 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 500 - descr: 'Пример: `2000`' - sort: 10 - - id: 305 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 512 - descr: 'Пример: `2048`' - sort: 20 - - id: 306 - code: resourceDisk - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: 'Пример: `20`' - sort: 10 - - id: 307 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `1`' - sort: 40 - - id: 308 - code: masterAccessList - data_type: json - required: true - descr: Список IP-адресов, с которых разрешён доступ. Если оставить пустым — доступ открыт всем. - man: 'Передаётся как массив строк. Пример: ["192.168.0.0", "192.168.0.1"]' - sort: 60 - - id: 545 - code: ipSpaceNameMaster - data_type: string - required: false - descr: 'Пример: `internet-no-antiddos-v1`' - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - sort: 50 - - name: resume - id: 117 - kind: instance - action: resume - params: [] - - name: suspend - id: 116 - kind: instance - action: suspend - man: Пока не работает из за того что RabbitmqCluster нельзя занулить - params: [] diff --git a/generated/dev/resources_yaml/94_lucee.yaml b/generated/dev/resources_yaml/94_lucee.yaml deleted file mode 100644 index 6507297..0000000 --- a/generated/dev/resources_yaml/94_lucee.yaml +++ /dev/null @@ -1,248 +0,0 @@ -name: lucee -service_id: 94 -service_display_name: Lucee -service_short_name: lucee -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 49 - kind: instance - action: create - params: - - id: 1019 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 430 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1020 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 431 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 432 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 433 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1021 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 434 - code: domain - data_type: string - required: true - default: lucee-kctea8wd - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1022 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 435 - code: version - data_type: string - required: true - default: "6.0" - value_list: - - "5.4" - - "6.0" - is_modifiable: false - - id: 436 - code: gitPath - data_type: string - required: true - default: https://github.com/xahys/testlucee - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 437 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1023 - code: jsonEnv - data_type: map - required: false - default: '{}' - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 50 - is_modifiable: true - - name: delete - id: 50 - kind: instance - action: delete - params: [] - - name: modify - id: 55 - kind: instance - action: modify - params: - - id: 1024 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 438 - code: cpu - data_type: string - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 439 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 440 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1025 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 441 - code: version - data_type: string - required: true - default: "" - value_list: - - "6.0" - - "5.4" - is_modifiable: false - - id: 442 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 443 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1026 - code: jsonEnv - data_type: map - required: false - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 30 - - name: reconcile - id: 373 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 53 - kind: action - action: redeploy - params: - - id: 1027 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 444 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: restart - id: 163 - kind: action - action: restart - params: [] - - name: resume - id: 95 - kind: instance - action: resume - params: [] - - name: suspend - id: 96 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/95_nodejs.yaml b/generated/dev/resources_yaml/95_nodejs.yaml deleted file mode 100644 index 2526e20..0000000 --- a/generated/dev/resources_yaml/95_nodejs.yaml +++ /dev/null @@ -1,248 +0,0 @@ -name: nodejs -service_id: 95 -service_display_name: NodeJS -service_short_name: nodejs -service_man: Развертывает контейнер в k8s кластере с node, который можно использовать для запуска своих приложений.
Приложение должно быть доступно по ссылки в git репозитории, которая является входным параметром. -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 58 - kind: instance - action: create - params: - - id: 1028 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 445 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1029 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 446 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 447 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 448 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1030 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 449 - code: domain - data_type: string - required: true - default: node-bd1l2h8o - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1031 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 450 - code: version - data_type: string - required: true - default: "23" - value_list: - - "18" - - "20" - - "22" - - "23" - is_modifiable: false - - id: 451 - code: gitPath - data_type: string - required: true - default: https://github.com/xahys/testnode.git - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 452 - code: healthPath - data_type: string - required: false - default: "1" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1032 - code: jsonEnv - data_type: map - required: false - default: '{}' - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 50 - is_modifiable: true - - name: delete - id: 61 - kind: instance - action: delete - params: [] - - name: modify - id: 59 - kind: instance - action: modify - params: - - id: 1033 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 453 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 454 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 455 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1034 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 456 - code: version - data_type: string - required: true - default: "" - value_list: - - "18" - - "20" - - "22" - - "23" - is_modifiable: false - - id: 457 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 458 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1035 - code: jsonEnv - data_type: map - required: false - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 30 - - name: reconcile - id: 374 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 60 - kind: action - action: redeploy - params: - - id: 1036 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 459 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: resume - id: 121 - kind: instance - action: resume - params: [] - - name: suspend - id: 120 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/96_pgadmin.yaml b/generated/dev/resources_yaml/96_pgadmin.yaml deleted file mode 100644 index c42a1d2..0000000 --- a/generated/dev/resources_yaml/96_pgadmin.yaml +++ /dev/null @@ -1,164 +0,0 @@ -name: pgadmin -service_id: 96 -service_display_name: pgAdmin -service_short_name: pgadmin -service_man: Инструмент с графическим интерфейсом для управления базами данных PostgreSQL -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 62 - kind: instance - action: create - params: - - id: 164 - code: domain - data_type: string - required: true - default: pgadmin-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 165 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (milicores). 1000Mili = 1 Ядро - man: Квота ядра пода (milicores). 1000Mili = 1 Ядро - sort: 30 - is_modifiable: true - - id: 166 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 512 - descr: Квота памяти пода (MBytes) - man: Квота памяти пода (MBytes) - sort: 40 - is_modifiable: true - - id: 167 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - is_modifiable: true - - id: 169 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 170 - code: login - data_type: string - required: true - regex: ^([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)$ - maxlength: 255 - minlength: 3 - descr: Логин для подключения к pgAdmin, в формате email (login@email.net) - man: Логин для подключения к pgAdmin, в формате email (login@email.net) - sort: 60 - - id: 171 - code: password - data_type: string - required: true - descr: Пароль для подключения к pgAdmin - man: Пароль для подключения к pgAdmin - sort: 70 - is_sensitive: true - - name: delete - id: 63 - kind: instance - action: delete - params: [] - - name: modify - id: 64 - kind: instance - action: modify - params: - - id: 172 - code: resourceCPU - data_type: integer > 0 - required: false - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - - id: 173 - code: resourceMemory - data_type: integer > 0 - required: false - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (MBytes) - man: Квота памяти пода (MBytes) - - id: 174 - code: resourceDisk - data_type: integer > 0 - required: false - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - - name: redeploy - id: 65 - kind: action - action: redeploy - params: - - id: 277 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - - name: resume - id: 123 - kind: instance - action: resume - params: [] - - name: suspend - id: 122 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/97_nodered.yaml b/generated/dev/resources_yaml/97_nodered.yaml deleted file mode 100644 index 445a2bc..0000000 --- a/generated/dev/resources_yaml/97_nodered.yaml +++ /dev/null @@ -1,95 +0,0 @@ -name: nodered -service_id: 97 -service_display_name: NodeRed -service_short_name: nodered -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 72 - kind: instance - action: create - params: - - id: 193 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 30 - - id: 194 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - sort: 40 - - id: 195 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 60 - - id: 196 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 197 - code: domain - data_type: string - required: true - default: nodered-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru - man: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru - sort: 10 - - id: 323 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 50 - - name: delete - id: 73 - kind: instance - action: delete - params: [] diff --git a/generated/dev/resources_yaml/98_http.yaml b/generated/dev/resources_yaml/98_http.yaml deleted file mode 100644 index b9d945e..0000000 --- a/generated/dev/resources_yaml/98_http.yaml +++ /dev/null @@ -1,166 +0,0 @@ -name: http -service_id: 98 -service_display_name: Простой HTTP контейнер -service_short_name: http -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 77 - kind: instance - action: create - params: - - id: 212 - code: domain - data_type: string - required: true - default: http-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 213 - code: resourceCPU - data_type: integer > 0 - required: true - default: "100" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili) - man: Квота ядра пода (Указывается в mili) - sort: 30 - is_modifiable: true - - id: 214 - code: resourceMemory - data_type: integer > 0 - required: true - default: "200" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 40 - is_modifiable: true - - id: 215 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество реплик приложения. Трафик будет распределяться между подами в режиме Round-Robin - sort: 50 - is_modifiable: true - - id: 216 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 217 - code: registryPath - data_type: string - required: true - default: nexus-sa.tst.nubes.ru/docker-nubes/jolt - descr: Путь до image, который будет установлен как deployment - man: Путь до image, который будет установлен как deployment - sort: 60 - - id: 241 - code: jsonEnv - data_type: map - required: false - man: Массив переменных, будут переданы в env приложения - sort: 70 - - id: 782 - code: ingressAnnotations - data_type: map - required: false - default: '{}' - man: Формат ключ/значение
Поддерживает пока простые аннотации - sort: 80 - - name: delete - id: 78 - kind: instance - action: delete - params: [] - - name: modify - id: 81 - kind: instance - action: modify - params: - - id: 218 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 100 - - id: 219 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 128 - - id: 220 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - - name: reconcile - id: 403 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 79 - kind: action - action: redeploy - params: - - id: 272 - code: resourceRealm - data_type: string - required: true - value_list: - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 10 - - name: resume - id: 130 - kind: instance - action: resume - params: [] - - name: suspend - id: 131 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/99_gitea.yaml b/generated/dev/resources_yaml/99_gitea.yaml deleted file mode 100644 index 25b5b95..0000000 --- a/generated/dev/resources_yaml/99_gitea.yaml +++ /dev/null @@ -1,169 +0,0 @@ -name: gitea -service_id: 99 -service_display_name: Gitea -service_short_name: gitea -service_man: '# Инструкция по развертыванию Gitea через платформу

---
## 1. Общая информация
Gitea — это лёгкий, быстрый и самодостаточный сервис хостинга Git-репозиториев с веб-интерфейсом.
Позволяет создавать и управлять репозиториями, организациями, командами, а также настраивать CI/CD через интеграции.
Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание сервиса Gitea
- **delete** — Удаление сервиса Gitea
- **modify** — Модификация сервиса Gitea
- **suspend** — Остановка сервиса Gitea
- **resume** — Запуск сервиса Gitea

---
## 2. Параметры развертывания
Ниже перечислены параметры, которые необходимо указать при создании ресурса Gitea:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развёрнут сервис). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик подов Gitea. Пример: `1`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `1024`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Размер (gigabyte) диска для базы данных:**
Размер PVC для хранения репозиториев и данных Gitea в гигабайтах. Пример: `10`

* **FQDN / Домен:**
Доменное имя, по которому будет доступен сервис. Пример: `git.example.com`

* **Инстанс услуги PostgreSQL:**
UUID услуги k8sPgsql, в которой будет создана база данных `gitea` и пользователь `techgitea`.

* **Дополнительные параметры сервера (iniServerParams):**
JSON-объект с дополнительными параметрами секции `[server]` в `app.ini`. Пример:
```json
{"MAX_CONTENT_LENGTH": "524288000"}
```

* **Дополнительные annotations для Ingress (ingressExtraAnnotations):**
JSON-объект с дополнительными annotations для Ingress-контроллера. Пример:
```json
{"nginx.ingress.kubernetes.io/proxy-body-size": "500m"}
```

---
## 3. Особенности подключения и работы с репозиториями

### 3.1. Доступ только по HTTPS
Сервис развёртывается с **rootless-образом** Gitea. SSH-сервер внутри и он **доступен только внутри кластера Kubernetes**.

**Внешний доступ к репозиториям осуществляется исключительно по HTTPS через Ingress.**

```bash
# Пример клонирования
https://<домен>/<пользователь>/<репозиторий>.git
```

### 3.2. Настройка git для передачи логина и пароля
Для работы с приватными репозиториями через HTTPS необходимо передать учётные данные. Рекомендуется использовать один из следующих способов:

**Встроить креды в URL remote**
```bash
git remote set-url origin https://<логин>:<пароль>@<домен>/<пользователь>/<репозиторий>.git
```
> **Важно:** Логин и пароль администратора создаются автоматически при развертывании и хранятся в Vault.

### 3.3. Работа с большими файлами
По умолчанию `MAX_CONTENT_LENGTH` установлен в **500 МБ**, а Ingress пропускает pack-файлы до **500 МБ**.

Если необходимо запушить файлы близкие к лимиту, увеличьте HTTP-буфер git на стороне клиента:
```bash
git config http.postBuffer 524288000
```

Для файлов **более 500 МБ** рекомендуется использовать **Git LFS**.

---
## 4. Выходные параметры
После успешного развертывания ресурса Gitea платформа возвращает ряд параметров.

Ключевым из них является:

**connectionUrl**
Это внешний URL для доступа к веб-интерфейсу и Git-репозиториям по HTTPS. Пример:
```
https://git.example.com
```

**internalConnect**
Внутренний эндпоинт для подключения к PostgreSQL из других сервисов внутри кластера.

---
## 5. Дополнительная информация
- База данных `gitea` и пользователь `techgitea` создаются автоматически в указанной услуге PostgreSQL.
- DNS-запись и TLS-сертификат (Let''s Encrypt) настраиваются автоматически на основе указанного домена.
- Для изменения параметров сервера Gitea (`app.ini`) используйте операцию **modify** с полем `iniServerParams`.
- Для добавления дополнительных Ingress-annotations используйте операцию **modify** с полем `ingressExtraAnnotations`.
---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 82 - kind: instance - action: create - params: - - id: 232 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 300 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 40 - is_modifiable: true - - id: 233 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 256 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 50 - is_modifiable: true - - id: 234 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 60 - is_modifiable: true - - id: 235 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 70 - is_modifiable: true - - id: 237 - code: domain - data_type: string - required: true - default: gitea-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 20 - - id: 238 - code: psqlUid - data_type: uuid - required: true - ref_svc_id: 90 - descr: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически - man: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически - sort: 10 - - id: 818 - code: ingressExtraAnnotations - data_type: map - required: false - is_modifiable: true - - id: 821 - code: iniServerParams - data_type: map - required: false - is_modifiable: true - - name: delete - id: 83 - kind: instance - action: delete - params: [] - - name: modify - id: 91 - kind: instance - action: modify - params: - - id: 259 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 10 - - id: 260 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 256 - descr: Квота памяти пода (Указывается в Megabytes) - sort: 20 - - id: 261 - code: resourceDisk - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 30 - - id: 262 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 40 - - id: 819 - code: ingressExtraAnnotations - data_type: map - required: false - man: 'Примеры:
- `nginx.ingress.kubernetes.io/proxy-body-size: 500m` (Размер пакета)
- `nginx.ingress.kubernetes.io/proxy-read-timeout: "600"` (Время ожидания при чтении)
- `nginx.ingress.kubernetes.io/proxy-send-timeout: "600"` (Время ожидания отправке)' - - id: 820 - code: iniServerParams - data_type: map - required: false - man: 'Примеры:
- `MAX_CONTENT_LENGTH: 524288000(~500мб)` (Размер пакета в байтах)' - - name: resume - id: 127 - kind: instance - action: resume - params: [] - - name: suspend - id: 126 - kind: instance - action: suspend - params: [] diff --git a/generated/dev/resources_yaml/embed.go b/generated/dev/resources_yaml/embed.go deleted file mode 100644 index 72e3ff2..0000000 --- a/generated/dev/resources_yaml/embed.go +++ /dev/null @@ -1,8 +0,0 @@ -package resources_yaml - -import "embed" - -// Files embeds unified service YAML specs for provider runtime lookup. -// -//go:embed *.yaml -var Files embed.FS diff --git a/generated/prod/go/100_openwhisk_resource.go b/generated/prod/go/100_openwhisk_resource.go deleted file mode 100644 index fa8337a..0000000 --- a/generated/prod/go/100_openwhisk_resource.go +++ /dev/null @@ -1,355 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: openwhisk -// Service ID: 100 - -var _ resource.Resource = &OpenwhiskResource{} -var _ resource.ResourceWithModifyPlan = &OpenwhiskResource{} -var _ resource.ResourceWithImportState = &OpenwhiskResource{} - -type OpenwhiskResource struct { - client *core.UniversalClient -} - -type OpenwhiskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Username types.String `tfsdk:"username"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewOpenwhiskResource() resource.Resource { - return &OpenwhiskResource{} -} - -func (r *OpenwhiskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_openwhisk" -} - -func (r *OpenwhiskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Имя пользователя Openwhisk Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *OpenwhiskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *OpenwhiskModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Username.IsNull() && !plan.Username.IsUnknown() && !state.Username.IsNull() && !state.Username.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Username.ValueString(), state.Username.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить username", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Username.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - if config.Username.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 64: resources_core.FormatString(config.Username), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 100, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *OpenwhiskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Username.IsNull() || data.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 64: resources_core.FormatString(data.Username), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 100, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *OpenwhiskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 100, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *OpenwhiskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan OpenwhiskModel - var state OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 100, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "username", Field: "Username", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *OpenwhiskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *OpenwhiskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *OpenwhiskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/109_zones_v2_record_resource.go b/generated/prod/go/109_zones_v2_record_resource.go deleted file mode 100644 index b875e09..0000000 --- a/generated/prod/go/109_zones_v2_record_resource.go +++ /dev/null @@ -1,366 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: zones_v2 -// Subresource: record - -var _ resource.Resource = &ZonesV2RecordResource{} - -// ZonesV2RecordResource manages a subresource via operations. -type ZonesV2RecordResource struct { - client *core.UniversalClient -} - -type ZonesV2RecordModel struct { - ID types.String `tfsdk:"id"` - ZonesV2ID types.String `tfsdk:"zones_v2_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - RecordInput types.String `tfsdk:"record_input"` - RecordName types.String `tfsdk:"record_name"` - RecordTTL types.Int64 `tfsdk:"record_t_t_l"` - RecordType types.String `tfsdk:"record_type"` -} - -func NewZonesV2RecordResource() resource.Resource { - return &ZonesV2RecordResource{} -} - -func (r *ZonesV2RecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_zones_v2_record" -} - -func (r *ZonesV2RecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "zones_v2_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "record_input": schema.StringAttribute{Required: true, MarkdownDescription: "Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции"}, - "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны в конце проставляется автоматически Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена"}, - "record_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("A")}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ZonesV2RecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ZonesV2RecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.RecordInput.IsNull() || plan.RecordInput.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if plan.RecordName.IsNull() || plan.RecordName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.ZonesV2ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - } - identityCodes := []string{ - "recordName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("record", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - "recordTTL": resources_core.FormatInt64(plan.RecordTTL), - "recordType": resources_core.FormatString(plan.RecordType), - "recordInput": resources_core.FormatString(plan.RecordInput), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_record", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_record", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ZonesV2RecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2RecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ZonesV2RecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_record" == "" { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ZonesV2ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - "recordTTL": resources_core.FormatInt64(plan.RecordTTL), - "recordType": resources_core.FormatString(plan.RecordType), - "recordInput": resources_core.FormatString(plan.RecordInput), - }) - - idParams := map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_record", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ZonesV2RecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_record" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ZonesV2ID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "recordName": resources_core.FormatString(state.RecordName), - } - identityCodes := []string{ - "recordName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("record", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(state.RecordName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_record", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ZonesV2RecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/109_zones_v2_resource.go b/generated/prod/go/109_zones_v2_resource.go deleted file mode 100644 index e15f4b2..0000000 --- a/generated/prod/go/109_zones_v2_resource.go +++ /dev/null @@ -1,484 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: zones_v2 -// Service ID: 109 - -var _ resource.Resource = &ZonesV2Resource{} -var _ resource.ResourceWithModifyPlan = &ZonesV2Resource{} -var _ resource.ResourceWithImportState = &ZonesV2Resource{} - -type ZonesV2Resource struct { - client *core.UniversalClient -} - -type ZonesV2Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Records types.String `tfsdk:"records"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ZoneExpire types.Int64 `tfsdk:"zone_expire"` - ZoneKind types.String `tfsdk:"zone_kind"` - ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` - ZoneName types.String `tfsdk:"zone_name"` - ZoneRefresh types.Int64 `tfsdk:"zone_refresh"` - ZoneRetry types.Int64 `tfsdk:"zone_retry"` - ZoneTTL types.Int64 `tfsdk:"zone_t_t_l"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewZonesV2Resource() resource.Resource { - return &ZonesV2Resource{} -} - -func (r *ZonesV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_zones_v2" -} - -func (r *ZonesV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "records": schema.StringAttribute{Required: true}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("powerdns"), MarkdownDescription: "Не изменяется после создания."}, - "zone_expire": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600000), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, - "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, - "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, - "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, - "zone_refresh": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, - "zone_retry": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7200), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, - "zone_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ZonesV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ZonesV2Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneExpire.IsNull() && !plan.ZoneExpire.IsUnknown() && !state.ZoneExpire.IsNull() && !state.ZoneExpire.IsUnknown() { - if plan.ZoneExpire.ValueInt64() != state.ZoneExpire.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_expire", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneKind.ValueString(), state.ZoneKind.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneMastersForSlave.ValueString(), state.ZoneMastersForSlave.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneName.ValueString(), state.ZoneName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneRefresh.IsNull() && !plan.ZoneRefresh.IsUnknown() && !state.ZoneRefresh.IsNull() && !state.ZoneRefresh.IsUnknown() { - if plan.ZoneRefresh.ValueInt64() != state.ZoneRefresh.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_refresh", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneRetry.IsNull() && !plan.ZoneRetry.IsUnknown() && !state.ZoneRetry.IsNull() && !state.ZoneRetry.IsUnknown() { - if plan.ZoneRetry.ValueInt64() != state.ZoneRetry.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_retry", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTTL.IsNull() && !plan.ZoneTTL.IsUnknown() && !state.ZoneTTL.IsNull() && !state.ZoneTTL.IsUnknown() { - if plan.ZoneTTL.ValueInt64() != state.ZoneTTL.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_t_t_l", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Records.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "records is required.") - return - } - if config.Records.IsUnknown() { - return - } - if config.ZoneName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - if config.ZoneName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 761: resources_core.FormatString(config.ZoneName), - 762: resources_core.FormatString(config.ZoneKind), - 763: resources_core.FormatString(config.ZoneMastersForSlave), - 764: resources_core.FormatInt64(config.ZoneRefresh), - 765: resources_core.FormatInt64(config.ZoneRetry), - 766: resources_core.FormatInt64(config.ZoneExpire), - 767: resources_core.FormatInt64(config.ZoneTTL), - 768: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 109, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ZonesV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Records.IsNull() || data.Records.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "records is required.") - return - } - if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 761: resources_core.FormatString(data.ZoneName), - 762: resources_core.FormatString(data.ZoneKind), - 763: resources_core.FormatString(data.ZoneMastersForSlave), - 764: resources_core.FormatInt64(data.ZoneRefresh), - 765: resources_core.FormatInt64(data.ZoneRetry), - 766: resources_core.FormatInt64(data.ZoneExpire), - 767: resources_core.FormatInt64(data.ZoneTTL), - 768: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 109, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 109, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ZonesV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ZonesV2Model - var state ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.Records.IsNull() != state.Records.IsNull() || plan.Records.IsUnknown() != state.Records.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Records.IsNull() && !plan.Records.IsUnknown() && !state.Records.IsNull() && !state.Records.IsUnknown() { - if !strings.EqualFold(plan.Records.ValueString(), state.Records.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 769: resources_core.FormatString(plan.Records), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 109, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ZonesV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ZonesV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/110_dnszone_resource.go b/generated/prod/go/110_dnszone_resource.go deleted file mode 100644 index fc3170b..0000000 --- a/generated/prod/go/110_dnszone_resource.go +++ /dev/null @@ -1,495 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnszone -// Service ID: 110 - -var _ resource.Resource = &DnszoneResource{} -var _ resource.ResourceWithModifyPlan = &DnszoneResource{} -var _ resource.ResourceWithImportState = &DnszoneResource{} - -type DnszoneResource struct { - client *core.UniversalClient -} - -type DnszoneModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ZoneEmail types.String `tfsdk:"zone_email"` - ZoneKind types.String `tfsdk:"zone_kind"` - ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` - ZoneName types.String `tfsdk:"zone_name"` - ZoneTimeToExpire types.String `tfsdk:"zone_time_to_expire"` - ZoneTimeToLive types.String `tfsdk:"zone_time_to_live"` - ZoneTimeToRefresh types.String `tfsdk:"zone_time_to_refresh"` - ZoneTimeToRetry types.String `tfsdk:"zone_time_to_retry"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDnszoneResource() resource.Resource { - return &DnszoneResource{} -} - -func (r *DnszoneResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnszone" -} - -func (r *DnszoneResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("powerdns"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "zone_email": schema.StringAttribute{Required: true, MarkdownDescription: "Контактный e-mail Не изменяется после создания."}, - "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, - "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, - "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, - "zone_time_to_expire": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3600000"), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, - "zone_time_to_live": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, - "zone_time_to_refresh": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, - "zone_time_to_retry": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("7200"), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DnszoneResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DnszoneModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneEmail.IsNull() && !plan.ZoneEmail.IsUnknown() && !state.ZoneEmail.IsNull() && !state.ZoneEmail.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneEmail.ValueString(), state.ZoneEmail.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_email", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneKind.ValueString(), state.ZoneKind.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneMastersForSlave.ValueString(), state.ZoneMastersForSlave.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneName.ValueString(), state.ZoneName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToExpire.IsNull() && !plan.ZoneTimeToExpire.IsUnknown() && !state.ZoneTimeToExpire.IsNull() && !state.ZoneTimeToExpire.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToExpire.ValueString(), state.ZoneTimeToExpire.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_expire", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToLive.IsNull() && !plan.ZoneTimeToLive.IsUnknown() && !state.ZoneTimeToLive.IsNull() && !state.ZoneTimeToLive.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToLive.ValueString(), state.ZoneTimeToLive.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_live", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToRefresh.IsNull() && !plan.ZoneTimeToRefresh.IsUnknown() && !state.ZoneTimeToRefresh.IsNull() && !state.ZoneTimeToRefresh.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToRefresh.ValueString(), state.ZoneTimeToRefresh.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_refresh", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToRetry.IsNull() && !plan.ZoneTimeToRetry.IsUnknown() && !state.ZoneTimeToRetry.IsNull() && !state.ZoneTimeToRetry.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToRetry.ValueString(), state.ZoneTimeToRetry.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_retry", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ZoneEmail.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") - return - } - if config.ZoneEmail.IsUnknown() { - return - } - if config.ZoneName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - if config.ZoneName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 66: resources_core.FormatString(config.ZoneEmail), - 67: resources_core.FormatString(config.ZoneName), - 68: resources_core.FormatString(config.ZoneKind), - 69: resources_core.FormatString(config.ZoneMastersForSlave), - 70: resources_core.FormatString(config.ZoneTimeToRefresh), - 71: resources_core.FormatString(config.ZoneTimeToRetry), - 72: resources_core.FormatString(config.ZoneTimeToExpire), - 73: resources_core.FormatString(config.ZoneTimeToLive), - 284: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 110, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DnszoneResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ZoneEmail.IsNull() || data.ZoneEmail.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") - return - } - if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 66: resources_core.FormatString(data.ZoneEmail), - 67: resources_core.FormatString(data.ZoneName), - 68: resources_core.FormatString(data.ZoneKind), - 69: resources_core.FormatString(data.ZoneMastersForSlave), - 70: resources_core.FormatString(data.ZoneTimeToRefresh), - 71: resources_core.FormatString(data.ZoneTimeToRetry), - 72: resources_core.FormatString(data.ZoneTimeToExpire), - 73: resources_core.FormatString(data.ZoneTimeToLive), - 284: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 110, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnszoneResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 110, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DnszoneResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnszoneModel - var state DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 110, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnszoneResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DnszoneResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DnszoneResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/111_dnsrecord_resource.go b/generated/prod/go/111_dnsrecord_resource.go deleted file mode 100644 index e5bea0b..0000000 --- a/generated/prod/go/111_dnsrecord_resource.go +++ /dev/null @@ -1,546 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnsrecord -// Service ID: 111 - -var _ resource.Resource = &DnsrecordResource{} -var _ resource.ResourceWithModifyPlan = &DnsrecordResource{} -var _ resource.ResourceWithImportState = &DnsrecordResource{} - -type DnsrecordResource struct { - client *core.UniversalClient -} - -type DnsrecordModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - RecordInput types.String `tfsdk:"record_input"` - RecordName types.String `tfsdk:"record_name"` - RecordTTL types.Int64 `tfsdk:"record_t_t_l"` - RecordType types.String `tfsdk:"record_type"` - ZoneUid types.String `tfsdk:"zone_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDnsrecordResource() resource.Resource { - return &DnsrecordResource{} -} - -func (r *DnsrecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnsrecord" -} - -func (r *DnsrecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "record_input": schema.StringAttribute{Required: true, MarkdownDescription: "Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP"}, - "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя записи. Имя зоны в конце проставляется автоматически Не изменяется после создания."}, - "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена Не изменяется после создания."}, - "record_type": schema.StringAttribute{Required: true, MarkdownDescription: "Тип DNS-записи Не изменяется после создания."}, - "zone_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID экземпляра услуги DNS-зоны Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DnsrecordResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DnsrecordModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.RecordName.IsNull() && !plan.RecordName.IsUnknown() && !state.RecordName.IsNull() && !state.RecordName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RecordName.ValueString(), state.RecordName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить record_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RecordTTL.IsNull() && !plan.RecordTTL.IsUnknown() && !state.RecordTTL.IsNull() && !state.RecordTTL.IsUnknown() { - if plan.RecordTTL.ValueInt64() != state.RecordTTL.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить record_t_t_l", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RecordType.IsNull() && !plan.RecordType.IsUnknown() && !state.RecordType.IsNull() && !state.RecordType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RecordType.ValueString(), state.RecordType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить record_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneUid.ValueString(), state.ZoneUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.RecordInput.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if config.RecordInput.IsUnknown() { - return - } - if config.RecordName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - if config.RecordName.IsUnknown() { - return - } - if config.RecordType.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") - return - } - if config.RecordType.IsUnknown() { - return - } - if config.ZoneUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") - return - } - if config.ZoneUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.ZoneUid.IsNull() && !config.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, config.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != config.ZoneUid.ValueString() { - config.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - params := map[int]string{ - 75: resources_core.FormatString(config.ZoneUid), - 76: resources_core.FormatString(config.RecordType), - 77: resources_core.FormatString(config.RecordName), - 78: resources_core.FormatString(config.RecordInput), - 450: resources_core.FormatInt64(config.RecordTTL), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 111, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DnsrecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.RecordInput.IsNull() || data.RecordInput.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if data.RecordName.IsNull() || data.RecordName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - if data.RecordType.IsNull() || data.RecordType.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") - return - } - if data.ZoneUid.IsNull() || data.ZoneUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalZoneUid := data.ZoneUid - if !data.ZoneUid.IsNull() && !data.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, data.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedZoneUid != "" && resolvedZoneUid != data.ZoneUid.ValueString() { - data.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 75: resources_core.FormatString(data.ZoneUid), - 76: resources_core.FormatString(data.RecordType), - 77: resources_core.FormatString(data.RecordName), - 78: resources_core.FormatString(data.RecordInput), - 450: resources_core.FormatInt64(data.RecordTTL), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 111, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, state.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { - state.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalZoneUid.IsNull() && !originalZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - if strings.EqualFold(state.ZoneUid.ValueString(), originalZoneUid.ValueString()) { - state.ZoneUid = originalZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnsrecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 111, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, newState.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != newState.ZoneUid.ValueString() { - newState.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() && !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { - if strings.EqualFold(newState.ZoneUid.ValueString(), state.ZoneUid.ValueString()) { - newState.ZoneUid = state.ZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DnsrecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnsrecordModel - var state DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.RecordInput.IsNull() != state.RecordInput.IsNull() || plan.RecordInput.IsUnknown() != state.RecordInput.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.RecordInput.IsNull() && !plan.RecordInput.IsUnknown() && !state.RecordInput.IsNull() && !state.RecordInput.IsUnknown() { - if !strings.EqualFold(plan.RecordInput.ValueString(), state.RecordInput.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 256: resources_core.FormatString(plan.RecordInput), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 111, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, state.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { - state.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - if strings.EqualFold(state.ZoneUid.ValueString(), plan.ZoneUid.ValueString()) { - state.ZoneUid = plan.ZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnsrecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DnsrecordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DnsrecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/112_tenant_resource.go b/generated/prod/go/112_tenant_resource.go deleted file mode 100644 index ec395dd..0000000 --- a/generated/prod/go/112_tenant_resource.go +++ /dev/null @@ -1,372 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: tenant -// Service ID: 112 - -var _ resource.Resource = &TenantResource{} -var _ resource.ResourceWithModifyPlan = &TenantResource{} -var _ resource.ResourceWithImportState = &TenantResource{} - -type TenantResource struct { - client *core.UniversalClient -} - -type TenantModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - GrafanaOrgName types.String `tfsdk:"grafana_org_name"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewTenantResource() resource.Resource { - return &TenantResource{} -} - -func (r *TenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_tenant" -} - -func (r *TenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "grafana_org_name": schema.StringAttribute{Required: true, MarkdownDescription: "WZ-Код клиента Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("grafana.ngcloud.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *TenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *TenantModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.GrafanaOrgName.IsNull() && !plan.GrafanaOrgName.IsUnknown() && !state.GrafanaOrgName.IsNull() && !state.GrafanaOrgName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.GrafanaOrgName.ValueString(), state.GrafanaOrgName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить grafana_org_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.GrafanaOrgName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") - return - } - if config.GrafanaOrgName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 239: resources_core.FormatString(config.GrafanaOrgName), - 240: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 112, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.GrafanaOrgName.IsNull() || data.GrafanaOrgName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 239: resources_core.FormatString(data.GrafanaOrgName), - 240: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 112, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 112, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *TenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TenantModel - var state TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 112, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *TenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *TenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/113_vc_complex_resource.go b/generated/prod/go/113_vc_complex_resource.go deleted file mode 100644 index 69d834d..0000000 --- a/generated/prod/go/113_vc_complex_resource.go +++ /dev/null @@ -1,563 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_complex -// Service ID: 113 - -var _ resource.Resource = &VcComplexResource{} -var _ resource.ResourceWithModifyPlan = &VcComplexResource{} -var _ resource.ResourceWithImportState = &VcComplexResource{} - -type VcComplexResource struct { - client *core.UniversalClient -} - -type VcComplexModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - CpuAllocated types.String `tfsdk:"cpu_allocated"` - CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - MemAllocated types.Int64 `tfsdk:"mem_allocated"` - MemGuaranteed types.Int64 `tfsdk:"mem_guaranteed"` - NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` - NeedExternalAddressSNAT types.Bool `tfsdk:"need_external_address_s_n_a_t"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SegroupName types.String `tfsdk:"segroup_name"` - StorageConfig types.String `tfsdk:"storage_config"` - VIPCount types.Int64 `tfsdk:"v_i_p_count"` - VappName types.String `tfsdk:"vapp_name"` - VdcNetworkPool types.String `tfsdk:"vdc_network_pool"` - VdcProviderGateway types.String `tfsdk:"vdc_provider_gateway"` - VirtualServicesCount types.String `tfsdk:"virtual_services_count"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcComplexResource() resource.Resource { - return &VcComplexResource{} -} - -func (r *VcComplexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_complex" -} - -func (r *VcComplexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "cpu_allocated": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "шт Не изменяется после создания."}, - "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("internet-ipv4-v1"), MarkdownDescription: "Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT Не изменяется после создания."}, - "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "gb Не изменяется после создания."}, - "mem_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, - "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает AVI в Cloud Director Не изменяется после создания."}, - "need_external_address_s_n_a_t": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP на Edge. Используется для SNAT всего трафика Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ngcloud.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "segroup_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("SEGROUP-SANDBOX-CL1-SHARED-01"), MarkdownDescription: "Наименование segroup, в котором будут выделяться пулы на AVI Не изменяется после создания."}, - "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{\"name\": \"\", \"size\": 1024}]"), MarkdownDescription: "Описывается как массив объектов, где описывается имя политики и её размер Не изменяется после создания.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "v_i_p_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "ш Не изменяется после создания."}, - "vapp_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя vApp Не изменяется после создания."}, - "vdc_network_pool": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nsxt-sandbox-geneve-np"), MarkdownDescription: "Не изменяется после создания."}, - "vdc_provider_gateway": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox-v1cl1-pvdc"), MarkdownDescription: "Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String Не изменяется после создания."}, - "virtual_services_count": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcComplexResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcComplexModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.CpuAllocated.ValueString(), state.CpuAllocated.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить cpu_allocated", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { - if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { - if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить mem_allocated", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MemGuaranteed.IsNull() && !plan.MemGuaranteed.IsUnknown() && !state.MemGuaranteed.IsNull() && !state.MemGuaranteed.IsUnknown() { - if plan.MemGuaranteed.ValueInt64() != state.MemGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить mem_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { - if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить need_enable_a_v_i", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NeedExternalAddressSNAT.IsNull() && !plan.NeedExternalAddressSNAT.IsUnknown() && !state.NeedExternalAddressSNAT.IsNull() && !state.NeedExternalAddressSNAT.IsUnknown() { - if plan.NeedExternalAddressSNAT.ValueBool() != state.NeedExternalAddressSNAT.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить need_external_address_s_n_a_t", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.SegroupName.IsNull() && !plan.SegroupName.IsUnknown() && !state.SegroupName.IsNull() && !state.SegroupName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.SegroupName.ValueString(), state.SegroupName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить segroup_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.StorageConfig.ValueString(), state.StorageConfig.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить storage_config", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VIPCount.IsNull() && !plan.VIPCount.IsUnknown() && !state.VIPCount.IsNull() && !state.VIPCount.IsUnknown() { - if plan.VIPCount.ValueInt64() != state.VIPCount.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить v_i_p_count", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappName.ValueString(), state.VappName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcNetworkPool.IsNull() && !plan.VdcNetworkPool.IsUnknown() && !state.VdcNetworkPool.IsNull() && !state.VdcNetworkPool.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcNetworkPool.ValueString(), state.VdcNetworkPool.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_network_pool", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcProviderGateway.IsNull() && !plan.VdcProviderGateway.IsUnknown() && !state.VdcProviderGateway.IsNull() && !state.VdcProviderGateway.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcProviderGateway.ValueString(), state.VdcProviderGateway.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_provider_gateway", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VirtualServicesCount.ValueString(), state.VirtualServicesCount.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить virtual_services_count", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VappName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") - return - } - if config.VappName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 332: resources_core.FormatString(config.ResourceRealm), - 334: resources_core.FormatString(config.VappName), - 358: resources_core.FormatString(config.VdcProviderGateway), - 359: resources_core.FormatString(config.VirtualServicesCount), - 360: resources_core.FormatString(config.IpSpaceName), - 399: resources_core.FormatString(config.StorageConfig), - 400: resources_core.FormatString(config.VdcNetworkPool), - 401: resources_core.FormatInt64(config.CpuGuaranteed), - 402: resources_core.FormatInt64(config.MemGuaranteed), - 403: resources_core.FormatString(config.SegroupName), - 404: resources_core.FormatBool(config.NeedExternalAddressSNAT), - 405: resources_core.FormatBool(config.NeedEnableAVI), - 566: resources_core.FormatString(config.CpuAllocated), - 567: resources_core.FormatInt64(config.MemAllocated), - 568: resources_core.FormatInt64(config.VIPCount), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 113, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcComplexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VappName.IsNull() || data.VappName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 332: resources_core.FormatString(data.ResourceRealm), - 334: resources_core.FormatString(data.VappName), - 358: resources_core.FormatString(data.VdcProviderGateway), - 359: resources_core.FormatString(data.VirtualServicesCount), - 360: resources_core.FormatString(data.IpSpaceName), - 399: resources_core.FormatString(data.StorageConfig), - 400: resources_core.FormatString(data.VdcNetworkPool), - 401: resources_core.FormatInt64(data.CpuGuaranteed), - 402: resources_core.FormatInt64(data.MemGuaranteed), - 403: resources_core.FormatString(data.SegroupName), - 404: resources_core.FormatBool(data.NeedExternalAddressSNAT), - 405: resources_core.FormatBool(data.NeedEnableAVI), - 566: resources_core.FormatString(data.CpuAllocated), - 567: resources_core.FormatInt64(data.MemAllocated), - 568: resources_core.FormatInt64(data.VIPCount), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 113, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcComplexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 113, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcComplexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcComplexModel - var state VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 113, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcComplexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcComplexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcComplexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/114_gitea_complex_resource.go b/generated/prod/go/114_gitea_complex_resource.go deleted file mode 100644 index f6c0b7c..0000000 --- a/generated/prod/go/114_gitea_complex_resource.go +++ /dev/null @@ -1,322 +0,0 @@ -package resources_gen - -import ( - "context" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: gitea_complex -// Service ID: 114 - -var _ resource.Resource = &GiteaComplexResource{} -var _ resource.ResourceWithModifyPlan = &GiteaComplexResource{} -var _ resource.ResourceWithImportState = &GiteaComplexResource{} - -type GiteaComplexResource struct { - client *core.UniversalClient -} - -type GiteaComplexModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewGiteaComplexResource() resource.Resource { - return &GiteaComplexResource{} -} - -func (r *GiteaComplexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_gitea_complex" -} - -func (r *GiteaComplexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *GiteaComplexResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *GiteaComplexModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *GiteaComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan GiteaComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{} - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 114, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *GiteaComplexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data GiteaComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 114, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{} - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 114, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 114, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaComplexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state GiteaComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 114, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *GiteaComplexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan GiteaComplexModel - var state GiteaComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 114, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaComplexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state GiteaComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *GiteaComplexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *GiteaComplexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/115_mariadb_database_resource.go b/generated/prod/go/115_mariadb_database_resource.go deleted file mode 100644 index 8ca1ed8..0000000 --- a/generated/prod/go/115_mariadb_database_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Subresource: database - -var _ resource.Resource = &MariadbDatabaseResource{} - -// MariadbDatabaseResource manages a subresource via operations. -type MariadbDatabaseResource struct { - client *core.UniversalClient -} - -type MariadbDatabaseModel struct { - ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` -} - -func NewMariadbDatabaseResource() resource.Resource { - return &MariadbDatabaseResource{} -} - -func (r *MariadbDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_database" -} - -func (r *MariadbDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mariadb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("db-zs0q9wuv"), MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MariadbDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/115_mariadb_resource.go b/generated/prod/go/115_mariadb_resource.go deleted file mode 100644 index 4e4044f..0000000 --- a/generated/prod/go/115_mariadb_resource.go +++ /dev/null @@ -1,447 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Service ID: 115 - -var _ resource.Resource = &MariadbResource{} -var _ resource.ResourceWithModifyPlan = &MariadbResource{} -var _ resource.ResourceWithImportState = &MariadbResource{} - -type MariadbResource struct { - client *core.UniversalClient -} - -// MariadbAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type MariadbAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// MariadbAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type MariadbAutoscaleConfigurationModel struct { - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` -} - -// MariadbBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type MariadbBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// MariadbClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type MariadbClusterConfigurationModel struct { - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// MariadbMariadbConfigurationModel — вложенная модель для map-fixed параметра mariadbConfiguration. -type MariadbMariadbConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -// MariadbStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type MariadbStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type MariadbModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *MariadbAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *MariadbAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *MariadbBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *MariadbClusterConfigurationModel `tfsdk:"cluster_configuration"` - MariadbConfiguration *MariadbMariadbConfigurationModel `tfsdk:"mariadb_configuration"` - StartupConfiguration *MariadbStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewMariadbResource() resource.Resource { - return &MariadbResource{} -} - -func (r *MariadbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb" -} - -func (r *MariadbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(720)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать"}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - }}, - "mariadb_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("9.4.0")}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *MariadbModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 877: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 878: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64())}), - 879: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - 880: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.MariadbConfiguration.Version.ValueString())}), - 881: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 882: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 115, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *MariadbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 877: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 878: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64())}), - 879: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - 880: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.MariadbConfiguration.Version.ValueString())}), - 881: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 882: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 115, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 115, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *MariadbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbModel - var state MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 883: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 884: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - 885: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.MariadbConfiguration.Version.ValueString())}), - 886: resources_core.BuildJSON(map[string]string{"retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString()), "s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString())}), - 887: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 115, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *MariadbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *MariadbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/115_mariadb_user_resource.go b/generated/prod/go/115_mariadb_user_resource.go deleted file mode 100644 index d1e6619..0000000 --- a/generated/prod/go/115_mariadb_user_resource.go +++ /dev/null @@ -1,367 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Subresource: user - -var _ resource.Resource = &MariadbUserResource{} - -// MariadbUserResource manages a subresource via operations. -type MariadbUserResource struct { - client *core.UniversalClient -} - -type MariadbUserModel struct { - ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - DbName types.String `tfsdk:"db_name"` - Password types.String `tfsdk:"password"` - Username types.String `tfsdk:"username"` -} - -func NewMariadbUserResource() resource.Resource { - return &MariadbUserResource{} -} - -func (r *MariadbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_user" -} - -func (r *MariadbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mariadb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("%"), MarkdownDescription: "Пример: `%` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо указать массив существующих баз Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "password": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abpwcb75"), MarkdownDescription: "Секрет будет записан в хранилище секретов и не будет отображаться публично Не изменяется после создания. Изменение требует пересоздания.", Sensitive: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("usertigstlyt"), MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "password": resources_core.FormatString(plan.Password), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "password", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "password": resources_core.FormatString(plan.Password), - "dbName": resources_core.FormatString(plan.DbName), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "password": resources_core.FormatString(plan.Password), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(state.AccessHosts), - "dbName": resources_core.FormatString(state.DbName), - "password": resources_core.FormatString(state.Password), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "password", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MariadbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/116_kafka_resource.go b/generated/prod/go/116_kafka_resource.go deleted file mode 100644 index 7ed0fa5..0000000 --- a/generated/prod/go/116_kafka_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Service ID: 116 - -var _ resource.Resource = &KafkaResource{} -var _ resource.ResourceWithModifyPlan = &KafkaResource{} -var _ resource.ResourceWithImportState = &KafkaResource{} - -type KafkaResource struct { - client *core.UniversalClient -} - -// KafkaAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type KafkaAccessConfigurationModel struct { - AllocationExternal types.Bool `tfsdk:"allocation_external" json:"allocationExternal"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// KafkaClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type KafkaClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// KafkaStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type KafkaStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type KafkaModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *KafkaAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *KafkaClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *KafkaStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewKafkaResource() resource.Resource { - return &KafkaResource{} -} - -func (r *KafkaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka" -} - -func (r *KafkaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "allocation_external": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048)}, - "replicas": schema.Int64Attribute{Required: true}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *KafkaModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1084: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1085: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1086: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", config.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 116, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *KafkaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1084: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1085: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1086: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", data.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 116, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 116, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *KafkaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaModel - var state KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1087: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1088: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", plan.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 116, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *KafkaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *KafkaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/116_kafka_topic_resource.go b/generated/prod/go/116_kafka_topic_resource.go deleted file mode 100644 index 3f289b3..0000000 --- a/generated/prod/go/116_kafka_topic_resource.go +++ /dev/null @@ -1,364 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Subresource: topic - -var _ resource.Resource = &KafkaTopicResource{} - -// KafkaTopicResource manages a subresource via operations. -type KafkaTopicResource struct { - client *core.UniversalClient -} - -type KafkaTopicModel struct { - ID types.String `tfsdk:"id"` - KafkaID types.String `tfsdk:"kafka_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - NameTopic types.String `tfsdk:"name_topic"` - Partitions types.Int64 `tfsdk:"partitions"` - Replicas types.Int64 `tfsdk:"replicas"` -} - -func NewKafkaTopicResource() resource.Resource { - return &KafkaTopicResource{} -} - -func (r *KafkaTopicResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka_topic" -} - -func (r *KafkaTopicResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "kafka_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций можно увеличить, но уменьшить их нельзя."}, - "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество реплик не может привышать количество реплик брокера"}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaTopicResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan KafkaTopicModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if plan.Partitions.IsNull() || plan.Partitions.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") - return - } - if plan.Replicas.IsNull() || plan.Replicas.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - } - identityCodes := []string{ - "nameTopic", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "partitions": resources_core.FormatInt64(plan.Partitions), - "replicas": resources_core.FormatInt64(plan.Replicas), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaTopicResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaTopicResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaTopicModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_topic" == "" { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "partitions": resources_core.FormatInt64(plan.Partitions), - "replicas": resources_core.FormatInt64(plan.Replicas), - }) - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_topic", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaTopicResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_topic" == "" { - return - } - - instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(state.NameTopic), - } - identityCodes := []string{ - "nameTopic", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(state.NameTopic), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_topic", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *KafkaTopicResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/116_kafka_user_resource.go b/generated/prod/go/116_kafka_user_resource.go deleted file mode 100644 index 52ea73c..0000000 --- a/generated/prod/go/116_kafka_user_resource.go +++ /dev/null @@ -1,373 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Subresource: user - -var _ resource.Resource = &KafkaUserResource{} - -// KafkaUserResource manages a subresource via operations. -type KafkaUserResource struct { - client *core.UniversalClient -} - -type KafkaUserModel struct { - ID types.String `tfsdk:"id"` - KafkaID types.String `tfsdk:"kafka_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - Group types.String `tfsdk:"group"` - NameTopic types.String `tfsdk:"name_topic"` - Operations types.String `tfsdk:"operations"` - Username types.String `tfsdk:"username"` -} - -func NewKafkaUserResource() resource.Resource { - return &KafkaUserResource{} -} - -func (r *KafkaUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka_user" -} - -func (r *KafkaUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "kafka_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("*"), MarkdownDescription: "Пример: `192.168.1.0/24` или `*`"}, - "group": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("*"), MarkdownDescription: "Пример: `order-consumers` или `*`"}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`"}, - "operations": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `Create, Describe, Read, Write`"}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `username-admin` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan KafkaUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if plan.Operations.IsNull() || plan.Operations.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "operations is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "nameTopic": resources_core.FormatString(plan.NameTopic), - "operations": resources_core.FormatString(plan.Operations), - "group": resources_core.FormatString(plan.Group), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_user" == "" { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "operations": resources_core.FormatString(plan.Operations), - "group": resources_core.FormatString(plan.Group), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "username": resources_core.FormatString(plan.Username), - }) - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *KafkaUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/117_nifi_resource.go b/generated/prod/go/117_nifi_resource.go deleted file mode 100644 index 77f570f..0000000 --- a/generated/prod/go/117_nifi_resource.go +++ /dev/null @@ -1,533 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nifi -// Service ID: 117 - -var _ resource.Resource = &NifiResource{} -var _ resource.ResourceWithModifyPlan = &NifiResource{} -var _ resource.ResourceWithImportState = &NifiResource{} - -type NifiResource struct { - client *core.UniversalClient -} - -type NifiModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KafkaUid types.String `tfsdk:"kafka_uid"` - NameTopic types.String `tfsdk:"name_topic"` - Partitions types.Int64 `tfsdk:"partitions"` - Replicas types.Int64 `tfsdk:"replicas"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNifiResource() resource.Resource { - return &NifiResource{} -} - -func (r *NifiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nifi" -} - -func (r *NifiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "kafka_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Выбрать кластер из списка Не изменяется после создания."}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Не изменяется после создания."}, - "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50`"}, - "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество партиций ≤ (число брокеров). Пример `2`"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NifiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NifiModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить kafka_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NameTopic.IsNull() && !plan.NameTopic.IsUnknown() && !state.NameTopic.IsNull() && !state.NameTopic.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NameTopic.ValueString(), state.NameTopic.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить name_topic", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.KafkaUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if config.KafkaUid.IsUnknown() { - return - } - if config.NameTopic.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if config.NameTopic.IsUnknown() { - return - } - if config.Partitions.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") - return - } - if config.Partitions.IsUnknown() { - return - } - if config.Replicas.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") - return - } - if config.Replicas.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { - config.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - params := map[int]string{ - 471: resources_core.FormatString(config.KafkaUid), - 472: resources_core.FormatInt64(config.Partitions), - 473: resources_core.FormatInt64(config.Replicas), - 474: resources_core.FormatString(config.NameTopic), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 117, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NifiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if data.NameTopic.IsNull() || data.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if data.Partitions.IsNull() || data.Partitions.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") - return - } - if data.Replicas.IsNull() || data.Replicas.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalKafkaUid := data.KafkaUid - if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { - data.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 117, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 471: resources_core.FormatString(data.KafkaUid), - 472: resources_core.FormatInt64(data.Partitions), - 473: resources_core.FormatInt64(data.Replicas), - 474: resources_core.FormatString(data.NameTopic), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 117, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 117, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "nameTopic", Field: "NameTopic", Type: "string"}, - {Code: "partitions", Field: "Partitions", Type: "int64"}, - {Code: "replicas", Field: "Replicas", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalKafkaUid.IsNull() && !originalKafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), originalKafkaUid.ValueString()) { - state.KafkaUid = originalKafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NifiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 117, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "nameTopic", Field: "NameTopic", Type: "string"}, - {Code: "partitions", Field: "Partitions", Type: "int64"}, - {Code: "replicas", Field: "Replicas", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { - newState.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() && !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - if strings.EqualFold(newState.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - newState.KafkaUid = state.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NifiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NifiModel - var state NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.Partitions.IsNull() != state.Partitions.IsNull() || plan.Partitions.IsUnknown() != state.Partitions.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Partitions.IsNull() && !plan.Partitions.IsUnknown() && !state.Partitions.IsNull() && !state.Partitions.IsUnknown() { - if plan.Partitions.ValueInt64() != state.Partitions.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.Replicas.IsNull() != state.Replicas.IsNull() || plan.Replicas.IsUnknown() != state.Replicas.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Replicas.IsNull() && !plan.Replicas.IsUnknown() && !state.Replicas.IsNull() && !state.Replicas.IsUnknown() { - if plan.Replicas.ValueInt64() != state.Replicas.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 477: resources_core.FormatInt64(plan.Partitions), - 478: resources_core.FormatInt64(plan.Replicas), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 117, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "nameTopic", Field: "NameTopic", Type: "string"}, - {Code: "partitions", Field: "Partitions", Type: "int64"}, - {Code: "replicas", Field: "Replicas", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), plan.KafkaUid.ValueString()) { - state.KafkaUid = plan.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NifiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NifiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NifiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/119_akhq_resource.go b/generated/prod/go/119_akhq_resource.go deleted file mode 100644 index d7e3c02..0000000 --- a/generated/prod/go/119_akhq_resource.go +++ /dev/null @@ -1,544 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: akhq -// Service ID: 119 - -var _ resource.Resource = &AkhqResource{} -var _ resource.ResourceWithModifyPlan = &AkhqResource{} -var _ resource.ResourceWithImportState = &AkhqResource{} - -type AkhqResource struct { - client *core.UniversalClient -} - -type AkhqModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - KafkaUid types.String `tfsdk:"kafka_uid"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewAkhqResource() resource.Resource { - return &AkhqResource{} -} - -func (r *AkhqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_akhq" -} - -func (r *AkhqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "kafka_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Выбрать кластер из списка"}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `1000` Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `1024` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *AkhqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *AkhqModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Domain.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if config.Domain.IsUnknown() { - return - } - if config.KafkaUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if config.KafkaUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { - config.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - params := map[int]string{ - 550: resources_core.FormatString(config.ResourceRealm), - 551: resources_core.FormatInt64(config.ResourceMemory), - 552: resources_core.FormatInt64(config.ResourceCPU), - 553: resources_core.FormatString(config.Domain), - 554: resources_core.FormatInt64(config.ResourceInstances), - 555: resources_core.FormatString(config.KafkaUid), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 119, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *AkhqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Domain.IsNull() || data.Domain.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalKafkaUid := data.KafkaUid - if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { - data.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 550: resources_core.FormatString(data.ResourceRealm), - 551: resources_core.FormatInt64(data.ResourceMemory), - 552: resources_core.FormatInt64(data.ResourceCPU), - 553: resources_core.FormatString(data.Domain), - 554: resources_core.FormatInt64(data.ResourceInstances), - 555: resources_core.FormatString(data.KafkaUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 119, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalKafkaUid.IsNull() && !originalKafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), originalKafkaUid.ValueString()) { - state.KafkaUid = originalKafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *AkhqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 119, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { - newState.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() && !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - if strings.EqualFold(newState.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - newState.KafkaUid = state.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *AkhqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan AkhqModel - var state AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.KafkaUid.IsNull() != state.KafkaUid.IsNull() || plan.KafkaUid.IsUnknown() != state.KafkaUid.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if !strings.EqualFold(plan.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 565: resources_core.FormatString(plan.KafkaUid), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 119, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), plan.KafkaUid.ValueString()) { - state.KafkaUid = plan.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *AkhqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *AkhqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *AkhqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/120_clickhouse_database_resource.go b/generated/prod/go/120_clickhouse_database_resource.go deleted file mode 100644 index cc5c42e..0000000 --- a/generated/prod/go/120_clickhouse_database_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Subresource: database - -var _ resource.Resource = &ClickhouseDatabaseResource{} - -// ClickhouseDatabaseResource manages a subresource via operations. -type ClickhouseDatabaseResource struct { - client *core.UniversalClient -} - -type ClickhouseDatabaseModel struct { - ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` -} - -func NewClickhouseDatabaseResource() resource.Resource { - return &ClickhouseDatabaseResource{} -} - -func (r *ClickhouseDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_database" -} - -func (r *ClickhouseDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "clickhouse_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("db01"), MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ClickhouseDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/120_clickhouse_resource.go b/generated/prod/go/120_clickhouse_resource.go deleted file mode 100644 index 53dcba4..0000000 --- a/generated/prod/go/120_clickhouse_resource.go +++ /dev/null @@ -1,487 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Service ID: 120 - -var _ resource.Resource = &ClickhouseResource{} -var _ resource.ResourceWithModifyPlan = &ClickhouseResource{} -var _ resource.ResourceWithImportState = &ClickhouseResource{} - -type ClickhouseResource struct { - client *core.UniversalClient -} - -// ClickhouseAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type ClickhouseAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// ClickhouseAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type ClickhouseAutoscaleConfigurationModel struct { - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` -} - -// ClickhouseBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type ClickhouseBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// ClickhouseClickhouseConfigurationModel — вложенная модель для map-fixed параметра clickhouseConfiguration. -type ClickhouseClickhouseConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -// ClickhouseClickhouseKeeperConfigurationModel — вложенная модель для map-fixed параметра clickhouseKeeperConfiguration. -type ClickhouseClickhouseKeeperConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// ClickhouseClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type ClickhouseClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Shards types.Int64 `tfsdk:"shards" json:"shards"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// ClickhouseStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type ClickhouseStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type ClickhouseModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *ClickhouseAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *ClickhouseAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *ClickhouseBackupConfigurationModel `tfsdk:"backup_configuration"` - ClickhouseConfiguration *ClickhouseClickhouseConfigurationModel `tfsdk:"clickhouse_configuration"` - ClickhouseFilesConfiguration types.String `tfsdk:"clickhouse_files_configuration"` - ClickhouseKeeperConfiguration *ClickhouseClickhouseKeeperConfigurationModel `tfsdk:"clickhouse_keeper_configuration"` - ClusterConfiguration *ClickhouseClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *ClickhouseStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewClickhouseResource() resource.Resource { - return &ClickhouseResource{} -} - -func (r *ClickhouseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse" -} - -func (r *ClickhouseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "clickhouse_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("25.10")}, - }}, - "clickhouse_files_configuration": schema.StringAttribute{Optional: true, MarkdownDescription: "Формат:
- path: somethings3.xml
xmlConfig: '<clickhouse>......</clickhouse>'"}, - "clickhouse_keeper_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда"}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ClickhouseModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 837: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 838: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", config.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 839: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - 840: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 841: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.ClickhouseConfiguration.Version.ValueString())}), - 842: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 843: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64())}), - 851: resources_core.FormatString(config.ClickhouseFilesConfiguration), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 120, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ClickhouseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 837: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 838: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", data.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 839: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - 840: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 841: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.ClickhouseConfiguration.Version.ValueString())}), - 842: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 843: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64())}), - 851: resources_core.FormatString(data.ClickhouseFilesConfiguration), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 120, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 120, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ClickhouseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseModel - var state ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ClickhouseFilesConfiguration.IsNull() != state.ClickhouseFilesConfiguration.IsNull() || plan.ClickhouseFilesConfiguration.IsUnknown() != state.ClickhouseFilesConfiguration.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ClickhouseFilesConfiguration.IsNull() && !plan.ClickhouseFilesConfiguration.IsUnknown() && !state.ClickhouseFilesConfiguration.IsNull() && !state.ClickhouseFilesConfiguration.IsUnknown() { - if !strings.EqualFold(plan.ClickhouseFilesConfiguration.ValueString(), state.ClickhouseFilesConfiguration.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 844: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", plan.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 845: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 846: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - 847: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.ClickhouseConfiguration.Version.ValueString())}), - 848: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 849: resources_core.BuildJSON(map[string]string{"quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64())}), - 850: resources_core.FormatString(plan.ClickhouseFilesConfiguration), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 120, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ClickhouseResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ClickhouseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/120_clickhouse_user_resource.go b/generated/prod/go/120_clickhouse_user_resource.go deleted file mode 100644 index 8dc28f7..0000000 --- a/generated/prod/go/120_clickhouse_user_resource.go +++ /dev/null @@ -1,359 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Subresource: user - -var _ resource.Resource = &ClickhouseUserResource{} - -// ClickhouseUserResource manages a subresource via operations. -type ClickhouseUserResource struct { - client *core.UniversalClient -} - -type ClickhouseUserModel struct { - ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - DbName types.String `tfsdk:"db_name"` - Username types.String `tfsdk:"username"` -} - -func NewClickhouseUserResource() resource.Resource { - return &ClickhouseUserResource{} -} - -func (r *ClickhouseUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_user" -} - -func (r *ClickhouseUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "clickhouse_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("::/0"), MarkdownDescription: "Пример: `::/0` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myusername"), MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(state.AccessHosts), - "dbName": resources_core.FormatString(state.DbName), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ClickhouseUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/12_s3_resource.go b/generated/prod/go/12_s3_resource.go deleted file mode 100644 index 6347765..0000000 --- a/generated/prod/go/12_s3_resource.go +++ /dev/null @@ -1,421 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 -// Service ID: 12 - -var _ resource.Resource = &S3Resource{} -var _ resource.ResourceWithModifyPlan = &S3Resource{} -var _ resource.ResourceWithImportState = &S3Resource{} - -type S3Resource struct { - client *core.UniversalClient -} - -type S3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DisplayName types.String `tfsdk:"display_name"` - MaxBucketsPerUser types.Int64 `tfsdk:"max_buckets_per_user"` - MaxObjectsPerBucket types.Int64 `tfsdk:"max_objects_per_bucket"` - MaxSizeGbPerUser types.Int64 `tfsdk:"max_size_gb_per_user"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewS3Resource() resource.Resource { - return &S3Resource{} -} - -func (r *S3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3" -} - -func (r *S3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "display_name": schema.StringAttribute{Required: true, MarkdownDescription: "Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента Не изменяется после создания."}, - "max_buckets_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на пользователя. Количество бакетов пользователя. Передаём руками"}, - "max_objects_per_bucket": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на бакет. Кол-во объектов. Передаём руками"}, - "max_size_gb_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "maxSizeGb. Передаём руками"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("s3.msk-1.ngcloud.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *S3Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.DisplayName.IsNull() && !plan.DisplayName.IsUnknown() && !state.DisplayName.IsNull() && !state.DisplayName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.DisplayName.ValueString(), state.DisplayName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить display_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.DisplayName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") - return - } - if config.DisplayName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 163: resources_core.FormatString(config.ResourceRealm), - 320: resources_core.FormatString(config.DisplayName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 12, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *S3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.DisplayName.IsNull() || data.DisplayName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 163: resources_core.FormatString(data.ResourceRealm), - 320: resources_core.FormatString(data.DisplayName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 12, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 12, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *S3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3Model - var state S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.MaxSizeGbPerUser.IsNull() != state.MaxSizeGbPerUser.IsNull() || plan.MaxSizeGbPerUser.IsUnknown() != state.MaxSizeGbPerUser.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxSizeGbPerUser.IsNull() && !plan.MaxSizeGbPerUser.IsUnknown() && !state.MaxSizeGbPerUser.IsNull() && !state.MaxSizeGbPerUser.IsUnknown() { - if plan.MaxSizeGbPerUser.ValueInt64() != state.MaxSizeGbPerUser.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MaxObjectsPerBucket.IsNull() != state.MaxObjectsPerBucket.IsNull() || plan.MaxObjectsPerBucket.IsUnknown() != state.MaxObjectsPerBucket.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxObjectsPerBucket.IsNull() && !plan.MaxObjectsPerBucket.IsUnknown() && !state.MaxObjectsPerBucket.IsNull() && !state.MaxObjectsPerBucket.IsUnknown() { - if plan.MaxObjectsPerBucket.ValueInt64() != state.MaxObjectsPerBucket.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MaxBucketsPerUser.IsNull() != state.MaxBucketsPerUser.IsNull() || plan.MaxBucketsPerUser.IsUnknown() != state.MaxBucketsPerUser.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxBucketsPerUser.IsNull() && !plan.MaxBucketsPerUser.IsUnknown() && !state.MaxBucketsPerUser.IsNull() && !state.MaxBucketsPerUser.IsUnknown() { - if plan.MaxBucketsPerUser.ValueInt64() != state.MaxBucketsPerUser.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), - 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), - 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 12, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *S3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *S3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/12_s3_sub_user_resource.go b/generated/prod/go/12_s3_sub_user_resource.go deleted file mode 100644 index b8e0168..0000000 --- a/generated/prod/go/12_s3_sub_user_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 -// Subresource: sub_user - -var _ resource.Resource = &S3SubUserResource{} - -// S3SubUserResource manages a subresource via operations. -type S3SubUserResource struct { - client *core.UniversalClient -} - -type S3SubUserModel struct { - ID types.String `tfsdk:"id"` - S3ID types.String `tfsdk:"s3_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SubUserAccess types.String `tfsdk:"sub_user_access"` - SubUserName types.String `tfsdk:"sub_user_name"` -} - -func NewS3SubUserResource() resource.Resource { - return &S3SubUserResource{} -} - -func (r *S3SubUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3_sub_user" -} - -func (r *S3SubUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "s3_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "sub_user_access": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "sub_user_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3SubUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan S3SubUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.SubUserAccess.IsNull() || plan.SubUserAccess.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "sub_user_access is required.") - return - } - if plan.SubUserName.IsNull() || plan.SubUserName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "sub_user_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - "subUserName": resources_core.FormatString(plan.SubUserName), - } - identityCodes := []string{ - "subUserAccess", - "subUserName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "subUserName": resources_core.FormatString(plan.SubUserName), - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *S3SubUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3SubUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3SubUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - "subUserName": resources_core.FormatString(plan.SubUserName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *S3SubUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_sub_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.S3ID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(state.SubUserAccess), - "subUserName": resources_core.FormatString(state.SubUserName), - } - identityCodes := []string{ - "subUserAccess", - "subUserName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "subUserName": resources_core.FormatString(state.SubUserName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_sub_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *S3SubUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/13_s3bucket_resource.go b/generated/prod/go/13_s3bucket_resource.go deleted file mode 100644 index c45905c..0000000 --- a/generated/prod/go/13_s3bucket_resource.go +++ /dev/null @@ -1,478 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3bucket -// Service ID: 13 - -var _ resource.Resource = &S3bucketResource{} -var _ resource.ResourceWithModifyPlan = &S3bucketResource{} -var _ resource.ResourceWithImportState = &S3bucketResource{} - -type S3bucketResource struct { - client *core.UniversalClient -} - -type S3bucketModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - BucketName types.String `tfsdk:"bucket_name"` - CorsAll types.Bool `tfsdk:"cors_all"` - ListAll types.Bool `tfsdk:"list_all"` - MaxSize types.String `tfsdk:"max_size"` - Placement types.String `tfsdk:"placement"` - ReadAll types.Bool `tfsdk:"read_all"` - S3UserUid types.String `tfsdk:"s3_user_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewS3bucketResource() resource.Resource { - return &S3bucketResource{} -} - -func (r *S3bucketResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3bucket" -} - -func (r *S3bucketResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "bucket_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя бакета s3 Не изменяется после создания."}, - "cors_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS Не изменяется после создания."}, - "list_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. Не изменяется после создания."}, - "max_size": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("-1"), MarkdownDescription: "Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes Не изменяется после создания."}, - "placement": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("HOT"), MarkdownDescription: "Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения Не изменяется после создания."}, - "read_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. Не изменяется после создания."}, - "s3_user_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UID Корневой услуги S3. На основе него будет создан бакет. Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3bucketResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *S3bucketModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.BucketName.IsNull() && !plan.BucketName.IsUnknown() && !state.BucketName.IsNull() && !state.BucketName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.BucketName.ValueString(), state.BucketName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить bucket_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.CorsAll.IsNull() && !plan.CorsAll.IsUnknown() && !state.CorsAll.IsNull() && !state.CorsAll.IsUnknown() { - if plan.CorsAll.ValueBool() != state.CorsAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить cors_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ListAll.IsNull() && !plan.ListAll.IsUnknown() && !state.ListAll.IsNull() && !state.ListAll.IsUnknown() { - if plan.ListAll.ValueBool() != state.ListAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить list_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MaxSize.IsNull() && !plan.MaxSize.IsUnknown() && !state.MaxSize.IsNull() && !state.MaxSize.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.MaxSize.ValueString(), state.MaxSize.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить max_size", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Placement.IsNull() && !plan.Placement.IsUnknown() && !state.Placement.IsNull() && !state.Placement.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Placement.ValueString(), state.Placement.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить placement", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ReadAll.IsNull() && !plan.ReadAll.IsUnknown() && !state.ReadAll.IsNull() && !state.ReadAll.IsUnknown() { - if plan.ReadAll.ValueBool() != state.ReadAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить read_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() && !state.S3UserUid.IsNull() && !state.S3UserUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.S3UserUid.ValueString(), state.S3UserUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить s3_user_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.BucketName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") - return - } - if config.BucketName.IsUnknown() { - return - } - if config.S3UserUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") - return - } - if config.S3UserUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.S3UserUid.IsNull() && !config.S3UserUid.IsUnknown() { - resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3UserUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) - } else if resolvedS3UserUid != "" && resolvedS3UserUid != config.S3UserUid.ValueString() { - config.S3UserUid = types.StringValue(resolvedS3UserUid) - } - } - params := map[int]string{ - 124: resources_core.FormatString(config.S3UserUid), - 125: resources_core.FormatString(config.BucketName), - 126: resources_core.FormatString(config.MaxSize), - 127: resources_core.FormatBool(config.ReadAll), - 128: resources_core.FormatBool(config.ListAll), - 129: resources_core.FormatBool(config.CorsAll), - 130: resources_core.FormatString(config.Placement), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 13, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *S3bucketResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.BucketName.IsNull() || data.BucketName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") - return - } - if data.S3UserUid.IsNull() || data.S3UserUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 124: resources_core.FormatString(data.S3UserUid), - 125: resources_core.FormatString(data.BucketName), - 126: resources_core.FormatString(data.MaxSize), - 127: resources_core.FormatBool(data.ReadAll), - 128: resources_core.FormatBool(data.ListAll), - 129: resources_core.FormatBool(data.CorsAll), - 130: resources_core.FormatString(data.Placement), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 13, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3bucketResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 13, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *S3bucketResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3bucketModel - var state S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 13, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *S3bucketResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *S3bucketResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/148_vc_mgmt_sthutrval_cluster_resource.go b/generated/prod/go/148_vc_mgmt_sthutrval_cluster_resource.go deleted file mode 100644 index 92f4d9f..0000000 --- a/generated/prod/go/148_vc_mgmt_sthutrval_cluster_resource.go +++ /dev/null @@ -1,419 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_mgmt_sthutrval_cluster -// Service ID: 148 - -var _ resource.Resource = &VcMgmtSthutrvalClusterResource{} -var _ resource.ResourceWithModifyPlan = &VcMgmtSthutrvalClusterResource{} -var _ resource.ResourceWithImportState = &VcMgmtSthutrvalClusterResource{} - -type VcMgmtSthutrvalClusterResource struct { - client *core.UniversalClient -} - -// VcMgmtSthutrvalClusterAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type VcMgmtSthutrvalClusterAccessConfigurationModel struct { - NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i" json:"needExternalAddressAPI"` - AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i" json:"accessIpListAPI"` - NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress" json:"needExternalAddressIngress"` - AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress" json:"accessIpListIngress"` -} - -// VcMgmtSthutrvalClusterClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type VcMgmtSthutrvalClusterClusterConfigurationModel struct { - AppVersion types.String `tfsdk:"app_version" json:"appVersion"` -} - -// VcMgmtSthutrvalClusterControlPlaneConfigurationModel — вложенная модель для map-fixed параметра controlPlaneConfiguration. -type VcMgmtSthutrvalClusterControlPlaneConfigurationModel struct { - Count types.Int64 `tfsdk:"count" json:"count"` - SizingPolicy types.String `tfsdk:"sizing_policy" json:"sizingPolicy"` - SizingDisk types.Int64 `tfsdk:"sizing_disk" json:"sizingDisk"` -} - -// VcMgmtSthutrvalClusterStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type VcMgmtSthutrvalClusterStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` - ClusterName types.String `tfsdk:"cluster_name" json:"clusterName"` -} - -type VcMgmtSthutrvalClusterModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *VcMgmtSthutrvalClusterAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *VcMgmtSthutrvalClusterClusterConfigurationModel `tfsdk:"cluster_configuration"` - ControlPlaneConfiguration *VcMgmtSthutrvalClusterControlPlaneConfigurationModel `tfsdk:"control_plane_configuration"` - StartupConfiguration *VcMgmtSthutrvalClusterStartupConfigurationModel `tfsdk:"startup_configuration"` - WorkerConfiguration types.String `tfsdk:"worker_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcMgmtSthutrvalClusterResource() resource.Resource { - return &VcMgmtSthutrvalClusterResource{} -} - -func (r *VcMgmtSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_mgmt_sthutrval_cluster" -} - -func (r *VcMgmtSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false"}, - "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**"}, - "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress"}, - "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.13.1")}, - }}, - "control_plane_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "sizing_policy": schema.StringAttribute{Required: true, MarkdownDescription: "Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана"}, - "sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ngcloud.ru"), MarkdownDescription: "Облако в котором будет развёрнут тенант"}, - "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example-ewwnrebe")}, - }}, - "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcMgmtSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.WorkerConfiguration.ValueString(), state.WorkerConfiguration.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить worker_configuration", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 771: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ClusterName.ValueString())}), - 772: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.AppVersion.ValueString())}), - 773: resources_core.BuildJSON(map[string]string{"count": fmt.Sprintf("%d", config.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", config.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", config.ControlPlaneConfiguration.SizingDisk.ValueInt64())}), - 774: resources_core.FormatString(config.WorkerConfiguration), - 775: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListIngress.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 148, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 148, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 771: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ClusterName.ValueString())}), - 772: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.AppVersion.ValueString())}), - 773: resources_core.BuildJSON(map[string]string{"count": fmt.Sprintf("%d", data.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", data.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", data.ControlPlaneConfiguration.SizingDisk.ValueInt64())}), - 774: resources_core.FormatString(data.WorkerConfiguration), - 775: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListIngress.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 148, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 148, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 148, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcMgmtSthutrvalClusterModel - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 148, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcMgmtSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcMgmtSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/149_valo_tenant_resource.go b/generated/prod/go/149_valo_tenant_resource.go deleted file mode 100644 index 37670d5..0000000 --- a/generated/prod/go/149_valo_tenant_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: valo_tenant -// Service ID: 149 - -var _ resource.Resource = &ValoTenantResource{} -var _ resource.ResourceWithModifyPlan = &ValoTenantResource{} -var _ resource.ResourceWithImportState = &ValoTenantResource{} - -type ValoTenantResource struct { - client *core.UniversalClient -} - -// ValoTenantValoOptionsModel — вложенная модель для map-fixed параметра valoOptions. -type ValoTenantValoOptionsModel struct { - FfUserImport types.Bool `tfsdk:"ff_user_import" json:"ffUserImport"` - FfThemes types.Bool `tfsdk:"ff_themes" json:"ffThemes"` - FfPublicLink types.Bool `tfsdk:"ff_public_link" json:"ffPublicLink"` - FfLogging types.Bool `tfsdk:"ff_logging" json:"ffLogging"` - UserCount types.Int64 `tfsdk:"user_count" json:"userCount"` - RequestApproveMaxPeriod types.Int64 `tfsdk:"request_approve_max_period" json:"requestApproveMaxPeriod"` - FfOnlineEditor types.Bool `tfsdk:"ff_online_editor" json:"ffOnlineEditor"` - FfWatermark types.Bool `tfsdk:"ff_watermark" json:"ffWatermark"` -} - -type ValoTenantModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceRealm types.String `tfsdk:"resource_realm"` - S3Storage types.Int64 `tfsdk:"s3_storage"` - ValoOptions *ValoTenantValoOptionsModel `tfsdk:"valo_options"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewValoTenantResource() resource.Resource { - return &ValoTenantResource{} -} - -func (r *ValoTenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_valo_tenant" -} - -func (r *ValoTenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Будет добавлено в зону valo.ngcloud.ru Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("valo.ngcloud.ru"), MarkdownDescription: "Не изменяется после создания."}, - "s3_storage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Параметр указывается в `Gb`"}, - "valo_options": schema.SingleNestedAttribute{Optional: true, Computed: true, - Attributes: map[string]schema.Attribute{ - "ff_user_import": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_themes": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_public_link": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_logging": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "user_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10)}, - "request_approve_max_period": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2), MarkdownDescription: "Максимальное значение периода согласования (в днях)"}, - "ff_online_editor": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_watermark": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ValoTenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ValoTenantModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Domain.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if config.Domain.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 655: resources_core.FormatString(config.ResourceRealm), - 656: resources_core.FormatString(config.Domain), - 657: resources_core.BuildJSON(map[string]string{"ffUserImport": fmt.Sprintf("%v", config.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", config.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", config.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", config.ValoOptions.FfLogging.ValueBool()), "userCount": fmt.Sprintf("%d", config.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", config.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", config.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", config.ValoOptions.FfWatermark.ValueBool())}), - 658: resources_core.FormatInt64(config.S3Storage), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 149, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ValoTenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Domain.IsNull() || data.Domain.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 655: resources_core.FormatString(data.ResourceRealm), - 656: resources_core.FormatString(data.Domain), - 657: resources_core.BuildJSON(map[string]string{"ffUserImport": fmt.Sprintf("%v", data.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", data.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", data.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", data.ValoOptions.FfLogging.ValueBool()), "userCount": fmt.Sprintf("%d", data.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", data.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", data.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", data.ValoOptions.FfWatermark.ValueBool())}), - 658: resources_core.FormatInt64(data.S3Storage), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 149, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ValoTenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 149, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ValoTenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ValoTenantModel - var state ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.S3Storage.IsNull() != state.S3Storage.IsNull() || plan.S3Storage.IsUnknown() != state.S3Storage.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.S3Storage.IsNull() && !plan.S3Storage.IsUnknown() && !state.S3Storage.IsNull() && !state.S3Storage.IsUnknown() { - if plan.S3Storage.ValueInt64() != state.S3Storage.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 659: resources_core.BuildJSON(map[string]string{"ffPublicLink": fmt.Sprintf("%v", plan.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", plan.ValoOptions.FfLogging.ValueBool()), "userCount": fmt.Sprintf("%d", plan.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", plan.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", plan.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", plan.ValoOptions.FfWatermark.ValueBool()), "ffUserImport": fmt.Sprintf("%v", plan.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", plan.ValoOptions.FfThemes.ValueBool())}), - 660: resources_core.FormatInt64(plan.S3Storage), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 149, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ValoTenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ValoTenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ValoTenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/150_k8s_sthutrval_cluster_resource.go b/generated/prod/go/150_k8s_sthutrval_cluster_resource.go deleted file mode 100644 index 34f08a9..0000000 --- a/generated/prod/go/150_k8s_sthutrval_cluster_resource.go +++ /dev/null @@ -1,429 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_sthutrval_cluster -// Service ID: 150 - -var _ resource.Resource = &K8sSthutrvalClusterResource{} -var _ resource.ResourceWithModifyPlan = &K8sSthutrvalClusterResource{} -var _ resource.ResourceWithImportState = &K8sSthutrvalClusterResource{} - -type K8sSthutrvalClusterResource struct { - client *core.UniversalClient -} - -// K8sSthutrvalClusterAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type K8sSthutrvalClusterAccessConfigurationModel struct { - NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i" json:"needExternalAddressAPI"` - AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i" json:"accessIpListAPI"` - NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress" json:"needExternalAddressIngress"` - AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress" json:"accessIpListIngress"` -} - -// K8sSthutrvalClusterClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sSthutrvalClusterClusterConfigurationModel struct { - AppVersion types.String `tfsdk:"app_version" json:"appVersion"` -} - -// K8sSthutrvalClusterControlPlaneConfigurationModel — вложенная модель для map-fixed параметра controlPlaneConfiguration. -type K8sSthutrvalClusterControlPlaneConfigurationModel struct { - SizingPolicy types.String `tfsdk:"sizing_policy" json:"sizingPolicy"` - SizingDisk types.Int64 `tfsdk:"sizing_disk" json:"sizingDisk"` - Count types.Int64 `tfsdk:"count" json:"count"` -} - -// K8sSthutrvalClusterStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sSthutrvalClusterStartupConfigurationModel struct { - VdcUid types.String `tfsdk:"vdc_uid" json:"vdcUid"` - NsxtUid types.String `tfsdk:"nsxt_uid" json:"nsxtUid"` - ClusterName types.String `tfsdk:"cluster_name" json:"clusterName"` -} - -type K8sSthutrvalClusterModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *K8sSthutrvalClusterAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *K8sSthutrvalClusterClusterConfigurationModel `tfsdk:"cluster_configuration"` - ControlPlaneConfiguration *K8sSthutrvalClusterControlPlaneConfigurationModel `tfsdk:"control_plane_configuration"` - StartupConfiguration *K8sSthutrvalClusterStartupConfigurationModel `tfsdk:"startup_configuration"` - WorkerConfiguration types.String `tfsdk:"worker_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sSthutrvalClusterResource() resource.Resource { - return &K8sSthutrvalClusterResource{} -} - -func (r *K8sSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster" -} - -func (r *K8sSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false"}, - "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, - "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress"}, - "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.13.1")}, - }}, - "control_plane_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "sizing_policy": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр`"}, - "sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50), MarkdownDescription: "Дисковая политика берётся из услуги VDC, первый в списке"}, - "count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "vdc_uid": schema.StringAttribute{Required: true}, - "nsxt_uid": schema.StringAttribute{Required: true}, - "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example-wln2kdvp")}, - }}, - "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]")}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 750: resources_core.BuildJSON(map[string]string{"vdcUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.VdcUid.ValueString()), "nsxtUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.NsxtUid.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ClusterName.ValueString())}), - 751: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", config.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", config.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", config.ControlPlaneConfiguration.Count.ValueInt64())}), - 752: resources_core.FormatString(config.WorkerConfiguration), - 753: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListIngress.ValueString())}), - 755: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.AppVersion.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 150, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 750: resources_core.BuildJSON(map[string]string{"vdcUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.VdcUid.ValueString()), "nsxtUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.NsxtUid.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ClusterName.ValueString())}), - 751: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", data.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", data.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", data.ControlPlaneConfiguration.Count.ValueInt64())}), - 752: resources_core.FormatString(data.WorkerConfiguration), - 753: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListIngress.ValueString())}), - 755: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.AppVersion.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 150, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 150, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sSthutrvalClusterModel - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.WorkerConfiguration.IsNull() != state.WorkerConfiguration.IsNull() || plan.WorkerConfiguration.IsUnknown() != state.WorkerConfiguration.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { - if !strings.EqualFold(plan.WorkerConfiguration.ValueString(), state.WorkerConfiguration.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 756: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", plan.ClusterConfiguration.AppVersion.ValueString())}), - 757: resources_core.BuildJSON(map[string]string{"sizingDisk": fmt.Sprintf("%d", plan.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", plan.ControlPlaneConfiguration.Count.ValueInt64()), "sizingPolicy": fmt.Sprintf("\"%s\"", plan.ControlPlaneConfiguration.SizingPolicy.ValueString())}), - 758: resources_core.FormatString(plan.WorkerConfiguration), - 759: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", plan.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", plan.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.AccessIpListIngress.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 150, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/150_k8s_sthutrval_cluster_user_resource.go b/generated/prod/go/150_k8s_sthutrval_cluster_user_resource.go deleted file mode 100644 index 55faf0e..0000000 --- a/generated/prod/go/150_k8s_sthutrval_cluster_user_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_sthutrval_cluster -// Subresource: user - -var _ resource.Resource = &K8sSthutrvalClusterUserResource{} - -// K8sSthutrvalClusterUserResource manages a subresource via operations. -type K8sSthutrvalClusterUserResource struct { - client *core.UniversalClient -} - -type K8sSthutrvalClusterUserModel struct { - ID types.String `tfsdk:"id"` - K8sSthutrvalClusterID types.String `tfsdk:"k8s_sthutrval_cluster_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Admins types.String `tfsdk:"admins"` -} - -func NewK8sSthutrvalClusterUserResource() resource.Resource { - return &K8sSthutrvalClusterUserResource{} -} - -func (r *K8sSthutrvalClusterUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster_user" -} - -func (r *K8sSthutrvalClusterUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_sthutrval_cluster_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "admins": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sSthutrvalClusterUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sSthutrvalClusterUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sSthutrvalClusterUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(state.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(state.Admins), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sSthutrvalClusterUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/163_llm_ai_resource.go b/generated/prod/go/163_llm_ai_resource.go deleted file mode 100644 index 506e337..0000000 --- a/generated/prod/go/163_llm_ai_resource.go +++ /dev/null @@ -1,345 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Service ID: 163 - -var _ resource.Resource = &LlmAiResource{} -var _ resource.ResourceWithModifyPlan = &LlmAiResource{} -var _ resource.ResourceWithImportState = &LlmAiResource{} - -type LlmAiResource struct { - client *core.UniversalClient -} - -type LlmAiModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewLlmAiResource() resource.Resource { - return &LlmAiResource{} -} - -func (r *LlmAiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai" -} - -func (r *LlmAiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("llmAi.aillm.ru"), MarkdownDescription: "Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *LlmAiModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 826: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 163, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *LlmAiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 163, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 826: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 163, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 163, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 163, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *LlmAiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiModel - var state LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 163, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *LlmAiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *LlmAiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/163_llm_ai_secrets_resource.go b/generated/prod/go/163_llm_ai_secrets_resource.go deleted file mode 100644 index 063f1fb..0000000 --- a/generated/prod/go/163_llm_ai_secrets_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Subresource: secrets - -var _ resource.Resource = &LlmAiSecretsResource{} - -// LlmAiSecretsResource manages a subresource via operations. -type LlmAiSecretsResource struct { - client *core.UniversalClient -} - -type LlmAiSecretsModel struct { - ID types.String `tfsdk:"id"` - LlmAiID types.String `tfsdk:"llm_ai_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KeyName types.String `tfsdk:"key_name"` - Models types.String `tfsdk:"models"` - RpmLimit types.Int64 `tfsdk:"rpm_limit"` - TeamName types.String `tfsdk:"team_name"` - TpmLimit types.Int64 `tfsdk:"tpm_limit"` -} - -func NewLlmAiSecretsResource() resource.Resource { - return &LlmAiSecretsResource{} -} - -func (r *LlmAiSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai_secrets" -} - -func (r *LlmAiSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "llm_ai_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "key_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "models": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "rpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(60), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()}}, - "team_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "tpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100000), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LlmAiSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.KeyName.IsNull() || plan.KeyName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "key_name is required.") - return - } - if plan.Models.IsNull() || plan.Models.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "models is required.") - return - } - if plan.TeamName.IsNull() || plan.TeamName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "team_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "keyName": resources_core.FormatString(plan.KeyName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "teamName": resources_core.FormatString(plan.TeamName), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - } - identityCodes := []string{ - "keyName", - "models", - "rpmLimit", - "teamName", - "tpmLimit", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - "keyName": resources_core.FormatString(plan.KeyName), - "teamName": resources_core.FormatString(plan.TeamName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "keyName": resources_core.FormatString(plan.KeyName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "teamName": resources_core.FormatString(plan.TeamName), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_secrets" == "" { - return - } - - instanceUID := strings.TrimSpace(state.LlmAiID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "keyName": resources_core.FormatString(state.KeyName), - "models": resources_core.FormatString(state.Models), - "rpmLimit": resources_core.FormatInt64(state.RpmLimit), - "teamName": resources_core.FormatString(state.TeamName), - "tpmLimit": resources_core.FormatInt64(state.TpmLimit), - } - identityCodes := []string{ - "keyName", - "models", - "rpmLimit", - "teamName", - "tpmLimit", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "keyName": resources_core.FormatString(state.KeyName), - "teamName": resources_core.FormatString(state.TeamName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_secrets", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *LlmAiSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/163_llm_ai_team_resource.go b/generated/prod/go/163_llm_ai_team_resource.go deleted file mode 100644 index e411362..0000000 --- a/generated/prod/go/163_llm_ai_team_resource.go +++ /dev/null @@ -1,365 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Subresource: team - -var _ resource.Resource = &LlmAiTeamResource{} - -// LlmAiTeamResource manages a subresource via operations. -type LlmAiTeamResource struct { - client *core.UniversalClient -} - -type LlmAiTeamModel struct { - ID types.String `tfsdk:"id"` - LlmAiID types.String `tfsdk:"llm_ai_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Models types.String `tfsdk:"models"` - RpmLimit types.Int64 `tfsdk:"rpm_limit"` - TeamName types.String `tfsdk:"team_name"` - TpmLimit types.Int64 `tfsdk:"tpm_limit"` -} - -func NewLlmAiTeamResource() resource.Resource { - return &LlmAiTeamResource{} -} - -func (r *LlmAiTeamResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai_team" -} - -func (r *LlmAiTeamResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "llm_ai_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "models": schema.StringAttribute{Required: true}, - "rpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(60)}, - "team_name": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "tpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100000)}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiTeamResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LlmAiTeamModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Models.IsNull() || plan.Models.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "models is required.") - return - } - if plan.TeamName.IsNull() || plan.TeamName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "team_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - } - identityCodes := []string{ - "teamName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("team", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_team", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_team", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiTeamResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiTeamResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiTeamModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_team" == "" { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - }) - - idParams := map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_team", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiTeamResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_team" == "" { - return - } - - instanceUID := strings.TrimSpace(state.LlmAiID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "teamName": resources_core.FormatString(state.TeamName), - } - identityCodes := []string{ - "teamName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("team", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(state.TeamName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_team", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *LlmAiTeamResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/19_vc_org_resource.go b/generated/prod/go/19_vc_org_resource.go deleted file mode 100644 index b1e8dc9..0000000 --- a/generated/prod/go/19_vc_org_resource.go +++ /dev/null @@ -1,391 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org -// Service ID: 19 - -var _ resource.Resource = &VcOrgResource{} -var _ resource.ResourceWithModifyPlan = &VcOrgResource{} -var _ resource.ResourceWithImportState = &VcOrgResource{} - -type VcOrgResource struct { - client *core.UniversalClient -} - -type VcOrgModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - OrganizationType types.String `tfsdk:"organization_type"` - ResourceRealm types.String `tfsdk:"resource_realm"` - VIPConfigure types.String `tfsdk:"v_i_p_configure"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcOrgResource() resource.Resource { - return &VcOrgResource{} -} - -func (r *VcOrgResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org" -} - -func (r *VcOrgResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "organization_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("iaas"), MarkdownDescription: "Доступ к организации предоставляется только при выборе `iaas` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ngcloud.ru"), MarkdownDescription: "Облако в котором будет развёрнут тенант Не изменяется после создания."}, - "v_i_p_configure": schema.StringAttribute{Required: true}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcOrgModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.OrganizationType.IsNull() && !plan.OrganizationType.IsUnknown() && !state.OrganizationType.IsNull() && !state.OrganizationType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.OrganizationType.ValueString(), state.OrganizationType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить organization_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VIPConfigure.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") - return - } - if config.VIPConfigure.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 418: resources_core.FormatString(config.ResourceRealm), - 556: resources_core.FormatString(config.OrganizationType), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 19, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcOrgResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VIPConfigure.IsNull() || data.VIPConfigure.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 418: resources_core.FormatString(data.ResourceRealm), - 556: resources_core.FormatString(data.OrganizationType), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 19, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 19, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcOrgResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgModel - var state VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VIPConfigure.IsNull() != state.VIPConfigure.IsNull() || plan.VIPConfigure.IsUnknown() != state.VIPConfigure.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VIPConfigure.IsNull() && !plan.VIPConfigure.IsUnknown() && !state.VIPConfigure.IsNull() && !state.VIPConfigure.IsUnknown() { - if !strings.EqualFold(plan.VIPConfigure.ValueString(), state.VIPConfigure.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 662: resources_core.FormatString(plan.VIPConfigure), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 19, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcOrgResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcOrgResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/19_vc_org_user_resource.go b/generated/prod/go/19_vc_org_user_resource.go deleted file mode 100644 index b18fcdd..0000000 --- a/generated/prod/go/19_vc_org_user_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org -// Subresource: user - -var _ resource.Resource = &VcOrgUserResource{} - -// VcOrgUserResource manages a subresource via operations. -type VcOrgUserResource struct { - client *core.UniversalClient -} - -type VcOrgUserModel struct { - ID types.String `tfsdk:"id"` - VcOrgID types.String `tfsdk:"vc_org_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Admins types.String `tfsdk:"admins"` -} - -func NewVcOrgUserResource() resource.Resource { - return &VcOrgUserResource{} -} - -func (r *VcOrgUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org_user" -} - -func (r *VcOrgUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "vc_org_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "admins": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcOrgUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcOrgUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcOrgUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.VcOrgID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(state.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(state.Admins), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *VcOrgUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/1_dummy_resource.go b/generated/prod/go/1_dummy_resource.go deleted file mode 100644 index 9867e09..0000000 --- a/generated/prod/go/1_dummy_resource.go +++ /dev/null @@ -1,593 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dummy -// Service ID: 1 - -var _ resource.Resource = &DummyResource{} -var _ resource.ResourceWithModifyPlan = &DummyResource{} -var _ resource.ResourceWithImportState = &DummyResource{} - -type DummyResource struct { - client *core.UniversalClient -} - -// DummyMapExampleModel — вложенная модель для map-fixed параметра mapExample. -type DummyMapExampleModel struct { - Subparam1 types.String `tfsdk:"subparam1" json:"subparam1"` - Secret types.String `tfsdk:"secret" json:"secret"` - Subparam2 types.String `tfsdk:"subparam2" json:"subparam2"` -} - -// DummyMapFixedModel — вложенная модель для map-fixed параметра mapFixed. -type DummyMapFixedModel struct { - Bol1 types.Bool `tfsdk:"bol1" json:"bol1"` - MinStr1 types.String `tfsdk:"min_str1" json:"minStr1"` - Param1 types.Int64 `tfsdk:"param1" json:"param1"` - Param2 types.String `tfsdk:"param2" json:"param2"` - Param3 types.Int64 `tfsdk:"param3" json:"param3"` -} - -type DummyModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Arr types.String `tfsdk:"arr"` - ArrayMapFixedExample types.String `tfsdk:"array_map_fixed_example"` - Bodymessage types.String `tfsdk:"bodymessage"` - DurationMs types.Int64 `tfsdk:"duration_ms"` - FailAtStart types.Bool `tfsdk:"fail_at_start"` - FailInProgress types.Bool `tfsdk:"fail_in_progress"` - JsonExample types.String `tfsdk:"json_example"` - MapExample *DummyMapExampleModel `tfsdk:"map_example"` - MapFixed *DummyMapFixedModel `tfsdk:"map_fixed"` - NestedRefExample types.String `tfsdk:"nested_ref_example"` - ResourceRealm types.String `tfsdk:"resource_realm"` - WhereFail types.Int64 `tfsdk:"where_fail"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDummyResource() resource.Resource { - return &DummyResource{} -} - -func (r *DummyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dummy" -} - -func (r *DummyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "arr": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "array_map_fixed_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Array of map-fixed Не изменяется после создания."}, - "bodymessage": schema.StringAttribute{Optional: true, MarkdownDescription: "Дата, уходящая в Vault"}, - "duration_ms": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "перед завершением подождать (sleep) столько миллисекунд"}, - "fail_at_start": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, завершить джоб, ничего не записывая в cmdb"}, - "fail_in_progress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb"}, - "json_example": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для тестирования интерфейса", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "map_example": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "subparam1": schema.StringAttribute{Optional: true, MarkdownDescription: "это для администратора"}, - "secret": schema.StringAttribute{Optional: true, MarkdownDescription: "это секрет"}, - "subparam2": schema.StringAttribute{Optional: true}, - }}, - "map_fixed": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "bol1": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Булевский, обязательный"}, - "min_str1": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abracadabra"), MarkdownDescription: "не меньше 10 символов"}, - "param1": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Integer, secret"}, - "param2": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Дескрипшен"}, - "param3": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "целое положительное"}, - }}, - "nested_ref_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "where_fail": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write"}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DummyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DummyModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Arr.IsNull() && !plan.Arr.IsUnknown() && !state.Arr.IsNull() && !state.Arr.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Arr.ValueString(), state.Arr.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить arr", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ArrayMapFixedExample.IsNull() && !plan.ArrayMapFixedExample.IsUnknown() && !state.ArrayMapFixedExample.IsNull() && !state.ArrayMapFixedExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ArrayMapFixedExample.ValueString(), state.ArrayMapFixedExample.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить array_map_fixed_example", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NestedRefExample.IsNull() && !plan.NestedRefExample.IsUnknown() && !state.NestedRefExample.IsNull() && !state.NestedRefExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NestedRefExample.ValueString(), state.NestedRefExample.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить nested_ref_example", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Arr.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - if config.Arr.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 198: resources_core.FormatInt64(config.DurationMs), - 199: resources_core.FormatBool(config.FailAtStart), - 200: resources_core.FormatBool(config.FailInProgress), - 201: resources_core.FormatInt64(config.WhereFail), - 242: resources_core.FormatString(config.ResourceRealm), - 286: resources_core.FormatString(config.Bodymessage), - 321: resources_core.BuildJSON(map[string]string{"subparam1": fmt.Sprintf("\"%s\"", config.MapExample.Subparam1.ValueString()), "secret": fmt.Sprintf("\"%s\"", config.MapExample.Secret.ValueString()), "subparam2": fmt.Sprintf("\"%s\"", config.MapExample.Subparam2.ValueString())}), - 322: resources_core.FormatString(config.JsonExample), - 396: resources_core.FormatString(config.NestedRefExample), - 647: resources_core.BuildJSON(map[string]string{"bol1": fmt.Sprintf("%v", config.MapFixed.Bol1.ValueBool()), "minStr1": fmt.Sprintf("\"%s\"", config.MapFixed.MinStr1.ValueString()), "param1": fmt.Sprintf("%d", config.MapFixed.Param1.ValueInt64()), "param2": fmt.Sprintf("\"%s\"", config.MapFixed.Param2.ValueString()), "param3": fmt.Sprintf("%d", config.MapFixed.Param3.ValueInt64())}), - 654: resources_core.FormatString(config.ArrayMapFixedExample), - 863: resources_core.FormatString(config.Arr), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 1, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Arr.IsNull() || data.Arr.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 198: resources_core.FormatInt64(data.DurationMs), - 199: resources_core.FormatBool(data.FailAtStart), - 200: resources_core.FormatBool(data.FailInProgress), - 201: resources_core.FormatInt64(data.WhereFail), - 242: resources_core.FormatString(data.ResourceRealm), - 286: resources_core.FormatString(data.Bodymessage), - 321: resources_core.BuildJSON(map[string]string{"subparam1": fmt.Sprintf("\"%s\"", data.MapExample.Subparam1.ValueString()), "secret": fmt.Sprintf("\"%s\"", data.MapExample.Secret.ValueString()), "subparam2": fmt.Sprintf("\"%s\"", data.MapExample.Subparam2.ValueString())}), - 322: resources_core.FormatString(data.JsonExample), - 396: resources_core.FormatString(data.NestedRefExample), - 647: resources_core.BuildJSON(map[string]string{"bol1": fmt.Sprintf("%v", data.MapFixed.Bol1.ValueBool()), "minStr1": fmt.Sprintf("\"%s\"", data.MapFixed.MinStr1.ValueString()), "param1": fmt.Sprintf("%d", data.MapFixed.Param1.ValueInt64()), "param2": fmt.Sprintf("\"%s\"", data.MapFixed.Param2.ValueString()), "param3": fmt.Sprintf("%d", data.MapFixed.Param3.ValueInt64())}), - 654: resources_core.FormatString(data.ArrayMapFixedExample), - 863: resources_core.FormatString(data.Arr), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 1, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DummyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 1, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DummyModel - var state DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.DurationMs.IsNull() != state.DurationMs.IsNull() || plan.DurationMs.IsUnknown() != state.DurationMs.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.DurationMs.IsNull() && !plan.DurationMs.IsUnknown() && !state.DurationMs.IsNull() && !state.DurationMs.IsUnknown() { - if plan.DurationMs.ValueInt64() != state.DurationMs.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.FailAtStart.IsNull() != state.FailAtStart.IsNull() || plan.FailAtStart.IsUnknown() != state.FailAtStart.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.FailAtStart.IsNull() && !plan.FailAtStart.IsUnknown() && !state.FailAtStart.IsNull() && !state.FailAtStart.IsUnknown() { - if plan.FailAtStart.ValueBool() != state.FailAtStart.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.FailInProgress.IsNull() != state.FailInProgress.IsNull() || plan.FailInProgress.IsUnknown() != state.FailInProgress.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.FailInProgress.IsNull() && !plan.FailInProgress.IsUnknown() && !state.FailInProgress.IsNull() && !state.FailInProgress.IsUnknown() { - if plan.FailInProgress.ValueBool() != state.FailInProgress.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.WhereFail.IsNull() != state.WhereFail.IsNull() || plan.WhereFail.IsUnknown() != state.WhereFail.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.WhereFail.IsNull() && !plan.WhereFail.IsUnknown() && !state.WhereFail.IsNull() && !state.WhereFail.IsUnknown() { - if plan.WhereFail.ValueInt64() != state.WhereFail.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.Bodymessage.IsNull() != state.Bodymessage.IsNull() || plan.Bodymessage.IsUnknown() != state.Bodymessage.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Bodymessage.IsNull() && !plan.Bodymessage.IsUnknown() && !state.Bodymessage.IsNull() && !state.Bodymessage.IsUnknown() { - if !strings.EqualFold(plan.Bodymessage.ValueString(), state.Bodymessage.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.JsonExample.IsNull() != state.JsonExample.IsNull() || plan.JsonExample.IsUnknown() != state.JsonExample.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonExample.IsNull() && !plan.JsonExample.IsUnknown() && !state.JsonExample.IsNull() && !state.JsonExample.IsUnknown() { - if !strings.EqualFold(plan.JsonExample.ValueString(), state.JsonExample.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 287: resources_core.FormatInt64(plan.DurationMs), - 288: resources_core.FormatBool(plan.FailAtStart), - 289: resources_core.FormatBool(plan.FailInProgress), - 290: resources_core.FormatInt64(plan.WhereFail), - 291: resources_core.FormatString(plan.Bodymessage), - 373: resources_core.BuildJSON(map[string]string{"secret": fmt.Sprintf("\"%s\"", plan.MapExample.Secret.ValueString())}), - 374: resources_core.FormatString(plan.JsonExample), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 1, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DummyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DummyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DummyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/20_vc_org_saas_resource.go b/generated/prod/go/20_vc_org_saas_resource.go deleted file mode 100644 index 7f53d87..0000000 --- a/generated/prod/go/20_vc_org_saas_resource.go +++ /dev/null @@ -1,348 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org_saas -// Service ID: 20 - -var _ resource.Resource = &VcOrgSaasResource{} -var _ resource.ResourceWithModifyPlan = &VcOrgSaasResource{} -var _ resource.ResourceWithImportState = &VcOrgSaasResource{} - -type VcOrgSaasResource struct { - client *core.UniversalClient -} - -type VcOrgSaasModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcOrgSaasResource() resource.Resource { - return &VcOrgSaasResource{} -} - -func (r *VcOrgSaasResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org_saas" -} - -func (r *VcOrgSaasResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ngcloud.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgSaasResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcOrgSaasModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 185: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 20, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcOrgSaasResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 185: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 20, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgSaasResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 20, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcOrgSaasResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgSaasModel - var state VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 20, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgSaasResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcOrgSaasResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcOrgSaasResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/21_vc_vdc_resource.go b/generated/prod/go/21_vc_vdc_resource.go deleted file mode 100644 index 9ebce30..0000000 --- a/generated/prod/go/21_vc_vdc_resource.go +++ /dev/null @@ -1,573 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc -// Service ID: 21 - -var _ resource.Resource = &VcVdcResource{} -var _ resource.ResourceWithModifyPlan = &VcVdcResource{} -var _ resource.ResourceWithImportState = &VcVdcResource{} - -type VcVdcResource struct { - client *core.UniversalClient -} - -type VcVdcModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - CpuAllocated types.Int64 `tfsdk:"cpu_allocated"` - CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` - MemAllocated types.Int64 `tfsdk:"mem_allocated"` - NetworkProvider types.String `tfsdk:"network_provider"` - OrganizationUid types.String `tfsdk:"organization_uid"` - ProviderVdc types.String `tfsdk:"provider_vdc"` - StorageConfig types.String `tfsdk:"storage_config"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVdcResource() resource.Resource { - return &VcVdcResource{} -} - -func (r *VcVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc" -} - -func (r *VcVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "cpu_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "шт"}, - "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "% резервирования Не изменяется после создания."}, - "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "gb"}, - "network_provider": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "organization_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` Не изменяется после создания."}, - "provider_vdc": schema.StringAttribute{Required: true, MarkdownDescription: "Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
Не изменяется после создания."}, - "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Первая политика в списке указывается как "По умолчанию""}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVdcModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { - if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NetworkProvider.IsNull() && !plan.NetworkProvider.IsUnknown() && !state.NetworkProvider.IsNull() && !state.NetworkProvider.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NetworkProvider.ValueString(), state.NetworkProvider.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить network_provider", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.OrganizationUid.ValueString(), state.OrganizationUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить organization_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ProviderVdc.IsNull() && !plan.ProviderVdc.IsUnknown() && !state.ProviderVdc.IsNull() && !state.ProviderVdc.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ProviderVdc.ValueString(), state.ProviderVdc.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить provider_vdc", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.NetworkProvider.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") - return - } - if config.NetworkProvider.IsUnknown() { - return - } - if config.OrganizationUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") - return - } - if config.OrganizationUid.IsUnknown() { - return - } - if config.ProviderVdc.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") - return - } - if config.ProviderVdc.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.OrganizationUid.IsNull() && !config.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, config.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != config.OrganizationUid.ValueString() { - config.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - params := map[int]string{ - 30: resources_core.FormatString(config.OrganizationUid), - 335: resources_core.FormatString(config.ProviderVdc), - 361: resources_core.FormatString(config.StorageConfig), - 397: resources_core.FormatInt64(config.CpuGuaranteed), - 557: resources_core.FormatInt64(config.CpuAllocated), - 558: resources_core.FormatInt64(config.MemAllocated), - 746: resources_core.FormatString(config.NetworkProvider), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 21, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.NetworkProvider.IsNull() || data.NetworkProvider.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") - return - } - if data.OrganizationUid.IsNull() || data.OrganizationUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") - return - } - if data.ProviderVdc.IsNull() || data.ProviderVdc.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalOrganizationUid := data.OrganizationUid - if !data.OrganizationUid.IsNull() && !data.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, data.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedOrganizationUid != "" && resolvedOrganizationUid != data.OrganizationUid.ValueString() { - data.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 30: resources_core.FormatString(data.OrganizationUid), - 335: resources_core.FormatString(data.ProviderVdc), - 361: resources_core.FormatString(data.StorageConfig), - 397: resources_core.FormatInt64(data.CpuGuaranteed), - 557: resources_core.FormatInt64(data.CpuAllocated), - 558: resources_core.FormatInt64(data.MemAllocated), - 746: resources_core.FormatString(data.NetworkProvider), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 21, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { - state.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalOrganizationUid.IsNull() && !originalOrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - if strings.EqualFold(state.OrganizationUid.ValueString(), originalOrganizationUid.ValueString()) { - state.OrganizationUid = originalOrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 21, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, newState.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != newState.OrganizationUid.ValueString() { - newState.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() && !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { - if strings.EqualFold(newState.OrganizationUid.ValueString(), state.OrganizationUid.ValueString()) { - newState.OrganizationUid = state.OrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcModel - var state VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.CpuAllocated.IsNull() != state.CpuAllocated.IsNull() || plan.CpuAllocated.IsUnknown() != state.CpuAllocated.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { - if plan.CpuAllocated.ValueInt64() != state.CpuAllocated.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MemAllocated.IsNull() != state.MemAllocated.IsNull() || plan.MemAllocated.IsUnknown() != state.MemAllocated.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { - if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.StorageConfig.IsNull() != state.StorageConfig.IsNull() || plan.StorageConfig.IsUnknown() != state.StorageConfig.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { - if !strings.EqualFold(plan.StorageConfig.ValueString(), state.StorageConfig.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 560: resources_core.FormatInt64(plan.CpuAllocated), - 561: resources_core.FormatInt64(plan.MemAllocated), - 562: resources_core.FormatString(plan.StorageConfig), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 21, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { - state.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - if strings.EqualFold(state.OrganizationUid.ValueString(), plan.OrganizationUid.ValueString()) { - state.OrganizationUid = plan.OrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVdcResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/22_vc_nsxt_resource.go b/generated/prod/go/22_vc_nsxt_resource.go deleted file mode 100644 index 443f3a6..0000000 --- a/generated/prod/go/22_vc_nsxt_resource.go +++ /dev/null @@ -1,614 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_nsxt -// Service ID: 22 - -var _ resource.Resource = &VcNsxtResource{} -var _ resource.ResourceWithModifyPlan = &VcNsxtResource{} -var _ resource.ResourceWithImportState = &VcNsxtResource{} - -type VcNsxtResource struct { - client *core.UniversalClient -} - -type VcNsxtModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` - QosProfile types.String `tfsdk:"qos_profile"` - VdcGroupUid types.String `tfsdk:"vdc_group_uid"` - VdcType types.String `tfsdk:"vdc_type"` - VdcUid types.String `tfsdk:"vdc_uid"` - VirtualServicesCount types.Int64 `tfsdk:"virtual_services_count"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcNsxtResource() resource.Resource { - return &VcNsxtResource{} -} - -func (r *VcNsxtResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_nsxt" -} - -func (r *VcNsxtResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ip Space для внешнего IP"}, - "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включение Load Balancer"}, - "qos_profile": schema.StringAttribute{Optional: true, MarkdownDescription: "Параметр пока не работает
Должен возвращать поле из ресурсной платформы типа vc из .vcd.hardware.gatewayQoSProfiles

Как по идее должен работать.
Нужно заполнить или CFS vdcUid, или vdcGroupUid
Идеально конечно проверять `if (params.vdcType == "vdc" && params.vdcUid != "")` или `if (params.vdcType == "vdcGroup" && params.vdcGroupUid != "")`

Далее надо пойти по стейту vdc -> org -> resPlatform, взять gatewayQoSProfiles
Или пойти по стейту vdcgroup -> vdc -> org -> resPlatform, взять gatewayQoSProfiles

Регулярку могу написать (Виталя)"}, - "vdc_group_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Группа виртуальных датацентров (groupvDC)` Не изменяется после создания."}, - "vdc_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vdc"), MarkdownDescription: "Тип родительской услуги Не изменяется после создания."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, - "virtual_services_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Кол-во виртуальных сервисов, которые **резервируются** на AVI"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcNsxtResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcNsxtModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcGroupUid.ValueString(), state.VdcGroupUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_group_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcType.IsNull() && !plan.VdcType.IsUnknown() && !state.VdcType.IsNull() && !state.VdcType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcType.ValueString(), state.VdcType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VdcGroupUid.IsNull() && !config.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, config.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != config.VdcGroupUid.ValueString() { - config.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 8: resources_core.FormatString(config.VdcUid), - 340: resources_core.FormatBool(config.NeedEnableAVI), - 341: resources_core.FormatInt64(config.VirtualServicesCount), - 621: resources_core.FormatString(config.VdcType), - 622: resources_core.FormatString(config.VdcGroupUid), - 825: resources_core.FormatString(config.QosProfile), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 22, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcNsxtResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcGroupUid := data.VdcGroupUid - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.VdcGroupUid.IsNull() && !data.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, data.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != data.VdcGroupUid.ValueString() { - data.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 8: resources_core.FormatString(data.VdcUid), - 340: resources_core.FormatBool(data.NeedEnableAVI), - 341: resources_core.FormatInt64(data.VirtualServicesCount), - 621: resources_core.FormatString(data.VdcType), - 622: resources_core.FormatString(data.VdcGroupUid), - 825: resources_core.FormatString(data.QosProfile), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 22, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { - state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVdcGroupUid.IsNull() && !originalVdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - if strings.EqualFold(state.VdcGroupUid.ValueString(), originalVdcGroupUid.ValueString()) { - state.VdcGroupUid = originalVdcGroupUid - } - } - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNsxtResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 22, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, newState.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != newState.VdcGroupUid.ValueString() { - newState.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() && !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { - if strings.EqualFold(newState.VdcGroupUid.ValueString(), state.VdcGroupUid.ValueString()) { - newState.VdcGroupUid = state.VdcGroupUid - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcNsxtResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcNsxtModel - var state VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.NeedEnableAVI.IsNull() != state.NeedEnableAVI.IsNull() || plan.NeedEnableAVI.IsUnknown() != state.NeedEnableAVI.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { - if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VirtualServicesCount.IsNull() != state.VirtualServicesCount.IsNull() || plan.VirtualServicesCount.IsUnknown() != state.VirtualServicesCount.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { - if plan.VirtualServicesCount.ValueInt64() != state.VirtualServicesCount.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.QosProfile.IsNull() != state.QosProfile.IsNull() || plan.QosProfile.IsUnknown() != state.QosProfile.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.QosProfile.IsNull() && !plan.QosProfile.IsUnknown() && !state.QosProfile.IsNull() && !state.QosProfile.IsUnknown() { - if !strings.EqualFold(plan.QosProfile.ValueString(), state.QosProfile.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 368: resources_core.FormatBool(plan.NeedEnableAVI), - 369: resources_core.FormatInt64(plan.VirtualServicesCount), - 372: resources_core.FormatString(plan.IpSpaceName), - 856: resources_core.FormatString(plan.QosProfile), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 22, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { - state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - if strings.EqualFold(state.VdcGroupUid.ValueString(), plan.VdcGroupUid.ValueString()) { - state.VdcGroupUid = plan.VdcGroupUid - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNsxtResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcNsxtResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcNsxtResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/23_vc_vm_resource.go b/generated/prod/go/23_vc_vm_resource.go deleted file mode 100644 index ff0b8c6..0000000 --- a/generated/prod/go/23_vc_vm_resource.go +++ /dev/null @@ -1,581 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm -// Service ID: 23 - -var _ resource.Resource = &VcVmResource{} -var _ resource.ResourceWithModifyPlan = &VcVmResource{} -var _ resource.ResourceWithImportState = &VcVmResource{} - -type VcVmResource struct { - client *core.UniversalClient -} - -type VcVmModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessIpList types.String `tfsdk:"access_ip_list"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - TemplateName types.String `tfsdk:"template_name"` - VappUid types.String `tfsdk:"vapp_uid"` - VmCpu types.String `tfsdk:"vm_cpu"` - VmDisksize types.String `tfsdk:"vm_disksize"` - VmName types.String `tfsdk:"vm_name"` - VmRam types.String `tfsdk:"vm_ram"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmResource() resource.Resource { - return &VcVmResource{} -} - -func (r *VcVmResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm" -} - -func (r *VcVmResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("79.111.119.78/32 185.247.187.1/32"), MarkdownDescription: "Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов"}, - "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) Не изменяется после создания."}, - "template_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("RockyLinux_9-16G"), MarkdownDescription: "ОС Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "InstanceUid vApp. vApp должен быть создан Не изменяется после создания."}, - "vm_cpu": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "CPU"}, - "vm_disksize": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("10"), MarkdownDescription: "Размер диска под сервис (GB)"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vm0001"), MarkdownDescription: "Имя ВМ. Не поддерживает спец-символы Не изменяется после создания."}, - "vm_ram": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "RAM (GB)"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.TemplateName.IsNull() && !plan.TemplateName.IsUnknown() && !state.TemplateName.IsNull() && !state.TemplateName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.TemplateName.ValueString(), state.TemplateName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить template_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappUid.ValueString(), state.VappUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmName.ValueString(), state.VmName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.VappUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - if config.VappUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { - config.VappUid = types.StringValue(resolvedVappUid) - } - } - params := map[int]string{ - 33: resources_core.FormatString(config.VappUid), - 35: resources_core.FormatString(config.VmName), - 36: resources_core.FormatString(config.VmCpu), - 37: resources_core.FormatString(config.VmRam), - 38: resources_core.FormatString(config.VmDisksize), - 176: resources_core.FormatString(config.TemplateName), - 177: resources_core.FormatString(config.AccessIpList), - 406: resources_core.FormatString(config.IpSpaceName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 23, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.VappUid.IsNull() || data.VappUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVappUid := data.VappUid - if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { - data.VappUid = types.StringValue(resolvedVappUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 33: resources_core.FormatString(data.VappUid), - 35: resources_core.FormatString(data.VmName), - 36: resources_core.FormatString(data.VmCpu), - 37: resources_core.FormatString(data.VmRam), - 38: resources_core.FormatString(data.VmDisksize), - 176: resources_core.FormatString(data.TemplateName), - 177: resources_core.FormatString(data.AccessIpList), - 406: resources_core.FormatString(data.IpSpaceName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 23, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVappUid.IsNull() && !originalVappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), originalVappUid.ValueString()) { - state.VappUid = originalVappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 23, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { - newState.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() && !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - if strings.EqualFold(newState.VappUid.ValueString(), state.VappUid.ValueString()) { - newState.VappUid = state.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmModel - var state VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { - if !strings.EqualFold(plan.VmCpu.ValueString(), state.VmCpu.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { - if !strings.EqualFold(plan.VmRam.ValueString(), state.VmRam.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmDisksize.IsNull() != state.VmDisksize.IsNull() || plan.VmDisksize.IsUnknown() != state.VmDisksize.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmDisksize.IsNull() && !plan.VmDisksize.IsUnknown() && !state.VmDisksize.IsNull() && !state.VmDisksize.IsUnknown() { - if !strings.EqualFold(plan.VmDisksize.ValueString(), state.VmDisksize.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { - if !strings.EqualFold(plan.AccessIpList.ValueString(), state.AccessIpList.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 186: resources_core.FormatString(plan.VmCpu), - 187: resources_core.FormatString(plan.VmRam), - 188: resources_core.FormatString(plan.VmDisksize), - 189: resources_core.FormatString(plan.AccessIpList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 23, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), plan.VappUid.ValueString()) { - state.VappUid = plan.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/25_vcexternalip_resource.go b/generated/prod/go/25_vcexternalip_resource.go deleted file mode 100644 index d11a84c..0000000 --- a/generated/prod/go/25_vcexternalip_resource.go +++ /dev/null @@ -1,604 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vcexternalip -// Service ID: 25 - -var _ resource.Resource = &VcexternalipResource{} -var _ resource.ResourceWithModifyPlan = &VcexternalipResource{} -var _ resource.ResourceWithImportState = &VcexternalipResource{} - -type VcexternalipResource struct { - client *core.UniversalClient -} - -type VcexternalipModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DnatCreate types.Bool `tfsdk:"dnat_create"` - FromServiceCloudEdgeName types.String `tfsdk:"from_service_cloud_edge_name"` - FromServiceCloudEdgeScope types.String `tfsdk:"from_service_cloud_edge_scope"` - FromServiceCloudOrgName types.String `tfsdk:"from_service_cloud_org_name"` - FromServiceCloudVdcName types.String `tfsdk:"from_service_cloud_vdc_name"` - FromServiceCloudVmwareUrl types.String `tfsdk:"from_service_cloud_vmware_url"` - FromServiceNamespace types.String `tfsdk:"from_service_namespace"` - FromServiceVdcGroupName types.String `tfsdk:"from_service_vdc_group_name"` - InternalAddrAccess types.String `tfsdk:"internal_addr_access"` - InternalPortAccess types.String `tfsdk:"internal_port_access"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ServiceUid types.String `tfsdk:"service_uid"` - SnatCreate types.Bool `tfsdk:"snat_create"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcexternalipResource() resource.Resource { - return &VcexternalipResource{} -} - -func (r *VcexternalipResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vcexternalip" -} - -func (r *VcexternalipResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "dnat_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходимо создать DNAT-правило."}, - "from_service_cloud_edge_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_edge_scope": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "from_service_cloud_org_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Org составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_vdc_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_vmware_url": schema.StringAttribute{Optional: true, MarkdownDescription: "Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_namespace": schema.StringAttribute{Required: true, MarkdownDescription: "Namespace составной услуги (для отображения правил FW) Не изменяется после создания."}, - "from_service_vdc_group_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "internal_addr_access": schema.StringAttribute{Required: true, MarkdownDescription: "Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true"}, - "internal_port_access": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("5432"), MarkdownDescription: "Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
"}, - "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ngcloud.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "service_uid": schema.StringAttribute{Required: true}, - "snat_create": schema.BoolAttribute{Optional: true, MarkdownDescription: "Необходимо создать SNAT-правило.
"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcexternalipResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcexternalipModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.FromServiceCloudEdgeName.IsNull() && !plan.FromServiceCloudEdgeName.IsUnknown() && !state.FromServiceCloudEdgeName.IsNull() && !state.FromServiceCloudEdgeName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudEdgeName.ValueString(), state.FromServiceCloudEdgeName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudEdgeScope.IsNull() && !plan.FromServiceCloudEdgeScope.IsUnknown() && !state.FromServiceCloudEdgeScope.IsNull() && !state.FromServiceCloudEdgeScope.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudEdgeScope.ValueString(), state.FromServiceCloudEdgeScope.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_scope", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudOrgName.IsNull() && !plan.FromServiceCloudOrgName.IsUnknown() && !state.FromServiceCloudOrgName.IsNull() && !state.FromServiceCloudOrgName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudOrgName.ValueString(), state.FromServiceCloudOrgName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_org_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudVdcName.IsNull() && !plan.FromServiceCloudVdcName.IsUnknown() && !state.FromServiceCloudVdcName.IsNull() && !state.FromServiceCloudVdcName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudVdcName.ValueString(), state.FromServiceCloudVdcName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vdc_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudVmwareUrl.IsNull() && !plan.FromServiceCloudVmwareUrl.IsUnknown() && !state.FromServiceCloudVmwareUrl.IsNull() && !state.FromServiceCloudVmwareUrl.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudVmwareUrl.ValueString(), state.FromServiceCloudVmwareUrl.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vmware_url", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceNamespace.IsNull() && !plan.FromServiceNamespace.IsUnknown() && !state.FromServiceNamespace.IsNull() && !state.FromServiceNamespace.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceNamespace.ValueString(), state.FromServiceNamespace.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_namespace", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceVdcGroupName.IsNull() && !plan.FromServiceVdcGroupName.IsUnknown() && !state.FromServiceVdcGroupName.IsNull() && !state.FromServiceVdcGroupName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceVdcGroupName.ValueString(), state.FromServiceVdcGroupName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_vdc_group_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.FromServiceNamespace.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") - return - } - if config.FromServiceNamespace.IsUnknown() { - return - } - if config.InternalAddrAccess.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") - return - } - if config.InternalAddrAccess.IsUnknown() { - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.ServiceUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") - return - } - if config.ServiceUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 140: resources_core.FormatString(config.ServiceUid), - 141: resources_core.FormatBool(config.DnatCreate), - 142: resources_core.FormatString(config.InternalPortAccess), - 143: resources_core.FormatBool(config.SnatCreate), - 144: resources_core.FormatString(config.InternalAddrAccess), - 324: resources_core.FormatString(config.FromServiceNamespace), - 325: resources_core.FormatString(config.FromServiceCloudEdgeName), - 326: resources_core.FormatString(config.FromServiceCloudVdcName), - 327: resources_core.FormatString(config.FromServiceCloudOrgName), - 328: resources_core.FormatString(config.FromServiceCloudVmwareUrl), - 336: resources_core.FormatString(config.IpSpaceName), - 488: resources_core.FormatString(config.ResourceRealm), - 624: resources_core.FormatString(config.FromServiceCloudEdgeScope), - 626: resources_core.FormatString(config.FromServiceVdcGroupName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 25, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcexternalipResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.FromServiceNamespace.IsNull() || data.FromServiceNamespace.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") - return - } - if data.InternalAddrAccess.IsNull() || data.InternalAddrAccess.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.ServiceUid.IsNull() || data.ServiceUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 140: resources_core.FormatString(data.ServiceUid), - 141: resources_core.FormatBool(data.DnatCreate), - 142: resources_core.FormatString(data.InternalPortAccess), - 143: resources_core.FormatBool(data.SnatCreate), - 144: resources_core.FormatString(data.InternalAddrAccess), - 324: resources_core.FormatString(data.FromServiceNamespace), - 325: resources_core.FormatString(data.FromServiceCloudEdgeName), - 326: resources_core.FormatString(data.FromServiceCloudVdcName), - 327: resources_core.FormatString(data.FromServiceCloudOrgName), - 328: resources_core.FormatString(data.FromServiceCloudVmwareUrl), - 336: resources_core.FormatString(data.IpSpaceName), - 488: resources_core.FormatString(data.ResourceRealm), - 624: resources_core.FormatString(data.FromServiceCloudEdgeScope), - 626: resources_core.FormatString(data.FromServiceVdcGroupName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 25, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcexternalipResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 25, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcexternalipResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcexternalipModel - var state VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ServiceUid.IsNull() != state.ServiceUid.IsNull() || plan.ServiceUid.IsUnknown() != state.ServiceUid.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ServiceUid.IsNull() && !plan.ServiceUid.IsUnknown() && !state.ServiceUid.IsNull() && !state.ServiceUid.IsUnknown() { - if !strings.EqualFold(plan.ServiceUid.ValueString(), state.ServiceUid.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.DnatCreate.IsNull() != state.DnatCreate.IsNull() || plan.DnatCreate.IsUnknown() != state.DnatCreate.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.DnatCreate.IsNull() && !plan.DnatCreate.IsUnknown() && !state.DnatCreate.IsNull() && !state.DnatCreate.IsUnknown() { - if plan.DnatCreate.ValueBool() != state.DnatCreate.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.SnatCreate.IsNull() != state.SnatCreate.IsNull() || plan.SnatCreate.IsUnknown() != state.SnatCreate.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.SnatCreate.IsNull() && !plan.SnatCreate.IsUnknown() && !state.SnatCreate.IsNull() && !state.SnatCreate.IsUnknown() { - if plan.SnatCreate.ValueBool() != state.SnatCreate.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.InternalPortAccess.IsNull() != state.InternalPortAccess.IsNull() || plan.InternalPortAccess.IsUnknown() != state.InternalPortAccess.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.InternalPortAccess.IsNull() && !plan.InternalPortAccess.IsUnknown() && !state.InternalPortAccess.IsNull() && !state.InternalPortAccess.IsUnknown() { - if !strings.EqualFold(plan.InternalPortAccess.ValueString(), state.InternalPortAccess.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.InternalAddrAccess.IsNull() != state.InternalAddrAccess.IsNull() || plan.InternalAddrAccess.IsUnknown() != state.InternalAddrAccess.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.InternalAddrAccess.IsNull() && !plan.InternalAddrAccess.IsUnknown() && !state.InternalAddrAccess.IsNull() && !state.InternalAddrAccess.IsUnknown() { - if !strings.EqualFold(plan.InternalAddrAccess.ValueString(), state.InternalAddrAccess.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 627: resources_core.FormatString(plan.ServiceUid), - 628: resources_core.FormatString(plan.IpSpaceName), - 629: resources_core.FormatBool(plan.DnatCreate), - 630: resources_core.FormatBool(plan.SnatCreate), - 631: resources_core.FormatString(plan.InternalPortAccess), - 632: resources_core.FormatString(plan.InternalAddrAccess), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 25, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcexternalipResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcexternalipResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcexternalipResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/26_vapp_resource.go b/generated/prod/go/26_vapp_resource.go deleted file mode 100644 index 158be46..0000000 --- a/generated/prod/go/26_vapp_resource.go +++ /dev/null @@ -1,571 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vapp -// Service ID: 26 - -var _ resource.Resource = &VappResource{} -var _ resource.ResourceWithModifyPlan = &VappResource{} -var _ resource.ResourceWithImportState = &VappResource{} - -type VappResource struct { - client *core.UniversalClient -} - -type VappModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - NsxtUid types.String `tfsdk:"nsxt_uid"` - VappName types.String `tfsdk:"vapp_name"` - VdcUid types.String `tfsdk:"vdc_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVappResource() resource.Resource { - return &VappResource{} -} - -func (r *VappResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vapp" -} - -func (r *VappResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "nsxt_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Сетевой шлюз периметра (Edge)` Не изменяется после создания."}, - "vapp_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3ecobacg"), MarkdownDescription: "Уникальное в рамках организации Не изменяется после создания."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VappResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VappModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NsxtUid.ValueString(), state.NsxtUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить nsxt_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappName.ValueString(), state.VappName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.NsxtUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") - return - } - if config.NsxtUid.IsUnknown() { - return - } - if config.VdcUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - if config.VdcUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.NsxtUid.IsNull() && !config.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, config.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != config.NsxtUid.ValueString() { - config.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 190: resources_core.FormatString(config.NsxtUid), - 191: resources_core.FormatString(config.VappName), - 623: resources_core.FormatString(config.VdcUid), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 26, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VappResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.NsxtUid.IsNull() || data.NsxtUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") - return - } - if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalNsxtUid := data.NsxtUid - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.NsxtUid.IsNull() && !data.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, data.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedNsxtUid != "" && resolvedNsxtUid != data.NsxtUid.ValueString() { - data.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 190: resources_core.FormatString(data.NsxtUid), - 191: resources_core.FormatString(data.VappName), - 623: resources_core.FormatString(data.VdcUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 26, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { - state.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalNsxtUid.IsNull() && !originalNsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - if strings.EqualFold(state.NsxtUid.ValueString(), originalNsxtUid.ValueString()) { - state.NsxtUid = originalNsxtUid - } - } - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VappResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 26, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, newState.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != newState.NsxtUid.ValueString() { - newState.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() && !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { - if strings.EqualFold(newState.NsxtUid.ValueString(), state.NsxtUid.ValueString()) { - newState.NsxtUid = state.NsxtUid - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VappResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VappModel - var state VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 26, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { - state.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - if strings.EqualFold(state.NsxtUid.ValueString(), plan.NsxtUid.ValueString()) { - state.NsxtUid = plan.NsxtUid - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VappResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VappResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VappResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/27_vc_vm_v2_resource.go b/generated/prod/go/27_vc_vm_v2_resource.go deleted file mode 100644 index afd6fec..0000000 --- a/generated/prod/go/27_vc_vm_v2_resource.go +++ /dev/null @@ -1,350 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v2 -// Service ID: 27 - -var _ resource.Resource = &VcVmV2Resource{} -var _ resource.ResourceWithModifyPlan = &VcVmV2Resource{} -var _ resource.ResourceWithImportState = &VcVmV2Resource{} - -type VcVmV2Resource struct { - client *core.UniversalClient -} - -type VcVmV2Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ServerListJson types.String `tfsdk:"server_list_json"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmV2Resource() resource.Resource { - return &VcVmV2Resource{} -} - -func (r *VcVmV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v2" -} - -func (r *VcVmV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "server_list_json": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmV2Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 301: resources_core.FormatString(config.ServerListJson), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 27, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 301: resources_core.FormatString(data.ServerListJson), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 27, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 27, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV2Model - var state VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ServerListJson.IsNull() != state.ServerListJson.IsNull() || plan.ServerListJson.IsUnknown() != state.ServerListJson.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ServerListJson.IsNull() && !plan.ServerListJson.IsUnknown() && !state.ServerListJson.IsNull() && !state.ServerListJson.IsUnknown() { - if !strings.EqualFold(plan.ServerListJson.ValueString(), state.ServerListJson.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 302: resources_core.FormatString(plan.ServerListJson), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 27, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/28_vc_vm_v3_resource.go b/generated/prod/go/28_vc_vm_v3_resource.go deleted file mode 100644 index 89e6d1e..0000000 --- a/generated/prod/go/28_vc_vm_v3_resource.go +++ /dev/null @@ -1,697 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v3 -// Service ID: 28 - -var _ resource.Resource = &VcVmV3Resource{} -var _ resource.ResourceWithModifyPlan = &VcVmV3Resource{} -var _ resource.ResourceWithImportState = &VcVmV3Resource{} - -type VcVmV3Resource struct { - client *core.UniversalClient -} - -type VcVmV3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessIpList types.String `tfsdk:"access_ip_list"` - AccessPortList types.String `tfsdk:"access_port_list"` - CloudInit types.String `tfsdk:"cloud_init"` - ImageVm types.String `tfsdk:"image_vm"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - UserLogin types.String `tfsdk:"user_login"` - UserPublicKey types.String `tfsdk:"user_public_key"` - VappUid types.String `tfsdk:"vapp_uid"` - VmCpu types.Int64 `tfsdk:"vm_cpu"` - VmDisk types.Int64 `tfsdk:"vm_disk"` - VmName types.String `tfsdk:"vm_name"` - VmRam types.Int64 `tfsdk:"vm_ram"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmV3Resource() resource.Resource { - return &VcVmV3Resource{} -} - -func (r *VcVmV3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v3" -} - -func (r *VcVmV3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "access_port_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"22\"]"), MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, - "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. Не изменяется после создания."}, - "image_vm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Required: true}, - "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myuser"), MarkdownDescription: "Учётка SSH Не изменяется после создания."}, - "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=26 (mapped to UUID in core) Не изменяется после создания."}, - "vm_cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "шт"}, - "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Не изменяется после создания."}, - "vm_ram": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Gb"}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmV3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmV3Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CloudInit.IsNull() && !plan.CloudInit.IsUnknown() && !state.CloudInit.IsNull() && !state.CloudInit.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.CloudInit.ValueString(), state.CloudInit.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить cloud_init", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ImageVm.IsNull() && !plan.ImageVm.IsUnknown() && !state.ImageVm.IsNull() && !state.ImageVm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ImageVm.ValueString(), state.ImageVm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить image_vm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserLogin.ValueString(), state.UserLogin.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserPublicKey.IsNull() && !plan.UserPublicKey.IsUnknown() && !state.UserPublicKey.IsNull() && !state.UserPublicKey.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserPublicKey.ValueString(), state.UserPublicKey.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_public_key", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappUid.ValueString(), state.VappUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmName.ValueString(), state.VmName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ImageVm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") - return - } - if config.ImageVm.IsUnknown() { - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.UserPublicKey.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") - return - } - if config.UserPublicKey.IsUnknown() { - return - } - if config.VappUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - if config.VappUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { - config.VappUid = types.StringValue(resolvedVappUid) - } - } - params := map[int]string{ - 407: resources_core.FormatString(config.VappUid), - 408: resources_core.FormatString(config.VmName), - 409: resources_core.FormatInt64(config.VmCpu), - 410: resources_core.FormatInt64(config.VmRam), - 411: resources_core.FormatInt64(config.VmDisk), - 412: resources_core.FormatString(config.IpSpaceName), - 413: resources_core.FormatString(config.AccessIpList), - 414: resources_core.FormatString(config.ImageVm), - 415: resources_core.FormatString(config.CloudInit), - 416: resources_core.FormatString(config.UserLogin), - 417: resources_core.FormatString(config.UserPublicKey), - 448: resources_core.FormatString(config.AccessPortList), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 28, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmV3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ImageVm.IsNull() || data.ImageVm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.UserPublicKey.IsNull() || data.UserPublicKey.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") - return - } - if data.VappUid.IsNull() || data.VappUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVappUid := data.VappUid - if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { - data.VappUid = types.StringValue(resolvedVappUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 407: resources_core.FormatString(data.VappUid), - 408: resources_core.FormatString(data.VmName), - 409: resources_core.FormatInt64(data.VmCpu), - 410: resources_core.FormatInt64(data.VmRam), - 411: resources_core.FormatInt64(data.VmDisk), - 412: resources_core.FormatString(data.IpSpaceName), - 413: resources_core.FormatString(data.AccessIpList), - 414: resources_core.FormatString(data.ImageVm), - 415: resources_core.FormatString(data.CloudInit), - 416: resources_core.FormatString(data.UserLogin), - 417: resources_core.FormatString(data.UserPublicKey), - 448: resources_core.FormatString(data.AccessPortList), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 28, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVappUid.IsNull() && !originalVappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), originalVappUid.ValueString()) { - state.VappUid = originalVappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 28, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { - newState.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() && !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - if strings.EqualFold(newState.VappUid.ValueString(), state.VappUid.ValueString()) { - newState.VappUid = state.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV3Model - var state VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { - if plan.VmCpu.ValueInt64() != state.VmCpu.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { - if plan.VmRam.ValueInt64() != state.VmRam.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmDisk.IsNull() != state.VmDisk.IsNull() || plan.VmDisk.IsUnknown() != state.VmDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmDisk.IsNull() && !plan.VmDisk.IsUnknown() && !state.VmDisk.IsNull() && !state.VmDisk.IsUnknown() { - if plan.VmDisk.ValueInt64() != state.VmDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { - if !strings.EqualFold(plan.AccessIpList.ValueString(), state.AccessIpList.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessPortList.IsNull() != state.AccessPortList.IsNull() || plan.AccessPortList.IsUnknown() != state.AccessPortList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessPortList.IsNull() && !plan.AccessPortList.IsUnknown() && !state.AccessPortList.IsNull() && !state.AccessPortList.IsUnknown() { - if !strings.EqualFold(plan.AccessPortList.ValueString(), state.AccessPortList.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 493: resources_core.FormatInt64(plan.VmCpu), - 494: resources_core.FormatInt64(plan.VmRam), - 495: resources_core.FormatInt64(plan.VmDisk), - 496: resources_core.FormatString(plan.IpSpaceName), - 497: resources_core.FormatString(plan.AccessIpList), - 498: resources_core.FormatString(plan.AccessPortList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 28, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), plan.VappUid.ValueString()) { - state.VappUid = plan.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmV3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmV3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/29_vc_vdc_group_resource.go b/generated/prod/go/29_vc_vdc_group_resource.go deleted file mode 100644 index f5a5213..0000000 --- a/generated/prod/go/29_vc_vdc_group_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc_group -// Service ID: 29 - -var _ resource.Resource = &VcVdcGroupResource{} -var _ resource.ResourceWithModifyPlan = &VcVdcGroupResource{} -var _ resource.ResourceWithImportState = &VcVdcGroupResource{} - -type VcVdcGroupResource struct { - client *core.UniversalClient -} - -type VcVdcGroupModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - VdcUid types.String `tfsdk:"vdc_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVdcGroupResource() resource.Resource { - return &VcVdcGroupResource{} -} - -func (r *VcVdcGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc_group" -} - -func (r *VcVdcGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core) Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVdcGroupModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VdcUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - if config.VdcUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 618: resources_core.FormatString(config.VdcUid), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 29, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVdcGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 618: resources_core.FormatString(data.VdcUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 29, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 29, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVdcGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcGroupModel - var state VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 29, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVdcGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVdcGroupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/29_vc_vdc_group_vdc_resource.go b/generated/prod/go/29_vc_vdc_group_vdc_resource.go deleted file mode 100644 index 3e3e8ef..0000000 --- a/generated/prod/go/29_vc_vdc_group_vdc_resource.go +++ /dev/null @@ -1,326 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc_group -// Subresource: vdc - -var _ resource.Resource = &VcVdcGroupVdcResource{} - -// VcVdcGroupVdcResource manages a subresource via operations. -type VcVdcGroupVdcResource struct { - client *core.UniversalClient -} - -type VcVdcGroupVdcModel struct { - ID types.String `tfsdk:"id"` - VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewVcVdcGroupVdcResource() resource.Resource { - return &VcVdcGroupVdcResource{} -} - -func (r *VcVdcGroupVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc_group_vdc" -} - -func (r *VcVdcGroupVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "vc_vdc_group_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcGroupVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcVdcGroupVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("vdc", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcVdcGroupVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcGroupVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcVdcGroupVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.VcVdcGroupID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("vdc", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *VcVdcGroupVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/2_template_resource.go b/generated/prod/go/2_template_resource.go deleted file mode 100644 index a7a5bbb..0000000 --- a/generated/prod/go/2_template_resource.go +++ /dev/null @@ -1,397 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: template -// Service ID: 2 - -var _ resource.Resource = &TemplateResource{} -var _ resource.ResourceWithModifyPlan = &TemplateResource{} -var _ resource.ResourceWithImportState = &TemplateResource{} - -type TemplateResource struct { - client *core.UniversalClient -} - -// TemplateAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type TemplateAccessConfigurationModel struct { - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` - Type types.String `tfsdk:"type" json:"type"` - Domain types.String `tfsdk:"domain" json:"domain"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` -} - -// TemplateClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type TemplateClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// TemplateStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type TemplateStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type TemplateModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *TemplateAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *TemplateClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *TemplateStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewTemplateResource() resource.Resource { - return &TemplateResource{} -} - -func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_template" -} - -func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]")}, - "type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ingress")}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abracadabra-8htr2l6c")}, - "master_ip_space": schema.StringAttribute{Required: true}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]")}, - "slave_ip_space": schema.StringAttribute{Required: true}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(128)}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(5)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru")}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *TemplateResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *TemplateModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 813: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 814: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 815: resources_core.BuildJSON(map[string]string{"slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString()), "type": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 2, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *TemplateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 813: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 814: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 815: resources_core.BuildJSON(map[string]string{"slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString()), "type": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 2, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 2, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TemplateModel - var state TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 816: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 817: resources_core.BuildJSON(map[string]string{"type": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 2, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *TemplateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/50_nextcloud_backup_resource.go b/generated/prod/go/50_nextcloud_backup_resource.go deleted file mode 100644 index feb1f0e..0000000 --- a/generated/prod/go/50_nextcloud_backup_resource.go +++ /dev/null @@ -1,344 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Subresource: backup - -var _ resource.Resource = &NextcloudBackupResource{} - -// NextcloudBackupResource manages a subresource via operations. -type NextcloudBackupResource struct { - client *core.UniversalClient -} - -type NextcloudBackupModel struct { - ID types.String `tfsdk:"id"` - NextcloudID types.String `tfsdk:"nextcloud_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` -} - -func NewNextcloudBackupResource() resource.Resource { - return &NextcloudBackupResource{} -} - -func (r *NextcloudBackupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud_backup" -} - -func (r *NextcloudBackupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "nextcloud_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudBackupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan NextcloudBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - } - identityCodes := []string{ - "startupConfiguration", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudBackupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudBackupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudBackupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.NextcloudID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(state.StartupConfiguration), - } - identityCodes := []string{ - "startupConfiguration", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *NextcloudBackupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/50_nextcloud_resource.go b/generated/prod/go/50_nextcloud_resource.go deleted file mode 100644 index 94badb7..0000000 --- a/generated/prod/go/50_nextcloud_resource.go +++ /dev/null @@ -1,414 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Service ID: 50 - -var _ resource.Resource = &NextcloudResource{} -var _ resource.ResourceWithModifyPlan = &NextcloudResource{} -var _ resource.ResourceWithImportState = &NextcloudResource{} - -type NextcloudResource struct { - client *core.UniversalClient -} - -// NextcloudAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type NextcloudAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// NextcloudAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type NextcloudAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - DocumentServer types.String `tfsdk:"document_server" json:"documentServer"` - LogLevel types.String `tfsdk:"log_level" json:"logLevel"` -} - -// NextcloudClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type NextcloudClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// NextcloudStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type NextcloudStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` - PsqlEnabled types.Bool `tfsdk:"psql_enabled" json:"psqlEnabled"` - RedisEnabled types.Bool `tfsdk:"redis_enabled" json:"redisEnabled"` - PsqlUid types.String `tfsdk:"psql_uid" json:"psqlUid"` - RedisUid types.String `tfsdk:"redis_uid" json:"redisUid"` -} - -type NextcloudModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *NextcloudAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *NextcloudAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *NextcloudClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *NextcloudStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNextcloudResource() resource.Resource { - return &NextcloudResource{} -} - -func (r *NextcloudResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud" -} - -func (r *NextcloudResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nextcloud-f6pb9obd"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("33.0.4")}, - "document_server": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("collabora"), MarkdownDescription: "Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ"}, - "log_level": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("INFO"), MarkdownDescription: "Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - "psql_enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud"}, - "redis_enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud"}, - "psql_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета`"}, - "redis_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета`"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NextcloudModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1055: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString()), "psqlEnabled": fmt.Sprintf("%v", config.StartupConfiguration.PsqlEnabled.ValueBool()), "redisEnabled": fmt.Sprintf("%v", config.StartupConfiguration.RedisEnabled.ValueBool()), "psqlUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.PsqlUid.ValueString()), "redisUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.RedisUid.ValueString())}), - 1056: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1057: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1058: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", config.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", config.AppConfiguration.LogLevel.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 50, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NextcloudResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1055: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString()), "psqlEnabled": fmt.Sprintf("%v", data.StartupConfiguration.PsqlEnabled.ValueBool()), "redisEnabled": fmt.Sprintf("%v", data.StartupConfiguration.RedisEnabled.ValueBool()), "psqlUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.PsqlUid.ValueString()), "redisUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.RedisUid.ValueString())}), - 1056: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1057: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1058: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", data.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", data.AppConfiguration.LogLevel.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 50, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 50, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NextcloudResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudModel - var state NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1059: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1060: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", plan.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", plan.AppConfiguration.LogLevel.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 50, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NextcloudResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NextcloudResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/81_superset_resource.go b/generated/prod/go/81_superset_resource.go deleted file mode 100644 index 0e08f09..0000000 --- a/generated/prod/go/81_superset_resource.go +++ /dev/null @@ -1,436 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: superset -// Service ID: 81 - -var _ resource.Resource = &SupersetResource{} -var _ resource.ResourceWithModifyPlan = &SupersetResource{} -var _ resource.ResourceWithImportState = &SupersetResource{} - -type SupersetResource struct { - client *core.UniversalClient -} - -type SupersetModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - Emails types.String `tfsdk:"emails"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewSupersetResource() resource.Resource { - return &SupersetResource{} -} - -func (r *SupersetResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_superset" -} - -func (r *SupersetResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("superset-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "emails": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("testuser1"), MarkdownDescription: "Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *SupersetResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *SupersetModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Emails.IsNull() && !plan.Emails.IsUnknown() && !state.Emails.IsNull() && !state.Emails.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Emails.ValueString(), state.Emails.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить emails", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 14: resources_core.FormatString(config.Domain), - 20: resources_core.FormatString(config.Emails), - 96: resources_core.FormatInt64(config.ResourceCPU), - 97: resources_core.FormatInt64(config.ResourceMemory), - 98: resources_core.FormatInt64(config.ResourceDisk), - 109: resources_core.FormatString(config.ResourceRealm), - 227: resources_core.FormatInt64(config.ResourceInstances), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 81, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *SupersetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 14: resources_core.FormatString(data.Domain), - 20: resources_core.FormatString(data.Emails), - 96: resources_core.FormatInt64(data.ResourceCPU), - 97: resources_core.FormatInt64(data.ResourceMemory), - 98: resources_core.FormatInt64(data.ResourceDisk), - 109: resources_core.FormatString(data.ResourceRealm), - 227: resources_core.FormatInt64(data.ResourceInstances), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 81, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *SupersetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 81, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *SupersetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan SupersetModel - var state SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 81, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *SupersetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *SupersetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *SupersetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/82_harbor_resource.go b/generated/prod/go/82_harbor_resource.go deleted file mode 100644 index 63306e1..0000000 --- a/generated/prod/go/82_harbor_resource.go +++ /dev/null @@ -1,458 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: harbor -// Service ID: 82 - -var _ resource.Resource = &HarborResource{} -var _ resource.ResourceWithModifyPlan = &HarborResource{} -var _ resource.ResourceWithImportState = &HarborResource{} - -type HarborResource struct { - client *core.UniversalClient -} - -type HarborModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - S3Uid types.String `tfsdk:"s3_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewHarborResource() resource.Resource { - return &HarborResource{} -} - -func (r *HarborResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_harbor" -} - -func (r *HarborResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("harbor-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "s3_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID экземпляра услуги S3 Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *HarborResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *HarborModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.S3Uid.ValueString(), state.S3Uid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.S3Uid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") - return - } - if config.S3Uid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { - resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) - } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { - config.S3Uid = types.StringValue(resolvedS3Uid) - } - } - params := map[int]string{ - 16: resources_core.FormatString(config.Domain), - 110: resources_core.FormatString(config.ResourceRealm), - 228: resources_core.FormatInt64(config.ResourceCPU), - 229: resources_core.FormatInt64(config.ResourceMemory), - 230: resources_core.FormatInt64(config.ResourceInstances), - 231: resources_core.FormatString(config.S3Uid), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 82, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *HarborResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 16: resources_core.FormatString(data.Domain), - 110: resources_core.FormatString(data.ResourceRealm), - 228: resources_core.FormatInt64(data.ResourceCPU), - 229: resources_core.FormatInt64(data.ResourceMemory), - 230: resources_core.FormatInt64(data.ResourceInstances), - 231: resources_core.FormatString(data.S3Uid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 82, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HarborResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 82, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *HarborResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HarborModel - var state HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 82, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HarborResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *HarborResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *HarborResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/86_k8s_velero_resource.go b/generated/prod/go/86_k8s_velero_resource.go deleted file mode 100644 index 950e65b..0000000 --- a/generated/prod/go/86_k8s_velero_resource.go +++ /dev/null @@ -1,402 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_velero -// Service ID: 86 - -var _ resource.Resource = &K8sVeleroResource{} -var _ resource.ResourceWithModifyPlan = &K8sVeleroResource{} -var _ resource.ResourceWithImportState = &K8sVeleroResource{} - -type K8sVeleroResource struct { - client *core.UniversalClient -} - -// K8sVeleroBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type K8sVeleroBackupConfigurationModel struct { - S3BucketUid types.String `tfsdk:"s3_bucket_uid" json:"s3BucketUid"` -} - -// K8sVeleroClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sVeleroClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// K8sVeleroNodeAgentConfigurationModel — вложенная модель для map-fixed параметра nodeAgentConfiguration. -type K8sVeleroNodeAgentConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// K8sVeleroStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sVeleroStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type K8sVeleroModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - BackupConfiguration *K8sVeleroBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *K8sVeleroClusterConfigurationModel `tfsdk:"cluster_configuration"` - NodeAgentConfiguration *K8sVeleroNodeAgentConfigurationModel `tfsdk:"node_agent_configuration"` - StartupConfiguration *K8sVeleroStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sVeleroResource() resource.Resource { - return &K8sVeleroResource{} -} - -func (r *K8sVeleroResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_velero" -} - -func (r *K8sVeleroResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_bucket_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3-Бакета обязателен
Учётная запись будет создана автоматически, если её нету"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "node_agent_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru")}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sVeleroResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sVeleroModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 962: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 963: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 964: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.NodeAgentConfiguration.Memory.ValueInt64())}), - 1076: resources_core.BuildJSON(map[string]string{"s3BucketUid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3BucketUid.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 86, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sVeleroResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 86, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 962: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 963: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 964: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.NodeAgentConfiguration.Memory.ValueInt64())}), - 1076: resources_core.BuildJSON(map[string]string{"s3BucketUid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3BucketUid.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 86, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 86, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sVeleroResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 86, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sVeleroResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sVeleroModel - var state K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 965: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64())}), - 966: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", plan.NodeAgentConfiguration.Memory.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.NodeAgentConfiguration.Cpu.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 86, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sVeleroResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sVeleroResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sVeleroResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/88_k8s_ziti_controller_resource.go b/generated/prod/go/88_k8s_ziti_controller_resource.go deleted file mode 100644 index a3dd491..0000000 --- a/generated/prod/go/88_k8s_ziti_controller_resource.go +++ /dev/null @@ -1,420 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_ziti_controller -// Service ID: 88 - -var _ resource.Resource = &K8sZitiControllerResource{} -var _ resource.ResourceWithModifyPlan = &K8sZitiControllerResource{} -var _ resource.ResourceWithImportState = &K8sZitiControllerResource{} - -type K8sZitiControllerResource struct { - client *core.UniversalClient -} - -type K8sZitiControllerModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sZitiControllerResource() resource.Resource { - return &K8sZitiControllerResource{} -} - -func (r *K8sZitiControllerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_ziti_controller" -} - -func (r *K8sZitiControllerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8sZitiController-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores) Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sZitiControllerResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sZitiControllerModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 294: resources_core.FormatInt64(config.ResourceCPU), - 295: resources_core.FormatInt64(config.ResourceMemory), - 296: resources_core.FormatInt64(config.ResourceDisk), - 297: resources_core.FormatInt64(config.ResourceInstances), - 298: resources_core.FormatString(config.ResourceRealm), - 299: resources_core.FormatString(config.Domain), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 88, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sZitiControllerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 294: resources_core.FormatInt64(data.ResourceCPU), - 295: resources_core.FormatInt64(data.ResourceMemory), - 296: resources_core.FormatInt64(data.ResourceDisk), - 297: resources_core.FormatInt64(data.ResourceInstances), - 298: resources_core.FormatString(data.ResourceRealm), - 299: resources_core.FormatString(data.Domain), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 88, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sZitiControllerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 88, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sZitiControllerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sZitiControllerModel - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 88, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sZitiControllerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sZitiControllerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sZitiControllerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/89_flask_resource.go b/generated/prod/go/89_flask_resource.go deleted file mode 100644 index 0951b0b..0000000 --- a/generated/prod/go/89_flask_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: flask -// Service ID: 89 - -var _ resource.Resource = &FlaskResource{} -var _ resource.ResourceWithModifyPlan = &FlaskResource{} -var _ resource.ResourceWithImportState = &FlaskResource{} - -type FlaskResource struct { - client *core.UniversalClient -} - -// FlaskAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type FlaskAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// FlaskAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type FlaskAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// FlaskClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type FlaskClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// FlaskStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type FlaskStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type FlaskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *FlaskAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *FlaskAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *FlaskClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *FlaskStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewFlaskResource() resource.Resource { - return &FlaskResource{} -} - -func (r *FlaskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_flask" -} - -func (r *FlaskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("flask-whmcbvdz"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3.12")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/Foxyhhd/Baldurs-Gate-test.git"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *FlaskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *FlaskModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1037: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1038: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1039: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1040: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1041: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 89, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *FlaskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1037: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1038: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1039: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1040: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1041: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 89, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *FlaskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 89, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan FlaskModel - var state FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1042: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1043: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1044: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 89, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *FlaskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *FlaskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *FlaskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/90_postgres_backup_resource.go b/generated/prod/go/90_postgres_backup_resource.go deleted file mode 100644 index 957eb3e..0000000 --- a/generated/prod/go/90_postgres_backup_resource.go +++ /dev/null @@ -1,326 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: backup - -var _ resource.Resource = &PostgresBackupResource{} - -// PostgresBackupResource manages a subresource via operations. -type PostgresBackupResource struct { - client *core.UniversalClient -} - -type PostgresBackupModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewPostgresBackupResource() resource.Resource { - return &PostgresBackupResource{} -} - -func (r *PostgresBackupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_backup" -} - -func (r *PostgresBackupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresBackupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresBackupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresBackupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresBackupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresBackupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/90_postgres_database_resource.go b/generated/prod/go/90_postgres_database_resource.go deleted file mode 100644 index 7310720..0000000 --- a/generated/prod/go/90_postgres_database_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: database - -var _ resource.Resource = &PostgresDatabaseResource{} - -// PostgresDatabaseResource manages a subresource via operations. -type PostgresDatabaseResource struct { - client *core.UniversalClient -} - -type PostgresDatabaseModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` - DbOwner types.String `tfsdk:"db_owner"` -} - -func NewPostgresDatabaseResource() resource.Resource { - return &PostgresDatabaseResource{} -} - -func (r *PostgresDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_database" -} - -func (r *PostgresDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_owner": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.DbName.IsNull() || plan.DbName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") - return - } - if plan.DbOwner.IsNull() || plan.DbOwner.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_owner is required.") - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - } - identityCodes := []string{ - "dbName", - "dbOwner", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - "dbOwner": resources_core.FormatString(state.DbOwner), - } - identityCodes := []string{ - "dbName", - "dbOwner", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/90_postgres_resource.go b/generated/prod/go/90_postgres_resource.go deleted file mode 100644 index 6ef10dd..0000000 --- a/generated/prod/go/90_postgres_resource.go +++ /dev/null @@ -1,475 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Service ID: 90 - -var _ resource.Resource = &PostgresResource{} -var _ resource.ResourceWithModifyPlan = &PostgresResource{} -var _ resource.ResourceWithImportState = &PostgresResource{} - -type PostgresResource struct { - client *core.UniversalClient -} - -// PostgresAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type PostgresAccessConfigurationModel struct { - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// PostgresAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type PostgresAutoscaleConfigurationModel struct { - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` -} - -// PostgresBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type PostgresBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// PostgresClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type PostgresClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// PostgresPostgresConfigurationModel — вложенная модель для map-fixed параметра postgresConfiguration. -type PostgresPostgresConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - SslRequired types.Bool `tfsdk:"ssl_required" json:"sslRequired"` - PoolerMaster types.Bool `tfsdk:"pooler_master" json:"poolerMaster"` - PoolerSlave types.Bool `tfsdk:"pooler_slave" json:"poolerSlave"` -} - -// PostgresStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type PostgresStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type PostgresModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *PostgresAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *PostgresAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *PostgresBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *PostgresClusterConfigurationModel `tfsdk:"cluster_configuration"` - PostgresConf types.String `tfsdk:"postgres_conf"` - PostgresConfiguration *PostgresPostgresConfigurationModel `tfsdk:"postgres_configuration"` - StartupConfiguration *PostgresStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewPostgresResource() resource.Resource { - return &PostgresResource{} -} - -func (r *PostgresResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres" -} - -func (r *PostgresResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "slave_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "postgres_conf": schema.StringAttribute{Optional: true}, - "postgres_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("17")}, - "ssl_required": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "По умолчанию подключение обязательное (sslmode=require)"}, - "pooler_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi**"}, - "pooler_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi**"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *PostgresModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 788: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 789: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 790: resources_core.BuildJSON(map[string]string{"slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - 791: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", config.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", config.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", config.PostgresConfiguration.PoolerSlave.ValueBool())}), - 792: resources_core.FormatString(config.PostgresConf), - 793: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 794: resources_core.BuildJSON(map[string]string{"schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 90, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *PostgresResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 788: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 789: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 790: resources_core.BuildJSON(map[string]string{"slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - 791: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", data.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", data.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", data.PostgresConfiguration.PoolerSlave.ValueBool())}), - 792: resources_core.FormatString(data.PostgresConf), - 793: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 794: resources_core.BuildJSON(map[string]string{"schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 90, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 90, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *PostgresResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresModel - var state PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.PostgresConf.IsNull() != state.PostgresConf.IsNull() || plan.PostgresConf.IsUnknown() != state.PostgresConf.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.PostgresConf.IsNull() && !plan.PostgresConf.IsUnknown() && !state.PostgresConf.IsNull() && !state.PostgresConf.IsUnknown() { - if !strings.EqualFold(plan.PostgresConf.ValueString(), state.PostgresConf.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 795: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 796: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString())}), - 797: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", plan.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", plan.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", plan.PostgresConfiguration.PoolerSlave.ValueBool())}), - 798: resources_core.FormatString(plan.PostgresConf), - 799: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 800: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 90, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *PostgresResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *PostgresResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/90_postgres_user_resource.go b/generated/prod/go/90_postgres_user_resource.go deleted file mode 100644 index 5101eca..0000000 --- a/generated/prod/go/90_postgres_user_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: user - -var _ resource.Resource = &PostgresUserResource{} - -// PostgresUserResource manages a subresource via operations. -type PostgresUserResource struct { - client *core.UniversalClient -} - -type PostgresUserModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Role types.String `tfsdk:"role"` - Username types.String `tfsdk:"username"` -} - -func NewPostgresUserResource() resource.Resource { - return &PostgresUserResource{} -} - -func (r *PostgresUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_user" -} - -func (r *PostgresUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "role": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать роль из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Role.IsNull() || plan.Role.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "role is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "role": resources_core.FormatString(plan.Role), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "role", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "role": resources_core.FormatString(plan.Role), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "role": resources_core.FormatString(plan.Role), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "role": resources_core.FormatString(state.Role), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "role", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/91_redis_resource.go b/generated/prod/go/91_redis_resource.go deleted file mode 100644 index 852aaeb..0000000 --- a/generated/prod/go/91_redis_resource.go +++ /dev/null @@ -1,393 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: redis -// Service ID: 91 - -var _ resource.Resource = &RedisResource{} -var _ resource.ResourceWithModifyPlan = &RedisResource{} -var _ resource.ResourceWithImportState = &RedisResource{} - -type RedisResource struct { - client *core.UniversalClient -} - -// RedisAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type RedisAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` -} - -// RedisClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type RedisClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// RedisStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type RedisStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type RedisModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *RedisAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *RedisClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *RedisStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewRedisResource() resource.Resource { - return &RedisResource{} -} - -func (r *RedisResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_redis" -} - -func (r *RedisResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "slave_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *RedisResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *RedisModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1061: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1062: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1063: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 91, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *RedisResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1061: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1062: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1063: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 91, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RedisResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 91, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *RedisResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RedisModel - var state RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1064: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 1065: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 91, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RedisResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *RedisResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *RedisResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/92_mongodb_resource.go b/generated/prod/go/92_mongodb_resource.go deleted file mode 100644 index e4f0135..0000000 --- a/generated/prod/go/92_mongodb_resource.go +++ /dev/null @@ -1,495 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mongodb -// Service ID: 92 - -var _ resource.Resource = &MongodbResource{} -var _ resource.ResourceWithModifyPlan = &MongodbResource{} -var _ resource.ResourceWithImportState = &MongodbResource{} - -type MongodbResource struct { - client *core.UniversalClient -} - -type MongodbModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` - MasterAccessList types.String `tfsdk:"master_access_list"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewMongodbResource() resource.Resource { - return &MongodbResource{} -} - -func (r *MongodbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mongodb" -} - -func (r *MongodbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Список IP-адресов, с которых разрешён доступ. Если оставить пустым — доступ открыт всем.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "resource_c_p_u": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `1000`"}, - "resource_disk": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `20`"}, - "resource_instances": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2`"}, - "resource_memory": schema.Int64Attribute{Required: true, MarkdownDescription: "Пример: `2048`"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MongodbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *MongodbModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceCPU.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") - return - } - if config.ResourceCPU.IsUnknown() { - return - } - if config.ResourceDisk.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") - return - } - if config.ResourceDisk.IsUnknown() { - return - } - if config.ResourceInstances.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") - return - } - if config.ResourceInstances.IsUnknown() { - return - } - if config.ResourceMemory.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") - return - } - if config.ResourceMemory.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 487: resources_core.FormatInt64(config.ResourceInstances), - 588: resources_core.FormatInt64(config.ResourceMemory), - 589: resources_core.FormatInt64(config.ResourceCPU), - 590: resources_core.FormatInt64(config.ResourceDisk), - 595: resources_core.FormatString(config.ResourceRealm), - 646: resources_core.FormatString(config.IpSpaceNameMaster), - 889: resources_core.FormatString(config.MasterAccessList), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 92, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *MongodbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceCPU.IsNull() || data.ResourceCPU.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_c_p_u is required.") - return - } - if data.ResourceDisk.IsNull() || data.ResourceDisk.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_disk is required.") - return - } - if data.ResourceInstances.IsNull() || data.ResourceInstances.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_instances is required.") - return - } - if data.ResourceMemory.IsNull() || data.ResourceMemory.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_memory is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 487: resources_core.FormatInt64(data.ResourceInstances), - 588: resources_core.FormatInt64(data.ResourceMemory), - 589: resources_core.FormatInt64(data.ResourceCPU), - 590: resources_core.FormatInt64(data.ResourceDisk), - 595: resources_core.FormatString(data.ResourceRealm), - 646: resources_core.FormatString(data.IpSpaceNameMaster), - 889: resources_core.FormatString(data.MasterAccessList), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 92, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 92, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *MongodbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MongodbModel - var state MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceNameMaster.ValueString(), state.IpSpaceNameMaster.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MasterAccessList.IsNull() != state.MasterAccessList.IsNull() || plan.MasterAccessList.IsUnknown() != state.MasterAccessList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MasterAccessList.IsNull() && !plan.MasterAccessList.IsUnknown() && !state.MasterAccessList.IsNull() && !state.MasterAccessList.IsUnknown() { - if !strings.EqualFold(plan.MasterAccessList.ValueString(), state.MasterAccessList.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 648: resources_core.FormatInt64(plan.ResourceInstances), - 649: resources_core.FormatInt64(plan.ResourceMemory), - 650: resources_core.FormatInt64(plan.ResourceCPU), - 651: resources_core.FormatInt64(plan.ResourceDisk), - 653: resources_core.FormatString(plan.IpSpaceNameMaster), - 890: resources_core.FormatString(plan.MasterAccessList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 92, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "masterAccessList", Field: "MasterAccessList", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *MongodbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *MongodbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/92_mongodb_user_resource.go b/generated/prod/go/92_mongodb_user_resource.go deleted file mode 100644 index 6f0e40d..0000000 --- a/generated/prod/go/92_mongodb_user_resource.go +++ /dev/null @@ -1,364 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mongodb -// Subresource: user - -var _ resource.Resource = &MongodbUserResource{} - -// MongodbUserResource manages a subresource via operations. -type MongodbUserResource struct { - client *core.UniversalClient -} - -type MongodbUserModel struct { - ID types.String `tfsdk:"id"` - MongodbID types.String `tfsdk:"mongodb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` - Role types.String `tfsdk:"role"` - Username types.String `tfsdk:"username"` -} - -func NewMongodbUserResource() resource.Resource { - return &MongodbUserResource{} -} - -func (r *MongodbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mongodb_user" -} - -func (r *MongodbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mongodb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Required: true}, - "role": schema.StringAttribute{Required: true}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MongodbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MongodbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.DbName.IsNull() || plan.DbName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") - return - } - if plan.Role.IsNull() || plan.Role.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "role is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "role": resources_core.FormatString(plan.Role), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MongodbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MongodbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_user" == "" { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "role": resources_core.FormatString(plan.Role), - }) - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MongodbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MongodbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MongodbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/93_rabbitmq_resource.go b/generated/prod/go/93_rabbitmq_resource.go deleted file mode 100644 index c26d54b..0000000 --- a/generated/prod/go/93_rabbitmq_resource.go +++ /dev/null @@ -1,452 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: rabbitmq -// Service ID: 93 - -var _ resource.Resource = &RabbitmqResource{} -var _ resource.ResourceWithModifyPlan = &RabbitmqResource{} -var _ resource.ResourceWithImportState = &RabbitmqResource{} - -type RabbitmqResource struct { - client *core.UniversalClient -} - -type RabbitmqModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` - NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewRabbitmqResource() resource.Resource { - return &RabbitmqResource{} -} - -func (r *RabbitmqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_rabbitmq" -} - -func (r *RabbitmqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, - "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`"}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `2000`"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Пример: `20`"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1124), MarkdownDescription: "Пример: `2048`"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *RabbitmqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *RabbitmqModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 116: resources_core.FormatInt64(config.ResourceCPU), - 117: resources_core.FormatInt64(config.ResourceMemory), - 118: resources_core.FormatInt64(config.ResourceDisk), - 119: resources_core.FormatString(config.ResourceRealm), - 224: resources_core.FormatInt64(config.ResourceInstances), - 225: resources_core.FormatBool(config.NeedExternalAddressMaster), - 543: resources_core.FormatString(config.IpSpaceNameMaster), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 93, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *RabbitmqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 116: resources_core.FormatInt64(data.ResourceCPU), - 117: resources_core.FormatInt64(data.ResourceMemory), - 118: resources_core.FormatInt64(data.ResourceDisk), - 119: resources_core.FormatString(data.ResourceRealm), - 224: resources_core.FormatInt64(data.ResourceInstances), - 225: resources_core.FormatBool(data.NeedExternalAddressMaster), - 543: resources_core.FormatString(data.IpSpaceNameMaster), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 93, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RabbitmqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 93, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *RabbitmqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RabbitmqModel - var state RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { - if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceNameMaster.ValueString(), state.IpSpaceNameMaster.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 304: resources_core.FormatInt64(plan.ResourceCPU), - 305: resources_core.FormatInt64(plan.ResourceMemory), - 306: resources_core.FormatInt64(plan.ResourceDisk), - 307: resources_core.FormatInt64(plan.ResourceInstances), - 308: resources_core.FormatBool(plan.NeedExternalAddressMaster), - 545: resources_core.FormatString(plan.IpSpaceNameMaster), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 93, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RabbitmqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *RabbitmqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *RabbitmqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/94_lucee_resource.go b/generated/prod/go/94_lucee_resource.go deleted file mode 100644 index 34033fe..0000000 --- a/generated/prod/go/94_lucee_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: lucee -// Service ID: 94 - -var _ resource.Resource = &LuceeResource{} -var _ resource.ResourceWithModifyPlan = &LuceeResource{} -var _ resource.ResourceWithImportState = &LuceeResource{} - -type LuceeResource struct { - client *core.UniversalClient -} - -// LuceeAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type LuceeAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// LuceeAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type LuceeAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// LuceeClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type LuceeClusterConfigurationModel struct { - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.String `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// LuceeStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type LuceeStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type LuceeModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *LuceeAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *LuceeAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *LuceeClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *LuceeStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewLuceeResource() resource.Resource { - return &LuceeResource{} -} - -func (r *LuceeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_lucee" -} - -func (r *LuceeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("lucee-un0nzrwz"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("6.0")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testlucee"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("200"), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LuceeResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *LuceeModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1019: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1020: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.Cpu.ValueString()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64())}), - 1021: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1022: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1023: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 94, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *LuceeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1019: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1020: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.Cpu.ValueString()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64())}), - 1021: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1022: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1023: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 94, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LuceeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 94, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LuceeModel - var state LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1024: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("\"%s\"", plan.ClusterConfiguration.Cpu.ValueString()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 1025: resources_core.BuildJSON(map[string]string{"gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString()), "version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString())}), - 1026: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 94, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LuceeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *LuceeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *LuceeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/95_nodejs_resource.go b/generated/prod/go/95_nodejs_resource.go deleted file mode 100644 index b9f5f92..0000000 --- a/generated/prod/go/95_nodejs_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodejs -// Service ID: 95 - -var _ resource.Resource = &NodejsResource{} -var _ resource.ResourceWithModifyPlan = &NodejsResource{} -var _ resource.ResourceWithImportState = &NodejsResource{} - -type NodejsResource struct { - client *core.UniversalClient -} - -// NodejsAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type NodejsAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// NodejsAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type NodejsAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// NodejsClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type NodejsClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// NodejsStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type NodejsStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type NodejsModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *NodejsAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *NodejsAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *NodejsClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *NodejsStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNodejsResource() resource.Resource { - return &NodejsResource{} -} - -func (r *NodejsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodejs" -} - -func (r *NodejsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("node-mt4rik7o"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("23")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testnode.git"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NodejsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NodejsModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1028: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1029: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1030: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1031: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1032: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 95, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NodejsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1028: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1029: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1030: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1031: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1032: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 95, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NodejsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 95, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NodejsModel - var state NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1033: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1034: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1035: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 95, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NodejsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NodejsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NodejsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/96_pgadmin_resource.go b/generated/prod/go/96_pgadmin_resource.go deleted file mode 100644 index 7d7568f..0000000 --- a/generated/prod/go/96_pgadmin_resource.go +++ /dev/null @@ -1,504 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: pgadmin -// Service ID: 96 - -var _ resource.Resource = &PgadminResource{} -var _ resource.ResourceWithModifyPlan = &PgadminResource{} -var _ resource.ResourceWithImportState = &PgadminResource{} - -type PgadminResource struct { - client *core.UniversalClient -} - -type PgadminModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Domain types.String `tfsdk:"domain"` - Login types.String `tfsdk:"login"` - Password types.String `tfsdk:"password"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewPgadminResource() resource.Resource { - return &PgadminResource{} -} - -func (r *PgadminResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_pgadmin" -} - -func (r *PgadminResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("pgadmin-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "login": schema.StringAttribute{Required: true, MarkdownDescription: "Логин для подключения к pgAdmin, в формате email (login@email.net) Не изменяется после создания."}, - "password": schema.StringAttribute{Required: true, MarkdownDescription: "Пароль для подключения к pgAdmin Не изменяется после создания.", Sensitive: true}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores). 1000Mili = 1 Ядро"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PgadminResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *PgadminModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Login.IsNull() && !plan.Login.IsUnknown() && !state.Login.IsNull() && !state.Login.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Login.ValueString(), state.Login.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Password.IsNull() && !plan.Password.IsUnknown() && !state.Password.IsNull() && !state.Password.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Password.ValueString(), state.Password.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить password", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Login.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "login is required.") - return - } - if config.Login.IsUnknown() { - return - } - if config.Password.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "password is required.") - return - } - if config.Password.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 164: resources_core.FormatString(config.Domain), - 165: resources_core.FormatInt64(config.ResourceCPU), - 166: resources_core.FormatInt64(config.ResourceMemory), - 167: resources_core.FormatInt64(config.ResourceDisk), - 169: resources_core.FormatString(config.ResourceRealm), - 170: resources_core.FormatString(config.Login), - 171: resources_core.FormatString(config.Password), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 96, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *PgadminResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Login.IsNull() || data.Login.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "login is required.") - return - } - if data.Password.IsNull() || data.Password.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "password is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 164: resources_core.FormatString(data.Domain), - 165: resources_core.FormatInt64(data.ResourceCPU), - 166: resources_core.FormatInt64(data.ResourceMemory), - 167: resources_core.FormatInt64(data.ResourceDisk), - 169: resources_core.FormatString(data.ResourceRealm), - 170: resources_core.FormatString(data.Login), - 171: resources_core.FormatString(data.Password), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 96, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PgadminResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 96, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *PgadminResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PgadminModel - var state PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 172: resources_core.FormatInt64(plan.ResourceCPU), - 173: resources_core.FormatInt64(plan.ResourceMemory), - 174: resources_core.FormatInt64(plan.ResourceDisk), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 96, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PgadminResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *PgadminResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *PgadminResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/97_nodered_resource.go b/generated/prod/go/97_nodered_resource.go deleted file mode 100644 index 147bc59..0000000 --- a/generated/prod/go/97_nodered_resource.go +++ /dev/null @@ -1,420 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodered -// Service ID: 97 - -var _ resource.Resource = &NoderedResource{} -var _ resource.ResourceWithModifyPlan = &NoderedResource{} -var _ resource.ResourceWithImportState = &NoderedResource{} - -type NoderedResource struct { - client *core.UniversalClient -} - -type NoderedModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNoderedResource() resource.Resource { - return &NoderedResource{} -} - -func (r *NoderedResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodered" -} - -func (r *NoderedResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nodered-1"), MarkdownDescription: "Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NoderedResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NoderedModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 193: resources_core.FormatInt64(config.ResourceCPU), - 194: resources_core.FormatInt64(config.ResourceMemory), - 195: resources_core.FormatInt64(config.ResourceDisk), - 196: resources_core.FormatString(config.ResourceRealm), - 197: resources_core.FormatString(config.Domain), - 323: resources_core.FormatInt64(config.ResourceInstances), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 97, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NoderedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 193: resources_core.FormatInt64(data.ResourceCPU), - 194: resources_core.FormatInt64(data.ResourceMemory), - 195: resources_core.FormatInt64(data.ResourceDisk), - 196: resources_core.FormatString(data.ResourceRealm), - 197: resources_core.FormatString(data.Domain), - 323: resources_core.FormatInt64(data.ResourceInstances), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 97, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NoderedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 97, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NoderedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NoderedModel - var state NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 97, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NoderedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NoderedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NoderedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/98_http_resource.go b/generated/prod/go/98_http_resource.go deleted file mode 100644 index 1965e6d..0000000 --- a/generated/prod/go/98_http_resource.go +++ /dev/null @@ -1,498 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: http -// Service ID: 98 - -var _ resource.Resource = &HttpResource{} -var _ resource.ResourceWithModifyPlan = &HttpResource{} -var _ resource.ResourceWithImportState = &HttpResource{} - -type HttpResource struct { - client *core.UniversalClient -} - -type HttpModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Domain types.String `tfsdk:"domain"` - IngressAnnotations types.String `tfsdk:"ingress_annotations"` - JsonEnv types.String `tfsdk:"json_env"` - RegistryPath types.String `tfsdk:"registry_path"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewHttpResource() resource.Resource { - return &HttpResource{} -} - -func (r *HttpResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_http" -} - -func (r *HttpResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("http-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_annotations": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Формат ключ/значение
Поддерживает пока простые аннотации Не изменяется после создания."}, - "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Массив переменных, будут переданы в env приложения Не изменяется после создания."}, - "registry_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nexus-sa.tst.nubes.ru/docker-nubes/jolt"), MarkdownDescription: "Путь до image, который будет установлен как deployment Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili)"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8s-4-ext-nubes-ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *HttpResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *HttpModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.IngressAnnotations.IsNull() && !plan.IngressAnnotations.IsUnknown() && !state.IngressAnnotations.IsNull() && !state.IngressAnnotations.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IngressAnnotations.ValueString(), state.IngressAnnotations.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ingress_annotations", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить json_env", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RegistryPath.IsNull() && !plan.RegistryPath.IsUnknown() && !state.RegistryPath.IsNull() && !state.RegistryPath.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RegistryPath.ValueString(), state.RegistryPath.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить registry_path", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 212: resources_core.FormatString(config.Domain), - 213: resources_core.FormatInt64(config.ResourceCPU), - 214: resources_core.FormatInt64(config.ResourceMemory), - 215: resources_core.FormatInt64(config.ResourceInstances), - 216: resources_core.FormatString(config.ResourceRealm), - 217: resources_core.FormatString(config.RegistryPath), - 241: resources_core.FormatString(config.JsonEnv), - 782: resources_core.FormatString(config.IngressAnnotations), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 98, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 212: resources_core.FormatString(data.Domain), - 213: resources_core.FormatInt64(data.ResourceCPU), - 214: resources_core.FormatInt64(data.ResourceMemory), - 215: resources_core.FormatInt64(data.ResourceInstances), - 216: resources_core.FormatString(data.ResourceRealm), - 217: resources_core.FormatString(data.RegistryPath), - 241: resources_core.FormatString(data.JsonEnv), - 782: resources_core.FormatString(data.IngressAnnotations), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 98, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HttpResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 98, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *HttpResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HttpModel - var state HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 218: resources_core.FormatInt64(plan.ResourceCPU), - 219: resources_core.FormatInt64(plan.ResourceMemory), - 220: resources_core.FormatInt64(plan.ResourceInstances), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 98, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HttpResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *HttpResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *HttpResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/99_gitea_resource.go b/generated/prod/go/99_gitea_resource.go deleted file mode 100644 index ca019ef..0000000 --- a/generated/prod/go/99_gitea_resource.go +++ /dev/null @@ -1,579 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: gitea -// Service ID: 99 - -var _ resource.Resource = &GiteaResource{} -var _ resource.ResourceWithModifyPlan = &GiteaResource{} -var _ resource.ResourceWithImportState = &GiteaResource{} - -type GiteaResource struct { - client *core.UniversalClient -} - -type GiteaModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - IngressExtraAnnotations types.String `tfsdk:"ingress_extra_annotations"` - IniServerParams types.String `tfsdk:"ini_server_params"` - PsqlUid types.String `tfsdk:"psql_uid"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewGiteaResource() resource.Resource { - return &GiteaResource{} -} - -func (r *GiteaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_gitea" -} - -func (r *GiteaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("gitea-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_extra_annotations": schema.StringAttribute{Optional: true}, - "ini_server_params": schema.StringAttribute{Optional: true}, - "psql_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *GiteaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *GiteaModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.PsqlUid.ValueString(), state.PsqlUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить psql_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.PsqlUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") - return - } - if config.PsqlUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.PsqlUid.IsNull() && !config.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, config.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != config.PsqlUid.ValueString() { - config.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - params := map[int]string{ - 232: resources_core.FormatInt64(config.ResourceCPU), - 233: resources_core.FormatInt64(config.ResourceMemory), - 234: resources_core.FormatInt64(config.ResourceDisk), - 235: resources_core.FormatInt64(config.ResourceInstances), - 237: resources_core.FormatString(config.Domain), - 238: resources_core.FormatString(config.PsqlUid), - 818: resources_core.FormatString(config.IngressExtraAnnotations), - 821: resources_core.FormatString(config.IniServerParams), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 99, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *GiteaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.PsqlUid.IsNull() || data.PsqlUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalPsqlUid := data.PsqlUid - if !data.PsqlUid.IsNull() && !data.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, data.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedPsqlUid != "" && resolvedPsqlUid != data.PsqlUid.ValueString() { - data.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 232: resources_core.FormatInt64(data.ResourceCPU), - 233: resources_core.FormatInt64(data.ResourceMemory), - 234: resources_core.FormatInt64(data.ResourceDisk), - 235: resources_core.FormatInt64(data.ResourceInstances), - 237: resources_core.FormatString(data.Domain), - 238: resources_core.FormatString(data.PsqlUid), - 818: resources_core.FormatString(data.IngressExtraAnnotations), - 821: resources_core.FormatString(data.IniServerParams), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 99, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { - state.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalPsqlUid.IsNull() && !originalPsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - if strings.EqualFold(state.PsqlUid.ValueString(), originalPsqlUid.ValueString()) { - state.PsqlUid = originalPsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 99, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, newState.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != newState.PsqlUid.ValueString() { - newState.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() && !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { - if strings.EqualFold(newState.PsqlUid.ValueString(), state.PsqlUid.ValueString()) { - newState.PsqlUid = state.PsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *GiteaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan GiteaModel - var state GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IngressExtraAnnotations.IsNull() != state.IngressExtraAnnotations.IsNull() || plan.IngressExtraAnnotations.IsUnknown() != state.IngressExtraAnnotations.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IngressExtraAnnotations.IsNull() && !plan.IngressExtraAnnotations.IsUnknown() && !state.IngressExtraAnnotations.IsNull() && !state.IngressExtraAnnotations.IsUnknown() { - if !strings.EqualFold(plan.IngressExtraAnnotations.ValueString(), state.IngressExtraAnnotations.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IniServerParams.IsNull() != state.IniServerParams.IsNull() || plan.IniServerParams.IsUnknown() != state.IniServerParams.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IniServerParams.IsNull() && !plan.IniServerParams.IsUnknown() && !state.IniServerParams.IsNull() && !state.IniServerParams.IsUnknown() { - if !strings.EqualFold(plan.IniServerParams.ValueString(), state.IniServerParams.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 259: resources_core.FormatInt64(plan.ResourceCPU), - 260: resources_core.FormatInt64(plan.ResourceMemory), - 261: resources_core.FormatInt64(plan.ResourceDisk), - 262: resources_core.FormatInt64(plan.ResourceInstances), - 819: resources_core.FormatString(plan.IngressExtraAnnotations), - 820: resources_core.FormatString(plan.IniServerParams), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 99, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { - state.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - if strings.EqualFold(state.PsqlUid.ValueString(), plan.PsqlUid.ValueString()) { - state.PsqlUid = plan.PsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *GiteaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *GiteaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/prod/go/registry.go b/generated/prod/go/registry.go deleted file mode 100644 index de4c11b..0000000 --- a/generated/prod/go/registry.go +++ /dev/null @@ -1,73 +0,0 @@ -package resources_gen - -import "github.com/hashicorp/terraform-plugin-framework/resource" - -// Code generated by tools/gen_v2. DO NOT EDIT. -func AllResources() []func() resource.Resource { - return []func() resource.Resource{ - NewAkhqResource, - NewClickhouseResource, - NewDnsrecordResource, - NewDnszoneResource, - NewDummyResource, - NewFlaskResource, - NewGiteaResource, - NewGiteaComplexResource, - NewHarborResource, - NewHttpResource, - NewK8sSthutrvalClusterResource, - NewK8sVeleroResource, - NewK8sZitiControllerResource, - NewKafkaResource, - NewLlmAiResource, - NewLuceeResource, - NewMariadbResource, - NewMongodbResource, - NewNextcloudResource, - NewNifiResource, - NewNodejsResource, - NewNoderedResource, - NewOpenwhiskResource, - NewPgadminResource, - NewPostgresResource, - NewRabbitmqResource, - NewRedisResource, - NewS3Resource, - NewS3bucketResource, - NewSupersetResource, - NewTemplateResource, - NewTenantResource, - NewValoTenantResource, - NewVappResource, - NewVcComplexResource, - NewVcMgmtSthutrvalClusterResource, - NewVcNsxtResource, - NewVcOrgResource, - NewVcOrgSaasResource, - NewVcVdcResource, - NewVcVdcGroupResource, - NewVcVmResource, - NewVcVmV2Resource, - NewVcVmV3Resource, - NewVcexternalipResource, - NewZonesV2Resource, - NewClickhouseDatabaseResource, - NewClickhouseUserResource, - NewK8sSthutrvalClusterUserResource, - NewKafkaTopicResource, - NewKafkaUserResource, - NewLlmAiSecretsResource, - NewLlmAiTeamResource, - NewMariadbDatabaseResource, - NewMariadbUserResource, - NewMongodbUserResource, - NewNextcloudBackupResource, - NewPostgresBackupResource, - NewPostgresDatabaseResource, - NewPostgresUserResource, - NewS3SubUserResource, - NewVcOrgUserResource, - NewVcVdcGroupVdcResource, - NewZonesV2RecordResource, - } -} diff --git a/generated/prod/resources_yaml/100_openwhisk.yaml b/generated/prod/resources_yaml/100_openwhisk.yaml deleted file mode 100644 index ec17796..0000000 --- a/generated/prod/resources_yaml/100_openwhisk.yaml +++ /dev/null @@ -1,48 +0,0 @@ -name: openwhisk -service_id: 100 -service_display_name: Serverless Openwhisk (openwhisk_tenant) -service_short_name: openwhisk -service_man: Добавление и удаления пользователя для работы с Openwhisk -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 41 - kind: instance - action: create - params: - - id: 64 - code: username - data_type: string - required: true - descr: Имя пользователя Openwhisk - - name: delete - id: 42 - kind: instance - action: delete - params: - - id: 65 - code: username - data_type: string - required: true - descr: Имя пользователя Openwhisk diff --git a/generated/prod/resources_yaml/109_zones_v2.yaml b/generated/prod/resources_yaml/109_zones_v2.yaml deleted file mode 100644 index 5919d79..0000000 --- a/generated/prod/resources_yaml/109_zones_v2.yaml +++ /dev/null @@ -1,264 +0,0 @@ -name: zones_v2 -service_id: 109 -service_display_name: Управление DNS -service_short_name: zonesV2 -service_man: '# Управление DNS

## Что делает сервис

Управляет DNS-зоной вашего домена в инфраструктуре `ngcloud.ru`.
Сервис помогает:

- создать DNS-зону;
- поддерживать записи в зоне (добавлять, удалять, массово менять);
- удалять зону;
- синхронизировать данные зоны с фактическим состоянием на DNS-серверах.

Сервис работает с авторитетными DNS-серверами зоны:

- `ns3.ngcloud.ru`
- `185.247.187.83`
- `2a10:aa80:0:5::3`
- `ns4.ngcloud.ru`
- `81.22.46.43`
- `2a10:aa80:0:6::3`

## Очень важно: что сделать у регистратора домена

Чтобы зона действительно начала обслуживаться, **нужно делегировать домен** у регистратора на авторитетные DNS-серверы:

- `ns3.ngcloud.ru`
- `ns4.ngcloud.ru`

В панели регистратора нужно заменить NS-серверы домена на значения выше.
Без этого записи в зоне могут быть созданы, но интернет не будет использовать их для вашего домена.

## Проверка после настройки

Проверить ответы от серверов зоны:

```bash
dig <ваш_домен> @ns3.ngcloud.ru
dig <ваш_домен> @ns4.ngcloud.ru
```

Проверить, что делегирование у регистратора применилось:

```bash
dig <ваш_домен> NS
```

В ответе должны быть `ns3.ngcloud.ru` и `ns4.ngcloud.ru`.
' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 254 - kind: instance - action: create - man: '# Операция `create` (создание DNS-зоны)

## Что делает операция

`create` создает новую DNS-зону на авторитетных серверах сервиса.
После создания зона закрепляется за пользователем, который запустил создание инстанса.

## Когда использовать

- подключаете домен к сервису впервые;
- нужна новая отдельная зона.

## Важное условие после создания

У регистратора домена нужно делегировать зону на:

- `ns3.ngcloud.ru`
- `185.247.187.83`
- `2a10:aa80:0:5::3`
- `ns4.ngcloud.ru`
- `81.22.46.43`
- `2a10:aa80:0:6::3`

Если этого не сделать, зона будет создана технически, но в интернете домен не начнет работать через эти DNS.

## Объяснение параметра `Типа зоны`

### `NATIVE`

Это основная (ведущая) зона в нашем сервисе.
Вы управляете записями здесь, и именно эта зона считается источником правды.

### `SLAVE`

Это копия зоны, которая подтягивает данные с внешнего DNS-сервера.
Такой режим нужен, когда истинный источник записей находится не в нашем сервисе, а на другом DNS.

## Что проверяет операция

- что такая зона еще не существует на целевых DNS;
- корректность входных параметров зоны;
- ограничения на служебные/запрещенные имена зон.' - params: - - id: 761 - code: zoneName - data_type: string - required: true - regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ - maxlength: 64 - minlength: 1 - descr: Имя зоны - sort: 20 - - id: 762 - code: zoneKind - data_type: string - required: true - default: NATIVE - value_list: - - NATIVE - - SLAVE - descr: Тип зоны - sort: 30 - - id: 763 - code: zoneMastersForSlave - data_type: string - required: false - descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов - man: Пример:
**8.8.8.8 8.8.4.4** - sort: 40 - - id: 764 - code: zoneRefresh - data_type: integer > 0 - required: true - default: "86400" - minvalue: 600 - descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов - sort: 50 - - id: 765 - code: zoneRetry - data_type: integer > 0 - required: true - default: "7200" - minvalue: 600 - descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов - sort: 60 - - id: 766 - code: zoneExpire - data_type: integer > 0 - required: true - default: "3600000" - minvalue: 3600 - descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным - sort: 70 - - id: 767 - code: zoneTTL - data_type: integer > 0 - required: true - default: "86400" - minvalue: 600 - descr: Время, в течение которого информация будет кешироваться другими DNS-серверами - sort: 80 - - id: 768 - code: resourceRealm - data_type: string - required: true - default: powerdns - value_list: - - powerdns - func: getAvailableResourceRealms - sort: 10 - - name: create_record - id: 260 - kind: subresource - action: create - subresource: record - man: '# Операция `create_record` (добавление одной записи)

## Что делает операция

`create_record` точечно добавляет одну DNS-запись в существующую зону.
Если аналогичная запись уже есть, итоговый набор дополняется в рамках логики типа/имени.

## Когда использовать

- нужно добавить одну новую запись без массовой переработки зоны;
- нужно быстро внести точечное изменение.
' - params: - - id: 777 - code: recordName - data_type: string - required: true - regex: ^([a-zA-Z0-9\-_]+\.)*[a-zA-Z0-9\-_]+$|^(\d{1,3}\.){3}\d{1,3}$|^.+$ - unique_scope: parent - maxlength: 64 - minlength: 1 - man: Имя зоны в конце проставляется автоматически - sort: 10 - - id: 778 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - maxvalue: 84000 - minvalue: 30 - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 20 - - id: 779 - code: recordType - data_type: string - required: true - default: A - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - sort: 30 - - id: 780 - code: recordInput - data_type: string - required: true - man: Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - sort: 40 - - name: delete - id: 255 - kind: instance - action: delete - man: '# Операция `delete` (удаление зоны)

## Что делает операция

`delete` удаляет DNS-зону целиком вместе со всеми записями.

## Когда использовать

- зона больше не нужна;
- домен выводится из эксплуатации в рамках сервиса.

## Важная логика

- удаляется вся зона, а не отдельные записи;
- перед удалением стоит проверить, что домен действительно не используется.
' - params: [] - - name: delete_record - id: 261 - kind: subresource - action: delete - subresource: record - man: '# Операция `delete_record` (удаление записей по имени)

## Что делает операция

`delete_record` удаляет все записи с указанным именем (`recordName`), независимо от типа (`A`, `TXT`, `CNAME` и т.д.).

## Когда использовать

- нужно полностью убрать конкретное имя из зоны;
- требуется быстро очистить все варианты записи для одного имени.

## Важная логика

- удаляются все типы записей для выбранного имени;
- если нужно удалить только один тип записи, лучше использовать `modify_record` с нужным целевым состоянием.
' - params: - - id: 781 - code: recordName - data_type: string - required: true - - name: modify - id: 256 - kind: instance - action: modify - man: '# Операция `modify` (массовое изменение записей зоны)

## Что делает операция

`modify` обновляет записи зоны целиком на основе переданного массива `records`.
Это операция "приведения к целевому состоянию": система берет входной набор и применяет его на DNS-серверах.

## Когда использовать

- нужно массово обновить зону;
- нужно заменить сразу несколько записей одним запуском;
- нужно синхронно привести зону к заранее подготовленному списку записей.

## Важная логика

- запись группируется по `(имя, тип, TTL)`;
- операция ориентирована на полное обновление набора, а не на "точечную мелкую правку".

Для точечных изменений одной записи обычно удобнее `create_record` или `modify_record`.
' - params: - - id: 769 - code: records - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 87 - code: recordInput - data_type: string - required: true - default: "" - man: Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - is_modifiable: false - - id: 86 - code: recordName - data_type: string - required: true - default: "" - regex: ^([a-zA-Z0-9\-_]+\.)*[a-zA-Z0-9\-_]+$|^(\d{1,3}\.){3}\d{1,3}$|^.+$ - man: Имя зоны в конце проставляется автоматически - is_modifiable: false - - id: 88 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - is_modifiable: false - - id: 85 - code: recordType - data_type: string - required: true - default: A - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - - CAA - is_modifiable: false - - name: modify_record - id: 262 - kind: subresource - action: modify - subresource: record - man: '# Операция `modify_record` (изменение конкретной записи)

## Что делает операция

`modify_record` заменяет записи с указанным именем одной новой записью заданного типа и значения.

## Когда использовать

- нужно исправить одну конкретную запись;
- нужно заменить старые значения записи на новое целевое значение.

## Важная логика

- существующие записи выбранного имени очищаются;
- затем добавляется одна новая запись с переданными параметрами.
' - params: - - id: 783 - code: recordName - data_type: string - required: true - sort: 10 - - id: 784 - code: recordTTL - data_type: integer > 0 - required: true - maxvalue: 84000 - minvalue: 30 - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 20 - - id: 785 - code: recordType - data_type: string - required: true - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - sort: 30 - - id: 786 - code: recordInput - data_type: string - required: true - man: Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - sort: 40 - - name: reconcile - id: 259 - kind: action - action: reconcile - man: '# Операция `reconcile` (актуализация состояния зоны)

## Что делает операция

`reconcile` подтягивает актуальное фактическое состояние DNS-зоны и синхронизирует его с данными инстанса.
Операция техническая: она нужна для выравнивания состояния, а не для бизнес-изменений записей.

## Когда использовать

- записи менялись вручную на DNS-серверах;
- нужно обновить схему/состояние инстанса в личном кабинете;
- нужно импортировать текущие записи в управляемое состояние.

## Важная логика

- актуализируется список записей в данных инстанса;
- стандартно ничего "нового" для клиента не добавляет и не удаляет само по себе, а приводит данные к реальности.
' - params: [] diff --git a/generated/prod/resources_yaml/110_dnszone.yaml b/generated/prod/resources_yaml/110_dnszone.yaml deleted file mode 100644 index de8d32c..0000000 --- a/generated/prod/resources_yaml/110_dnszone.yaml +++ /dev/null @@ -1,97 +0,0 @@ -name: dnszone -service_id: 110 -service_display_name: DNS зона -service_short_name: dnszone -service_man: '## ![image](https://img.icons8.com/?size=50&id=41400&format=png) Необходимо делегировать зону у регистратора на Авторитетные DNS (Сервера зон)

ns3.ngcloud.ru
``` bash
185.247.187.83
2a10:aa80:0:5::3
```

ns4.ngcloud.ru
``` bash
81.22.46.43
2a10:aa80:0:6::3
```' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 43 - kind: instance - action: create - params: - - id: 66 - code: zoneEmail - data_type: string - required: true - descr: Контактный e-mail - - id: 67 - code: zoneName - data_type: string - required: true - regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ - descr: Имя зоны - man: Имя зоны - - id: 68 - code: zoneKind - data_type: string - required: true - default: NATIVE - value_list: - - NATIVE - - SLAVE - descr: Тип зоны - - id: 69 - code: zoneMastersForSlave - data_type: string - required: false - descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов - - id: 70 - code: zoneTimeToRefresh - data_type: string - required: true - default: "86400" - descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов - - id: 71 - code: zoneTimeToRetry - data_type: string - required: true - default: "7200" - descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов - - id: 72 - code: zoneTimeToExpire - data_type: string - required: true - default: "3600000" - descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным - - id: 73 - code: zoneTimeToLive - data_type: string - required: true - default: "86400" - descr: Время, в течение которого информация будет кешироваться другими DNS-серверами - - id: 284 - code: resourceRealm - data_type: string - required: true - default: powerdns - value_list: - - powerdns - func: getAvailableResourceRealms - descr: Платформа для развертывания - - name: delete - id: 44 - kind: instance - action: delete - params: [] diff --git a/generated/prod/resources_yaml/111_dnsrecord.yaml b/generated/prod/resources_yaml/111_dnsrecord.yaml deleted file mode 100644 index d87a3c1..0000000 --- a/generated/prod/resources_yaml/111_dnsrecord.yaml +++ /dev/null @@ -1,105 +0,0 @@ -name: dnsrecord -service_id: 111 -service_display_name: DNS запись -service_short_name: dnsrecord -service_man: Необходима DNS зона, в которой будет создана DNS запись.
Выберите
* Зону в которой будет создана запись
* Тип записи
* Имя записи
* Значение записи -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 45 - kind: instance - action: create - params: - - id: 75 - code: zoneUid - data_type: uuid - required: true - ref_svc_id: 109 - descr: UUID экземпляра услуги DNS-зоны - man: UUID экземпляра услуги DNS-зоны - sort: 10 - - id: 76 - code: recordType - data_type: string - required: true - value_list: - - A - - AAAA - - CNAME - - MX - - PTR - - SOA - - SRV - - TXT - - LUA - - NS - - SSHFP - descr: Тип DNS-записи - man: Тип DNS-записи - sort: 20 - - id: 77 - code: recordName - data_type: string - required: true - unique_scope: parent - descr: Имя записи. Имя зоны в конце проставляется автоматически - man: Имя записи. Имя зоны в конце проставляется автоматически - sort: 30 - - id: 78 - code: recordInput - data_type: string - required: true - descr: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - man: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - sort: 40 - is_modifiable: true - - id: 450 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - maxvalue: 604800 - minvalue: 30 - descr: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 50 - - name: delete - id: 46 - kind: instance - action: delete - params: [] - - name: modify - id: 90 - kind: instance - action: modify - params: - - id: 256 - code: recordInput - data_type: string - required: true - descr: Запись. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - - name: reconcile - id: 408 - kind: action - action: reconcile - params: [] diff --git a/generated/prod/resources_yaml/112_tenant.yaml b/generated/prod/resources_yaml/112_tenant.yaml deleted file mode 100644 index 89bc4a6..0000000 --- a/generated/prod/resources_yaml/112_tenant.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: tenant -service_id: 112 -service_display_name: Тенант в Grafana -service_short_name: tenant -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 84 - kind: instance - action: create - params: - - id: 239 - code: grafanaOrgName - data_type: string - required: true - regex: ^WZ[\d]+$ - descr: WZ-Код клиента - sort: 10 - - id: 240 - code: resourceRealm - data_type: string - required: true - default: grafana.ngcloud.ru - value_list: - - grafana.ngcloud.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 20 - - name: delete - id: 86 - kind: instance - action: delete - params: [] - - name: modify - id: 87 - kind: instance - action: modify - man: Просто удаляет все текущие датасоурсы и ставит их заново
Полезно, когда нужно "ресетнуть" / "добавить" - params: [] diff --git a/generated/prod/resources_yaml/113_vc_complex.yaml b/generated/prod/resources_yaml/113_vc_complex.yaml deleted file mode 100644 index 5a15864..0000000 --- a/generated/prod/resources_yaml/113_vc_complex.yaml +++ /dev/null @@ -1,158 +0,0 @@ -name: vc_complex -service_id: 113 -service_display_name: Быстрый старт -service_short_name: vcComplex -service_man: '# Создает окружение для ВМ.

## Создает сервисы:
**Организация**
**Виртуальный датацентр**
**Каталог**
**Сетевой шлюз периметра**

' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 107 - kind: instance - action: create - params: - - id: 332 - code: resourceRealm - data_type: string - required: true - default: ngcloud.ru - value_list: - - ngcloud.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы - sort: 1 - - id: 334 - code: vappName - data_type: string - required: true - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: Имя vApp - sort: 10 - - id: 358 - code: vdcProviderGateway - data_type: string - required: true - default: sandbox-v1cl1-pvdc - descr: Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String - sort: 200 - - id: 359 - code: virtualServicesCount - required: true - default: "1" - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 - sort: 110 - - id: 360 - code: ipSpaceName - data_type: string - required: false - default: internet-ipv4-v1 - descr: Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT - sort: 130 - - id: 399 - code: storageConfig - data_type: json - required: true - default: '[{"name": "", "size": 1024}]' - descr: Описывается как массив объектов, где описывается имя политики и её размер - sort: 210 - - id: 400 - code: vdcNetworkPool - data_type: string - required: true - default: nsxt-sandbox-geneve-np - sort: 120 - - id: 401 - code: cpuGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - sort: 20 - - id: 402 - code: memGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - sort: 30 - - id: 403 - code: segroupName - data_type: string - required: false - default: SEGROUP-SANDBOX-CL1-SHARED-01 - descr: Наименование segroup, в котором будут выделяться пулы на AVI - sort: 100 - - id: 404 - code: needExternalAddressSNAT - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Выделяет внешний IP на Edge. Используется для SNAT всего трафика - sort: 40 - - id: 405 - code: needEnableAVI - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Включает AVI в Cloud Director - sort: 50 - - id: 566 - code: cpuAllocated - data_type: string - required: true - default: "1" - minvalue: 1 - descr: шт - sort: 21 - - id: 567 - code: memAllocated - data_type: integer > 0 - required: true - default: "10" - minvalue: 1 - descr: gb - sort: 31 - - id: 568 - code: vIPCount - data_type: integer > 0 - required: true - default: "0" - maxvalue: 20 - minvalue: 0 - descr: ш - sort: 131 diff --git a/generated/prod/resources_yaml/114_gitea_complex.yaml b/generated/prod/resources_yaml/114_gitea_complex.yaml deleted file mode 100644 index b18075e..0000000 --- a/generated/prod/resources_yaml/114_gitea_complex.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: gitea_complex -service_id: 114 -service_display_name: Комплексная услуга по созданию gitea -service_short_name: GiteaComplex -service_man: Комплексная услуга из ["gittea", "postgres"] -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: [] diff --git a/generated/prod/resources_yaml/115_mariadb.yaml b/generated/prod/resources_yaml/115_mariadb.yaml deleted file mode 100644 index 86afc95..0000000 --- a/generated/prod/resources_yaml/115_mariadb.yaml +++ /dev/null @@ -1,457 +0,0 @@ -name: mariadb -service_id: 115 -service_display_name: Mariadb -service_short_name: mariadb -service_man: 'Mariadb кластер. Ресурсная платформа: k8s. Автоматизация: k8s operator.
Автоматическое резервное копирование, ручное восстановление.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 138 - kind: instance - action: create - params: - - id: 877 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 242 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 878 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 245 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - man: Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать - is_modifiable: false - - id: 246 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 243 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 244 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 879 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 247 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 248 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 880 - code: mariadbConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 249 - code: version - data_type: string - required: true - default: 9.4.0 - value_list: - - 8.4.0 - - 9.4.0 - is_modifiable: false - - id: 881 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - is_modifiable: true - sub_params: - - id: 250 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 251 - code: retain - data_type: integer > 0 - required: true - default: "720" - is_modifiable: false - - id: 252 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 882 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 253 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 254 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 255 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 256 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - name: create_database - id: 242 - kind: subresource - action: create - subresource: database - params: - - id: 734 - code: dbName - data_type: string - required: true - default: db-zs0q9wuv - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: 'Пример: `db01`' - - name: create_user - id: 243 - kind: subresource - action: create - subresource: user - params: - - id: 735 - code: username - data_type: string - required: true - default: usertigstlyt - regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ - maxlength: 62 - minlength: 2 - descr: 'Пример: `myusername`' - sort: 10 - - id: 736 - code: password - data_type: string - required: true - default: abpwcb75 - maxlength: 62 - minlength: 8 - descr: Секрет будет записан в хранилище секретов и не будет отображаться публично - sort: 20 - is_sensitive: true - - id: 737 - code: dbName - data_type: array-map-fixed - required: true - default: '[{}]' - descr: Необходимо указать массив существующих баз - man: Если базы не существует - при выполнении операции вернётся ошибка - sort: 30 - sub_params: - - id: 54 - code: dbname - data_type: string - required: true - default: "" - is_modifiable: false - - id: 738 - code: accessHosts - data_type: string - required: true - default: '%' - descr: 'Пример: `%`' - sort: 40 - - name: delete - id: 139 - kind: instance - action: delete - params: - - id: 456 - code: deleteS3Bucket - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Также удалить технический S3 Bucket с бэкапами? - sort: 10 - - name: delete_database - id: 248 - kind: subresource - action: delete - subresource: database - params: - - id: 745 - code: dbName - data_type: string - required: true - descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ - - name: delete_user - id: 247 - kind: subresource - action: delete - subresource: user - params: - - id: 744 - code: username - data_type: string - required: true - descr: Имя пользователя, созданное через операцию `create_user` - - name: modify - id: 142 - kind: instance - action: modify - params: - - id: 883 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 257 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 258 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 259 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - man: Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать - is_modifiable: false - - id: 260 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 884 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 261 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 262 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 885 - code: mariadbConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 263 - code: version - data_type: string - required: true - default: "" - value_list: - - 8.4.0 - - 9.4.0 - is_modifiable: false - - id: 886 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 265 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 266 - code: schedule - data_type: string - required: true - default: "" - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 264 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 887 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 267 - code: enabled - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 268 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 269 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 270 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - name: reconcile - id: 287 - kind: action - action: reconcile - params: [] - - name: resume - id: 141 - kind: instance - action: resume - params: [] - - name: suspend - id: 140 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/116_kafka.yaml b/generated/prod/resources_yaml/116_kafka.yaml deleted file mode 100644 index bdac0cb..0000000 --- a/generated/prod/resources_yaml/116_kafka.yaml +++ /dev/null @@ -1,334 +0,0 @@ -name: kafka -service_id: 116 -service_display_name: ApacheKafka -service_short_name: kafka -service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Идентификатор организации
Уникальный идентификатор созданной организации. Используется для всех дальнейших API-операций.

### Статус организации
Отражает текущее состояние: active, suspended, modifying, deleting.

### Параметры сети
Выходные данные при наличии ipSpaces: список выделенных внешних IP, имя ip_space, количество IP.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)**;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к этой организации.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.

' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 148 - kind: instance - action: create - params: - - id: 1084 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 579 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1085 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 580 - code: cpu - data_type: integer > 0 - required: true - default: "2000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 581 - code: memory - data_type: integer > 0 - required: true - default: "2048" - is_modifiable: false - - id: 582 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 583 - code: disk - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 1086 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 584 - code: allocationExternal - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - is_modifiable: false - - id: 585 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: create_topic - id: 165 - kind: subresource - action: create - subresource: topic - man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' - params: - - id: 523 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`' - sort: 10 - - id: 524 - code: partitions - data_type: integer > 0 - required: true - descr: Число партиций можно увеличить, но уменьшить их нельзя. - - id: 525 - code: replicas - data_type: integer > 0 - required: true - descr: Количество реплик не может привышать количество реплик брокера - sort: 20 - - name: create_user - id: 168 - kind: subresource - action: create - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 530 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - id: 531 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' - - id: 532 - code: operations - data_type: array - required: true - value_list: - - Create - - Describe - - Read - - Write - descr: 'Пример: `Create, Describe, Read, Write`' - - id: 533 - code: group - data_type: string - required: true - default: '*' - descr: 'Пример: `order-consumers` или `*`' - - id: 534 - code: accessHosts - data_type: string - required: true - default: '*' - descr: 'Пример: `192.168.1.0/24` или `*`' - - name: delete - id: 149 - kind: instance - action: delete - params: [] - - name: delete_topic - id: 167 - kind: subresource - action: delete - subresource: topic - params: - - id: 529 - code: nameTopic - data_type: string - required: true - descr: Необходимо выбрать топик, который необходимо удалить - - name: delete_user - id: 170 - kind: subresource - action: delete - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 540 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - name: modify - id: 162 - kind: instance - action: modify - params: - - id: 1087 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 586 - code: cpu - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 587 - code: memory - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 588 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 589 - code: disk - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 1088 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 590 - code: allocationExternal - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - is_modifiable: false - - id: 591 - code: masterAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - name: modify_topic - id: 166 - kind: subresource - action: modify - subresource: topic - man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' - params: - - id: 526 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`' - sort: 10 - - id: 527 - code: partitions - required: false - descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` - sort: 30 - - id: 528 - code: replicas - required: false - descr: Количество партиций ≤ (число брокеров). Пример `2` - sort: 20 - - name: modify_user - id: 169 - kind: subresource - action: modify - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 535 - code: nameTopic - data_type: string - required: false - default: '*' - descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' - - id: 536 - code: operations - data_type: array - required: false - value_list: - - Create - - Describe - - Read - - Write - descr: 'Пример: `Create, Describe, Read, Write`' - - id: 537 - code: group - data_type: string - required: false - default: '*' - descr: 'Пример: `order-consumers` или `*`' - - id: 538 - code: accessHosts - data_type: string - required: false - default: '*' - descr: 'Пример: `192.168.1.0/24` или `*`' - - id: 539 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - sort: 10 - - name: resume - id: 161 - kind: instance - action: resume - params: [] - - name: suspend - id: 160 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/117_nifi.yaml b/generated/prod/resources_yaml/117_nifi.yaml deleted file mode 100644 index 39c49bd..0000000 --- a/generated/prod/resources_yaml/117_nifi.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: nifi -service_id: 117 -service_display_name: Nifi -service_short_name: nifi -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 150 - kind: instance - action: create - params: - - id: 471 - code: kafkaUid - data_type: uuid - required: true - ref_svc_id: 116 - descr: Выбрать кластер из списка - sort: 10 - - id: 472 - code: partitions - data_type: integer > 0 - required: true - descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` - man: 'Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.' - sort: 30 - is_modifiable: true - - id: 473 - code: replicas - data_type: integer > 0 - required: true - descr: Количество партиций ≤ (число брокеров). Пример `2` - sort: 40 - is_modifiable: true - - id: 474 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`' - sort: 20 - - name: delete - id: 151 - kind: instance - action: delete - params: [] - - name: modify - id: 154 - kind: instance - action: modify - params: - - id: 477 - code: partitions - data_type: integer > 0 - required: false - descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` - man: 'Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.' - - id: 478 - code: replicas - data_type: integer > 0 - required: false - descr: Количество партиций ≤ (число брокеров). Пример `2` diff --git a/generated/prod/resources_yaml/119_akhq.yaml b/generated/prod/resources_yaml/119_akhq.yaml deleted file mode 100644 index 005512b..0000000 --- a/generated/prod/resources_yaml/119_akhq.yaml +++ /dev/null @@ -1,107 +0,0 @@ -name: akhq -service_id: 119 -service_display_name: Akhq -service_short_name: Akhq -service_man: '# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
AKHQ — это веб-интерфейс для работы с кластерами Apache Kafka.
Позволяет просматривать топики, consumer group''ы, сообщения, ACL, схему безопасности, состояние брокеров и метаданные.
Поддерживает работу с несколькими кластерами Kafka одновременно, включая внутренние и внешние (TLS) подключения.

## Доступные операции
- **create** — Создание ресурса AKHQ
- **suspend** — Остановка ресурса
- **resume** — Возобновление работы ресурса
- **modify** — Модификация настроек
- **delete** — Удаление ресурса

---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Кластер Kafka к которому нужно настроить подключение**
Для использования AKHQ необходимо предварительно развернуть сервис Kafka. Пример: `kafka-external`.

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов для работы Akhq. Пример `2`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `512`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Имя домена:**
Dns Имя по которому будет происходить обращение к сервису. Указать необходимо уникальное имя до . ( то есть если необходимо получить dns ui) Пример: `kafka-external`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**connectionUrl**
Это полное доменное имя (FQDN), по которому будет доступен интерфейс AKHQ. После развёртывания подождите несколько минут — UI станет доступен по HTTPS, как только все сервисы полностью запустятся.

**Учетные данные**
Здесь представлены поля для ввода логина и пароля, с помощью которых вы сможете авторизоваться в сервисе.

---
## 5. Дополнительная информация
Учётные записи, созданные для работы с AKHQ, предоставляют доступ только для просмотра данных. Для управления Kafka, пожалуйста, используйте возможности личного кабинета.
---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 175 - kind: instance - action: create - params: - - id: 550 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Выбрать платформу из списка - sort: 10 - - id: 551 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - minvalue: 512 - descr: 'Пример: `1024`' - sort: 20 - - id: 552 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - minvalue: 500 - descr: 'Пример: `1000`' - sort: 30 - - id: 553 - code: domain - data_type: string - required: true - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 40 - - id: 554 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - minvalue: 1 - sort: 11 - - id: 555 - code: kafkaUid - data_type: uuid - required: true - ref_svc_id: 116 - descr: Выбрать кластер из списка - sort: 5 - is_modifiable: true - - name: delete - id: 176 - kind: instance - action: delete - params: [] - - name: modify - id: 180 - kind: instance - action: modify - params: - - id: 565 - code: kafkaUid - data_type: uuid - required: true - ref_svc_id: 116 - descr: Выбрать кластер из списка - - name: resume - id: 178 - kind: instance - action: resume - params: [] - - name: suspend - id: 177 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/120_clickhouse.yaml b/generated/prod/resources_yaml/120_clickhouse.yaml deleted file mode 100644 index 0740a04..0000000 --- a/generated/prod/resources_yaml/120_clickhouse.yaml +++ /dev/null @@ -1,569 +0,0 @@ -name: clickhouse -service_id: 120 -service_display_name: ClickHouse -service_short_name: clickhouse -service_man: '# Инструкция по управлению кластерами и пользователями ClickHouse

---

## Общая информация

ClickHouse — это колоночная система управления базами данных, предназначенная для обработки аналитических запросов в реальном времени. Платформа позволяет создавать, удалять, приостанавливать и возобновлять кластеры ClickHouse, а также управлять базами данных и пользователями.

Кластер создаётся через оператор Altinity (`https://github.com/Altinity/clickhouse-operator`) в ресурсной платформе типа `k8s`

Кластеры могут создаваться шардированными с произвольным количеством реплик. Для обеспечения отказоустойчивости и координации обязательно включение параметра `Создать ClickHouse-Keeper`.

Кластера создаются с обязательным бекапом. Для этого необходимо предварительно создать услугу `S3 Object Storage`.
---

## Доступные операции

* **create** — Создание кластера
* **delete** — Удаление кластера
* **suspend** — Приостановка работы кластера
* **resume** — Возобновление работы кластера
* **create_database** — Создание базы данных
* **delete_database** — Удаление базы данных
* **create_user** — Создание пользователя и настройка прав доступа к базе данных
* **delete_user** — Удаление пользователя

## Процесс создания и первого подключения

1. Выполнить операцию `create` - будет создан кластер
2. Выполнить операцию `create_database` - будет создана база данных
3. Выполнить операцию `create_user` - будет создан пользователь (выбрать доступ подключения к БД, созданной в п.2)

## Примеры подключения

### Подключение через HTTP-интерфейс

```bash
# Проверка доступности кластера
curl "http://<username>:<password>@<host>:8123/" -d "SELECT 1"

# Выполнение запроса
curl "http://<username>:<password>@<host>:8123/" -d "SHOW DATABASES"
```

## Простые кейсы
*Создать БД и обычной таблицы*
```clickhouse
CREATE TABLE database.table
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

INSERT INTO database.table SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM database.table;
```
Базы и таблицы не реплицируются без специального ключа (`ON CLUSTER ''clickhousek8s''`)
В случае создания в стиле, описанным выше, необходимо будет выполнять действия на всех шардах/репликах


*Создание БД и шардированной таблицы*
```clickhouse
CREATE TABLE database.localUsers
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

CREATE TABLE database.shardedTable
AS localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', id);

INSERT INTO shardedTable SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM shardedTable;
```
Важный момент - в данном примере таблицы придётся также выполнять на всех нодах ClickHouse

*Создание реплицируемой и шардированной таблицы*
```clickhouse
CREATE TABLE IF NOT EXISTS database.localUsers ON cluster ''clickhousek8s''
(
user_id UInt64,
name String,
email String
)
ENGINE = ReplicatedMergeTree(
''/clickhouse/tables/{shard}/localUsers'',
''{replica}''
)
ORDER BY user_id;

CREATE TABLE IF NOT EXISTS database.users ON cluster ''clickhousek8s''
AS database.localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', rand());

INSERT INTO database.users VALUES (1, ''Alice'', ''alice@example.com'');
INSERT INTO database.users VALUES (2, ''Bob'', ''bob@example.com'');
INSERT INTO database.users VALUES (3, ''Charlie'', ''charlie@example.com'');
INSERT INTO database.users VALUES (4, ''David'', ''david@example.com'');
INSERT INTO database.users VALUES (5, ''Gosha'', ''gosha@example.com'');
INSERT INTO database.users VALUES (6, ''Eve'', ''eve@example.com'');
INSERT INTO database.users VALUES (7, ''Frank'', ''frank@example.com'');
INSERT INTO database.users VALUES (8, ''Grace'', ''grace@example.com'');
INSERT INTO database.users VALUES (9, ''Hannah'', ''hannah@example.com'');
INSERT INTO database.users VALUES (10, ''Ivan'', ''ivan@example.com'');

SELECT * FROM database.localUsers;
SELECT * FROM database.users;

SELECT COUNT(*) AS total_users FROM database.users;
SELECT COUNT(*) AS total_users_local FROM database.localUsers;
```' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 181 - kind: instance - action: create - man: Операция создания кластера ClickHouse

Для подключения к кластеру также необходимо создать базу данных через операцию `create_database` и создание пользователя через операцию `create_user` - params: - - id: 837 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 194 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 838 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 195 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 196 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 197 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 199 - code: shards - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - - "3" - man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' - is_modifiable: false - - id: 198 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 839 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 200 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 201 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 840 - code: clickhouseKeeperConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 202 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 203 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 204 - code: replicas - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "1" - - "3" - man: Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут - is_modifiable: false - - id: 841 - code: clickhouseConfiguration - data_type: map-fixed - required: true - sort: 50 - is_modifiable: true - sub_params: - - id: 205 - code: version - data_type: string - required: true - default: "25.10" - value_list: - - "25.10" - is_modifiable: false - - id: 842 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 206 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 207 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 208 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 843 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 80 - is_modifiable: true - sub_params: - - id: 209 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 210 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 211 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 212 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 851 - code: clickhouseFilesConfiguration - data_type: array-map-fixed - required: false - man: 'Формат:
- path: somethings3.xml
xmlConfig: ''<clickhouse>......</clickhouse>''' - sort: 60 - is_modifiable: true - sub_params: - - id: 234 - code: path - data_type: string - required: true - default: something.xml - regex: ^[a-zA-Z0-9]+\.xml$ - is_modifiable: false - - id: 235 - code: xmlConfig - data_type: string - required: true - default: '<clickhouse></clickhouse>' - man: Предварительно xml необходимо отформатировать через
```
$ tr -d '\n \t' < input.xml > output.xml
```

Позже будет исправлено - is_modifiable: false - is_sensitive: true - - name: create_database - id: 186 - kind: subresource - action: create - subresource: database - man: База данных создаётся на всех репликах кластера ClickHouse - params: - - id: 600 - code: dbName - data_type: string - required: true - default: db01 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: 'Пример: `db01`' - sort: 10 - - name: create_user - id: 185 - kind: subresource - action: create - subresource: user - params: - - id: 596 - code: username - data_type: string - required: true - default: myusername - regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ - maxlength: 62 - minlength: 2 - descr: 'Пример: `myusername`' - sort: 10 - - id: 598 - code: dbName - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 30 - sub_params: - - id: 233 - code: dbName - data_type: string - required: true - default: "" - is_modifiable: false - - id: 599 - code: accessHosts - data_type: string - required: true - default: ::/0 - descr: 'Пример: `::/0`' - sort: 40 - - name: delete - id: 184 - kind: instance - action: delete - man: Удаление возможно только после остановки сервиса через операцию `suspend`
После выполнения операции необходимо выждать определенное кол-во времени - params: [] - - name: delete_database - id: 188 - kind: subresource - action: delete - subresource: database - man: Необратимое действие!!!
Трижды подумай, заблокируй телефон тимлида и не выполняй это действие - params: - - id: 602 - code: dbName - data_type: string - required: true - descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ - - name: delete_user - id: 187 - kind: subresource - action: delete - subresource: user - params: - - id: 601 - code: username - required: true - descr: Имя пользователя, созданное через операцию `create_user` - - name: modify - id: 189 - kind: instance - action: modify - params: - - id: 844 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 213 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 214 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 215 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 216 - code: shards - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - - "3" - man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' - is_modifiable: false - - id: 217 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 845 - code: clickhouseKeeperConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 218 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 219 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 220 - code: replicas - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "1" - - "3" - man: Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут - is_modifiable: false - - id: 846 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 221 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 222 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 847 - code: clickhouseConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 223 - code: version - data_type: string - required: true - default: "" - value_list: - - "25.10" - is_modifiable: false - - id: 848 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 224 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 225 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 226 - code: schedule - data_type: string - required: true - default: "" - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 849 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - sub_params: - - id: 229 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 230 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 227 - code: enabled - data_type: boolean - required: true - default: false,true - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 228 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 850 - code: clickhouseFilesConfiguration - data_type: array-map-fixed - required: false - man: 'Формат:
- path: somethings3.xml
xmlConfig: ''<clickhouse>......</clickhouse>''' - sort: 50 - sub_params: - - id: 232 - code: xmlConfig - data_type: string - required: true - default: '<clickhouse></clickhouse>' - man: Предварительно xml необходимо отформатировать через
```
$ tr -d '\n \t' < input.xml > output.xml
```

Позже будет исправлено - is_modifiable: false - is_sensitive: true - - id: 231 - code: path - data_type: string - required: true - default: something.xml - regex: ^[a-zA-Z0-9]+\.xml$ - is_modifiable: false - - name: reconcile - id: 276 - kind: action - action: reconcile - params: [] - - name: resume - id: 183 - kind: instance - action: resume - params: [] - - name: suspend - id: 182 - kind: instance - action: suspend - man: Операция приводит к удалению всех подов
Persistent Volume остаются нетронутыми - params: [] diff --git a/generated/prod/resources_yaml/12_s3.yaml b/generated/prod/resources_yaml/12_s3.yaml deleted file mode 100644 index f1e5597..0000000 --- a/generated/prod/resources_yaml/12_s3.yaml +++ /dev/null @@ -1,130 +0,0 @@ -name: s3 -service_id: 12 -service_display_name: S3 Object Storage -service_short_name: s3 -service_man: Руководство доступно по ссылке:
https://docs.s3-website.msk-1.ngcloud.ru/ -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 1 - kind: instance - action: create - params: - - id: 163 - code: resourceRealm - required: true - default: s3.msk-1.ngcloud.ru - value_list: - - s3.msk-1.ngcloud.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: кластер S3 для подключения - sort: 20 - - id: 320 - code: displayName - data_type: string - required: true - regex: ^[a-zA-Z0-9_]{1,61}[a-zA-Z0-9]$ - descr: Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента - sort: 10 - - name: create_sub_user - id: 6 - kind: subresource - action: create - subresource: sub_user - params: - - id: 60 - code: subUserName - data_type: string - required: true - - id: 61 - code: subUserAccess - data_type: string - required: true - value_list: - - read - - write - - readwrite - - full - - name: delete - id: 5 - kind: instance - action: delete - params: [] - - name: delete_sub_user - id: 7 - kind: subresource - action: delete - subresource: sub_user - params: - - id: 63 - code: subUserName - data_type: string - required: true - descr: subUserName. По идее услуга уже создана - должен передаваться из ЛК - - name: modify - id: 4 - kind: instance - action: modify - man: При первичном вызове операции услуга Trial прекращает действие - params: - - id: 50 - code: maxSizeGbPerUser - data_type: integer > 0 - required: false - maxvalue: 50000 - minvalue: 1 - descr: maxSizeGb. Передаём руками - man: maxSizeGb. Передаём руками - - id: 51 - code: maxObjectsPerBucket - data_type: integer > 0 - required: false - maxvalue: 1e+07 - minvalue: 1 - descr: Квота на бакет. Кол-во объектов. Передаём руками - man: Квота на бакет. Кол-во объектов. Передаём руками - - id: 52 - code: maxBucketsPerUser - data_type: integer > 0 - required: false - maxvalue: 1000 - minvalue: 1 - descr: Квота на пользователя. Количество бакетов пользователя. Передаём руками - man: Квота на пользователя. Количество бакетов пользователя. Передаём руками - - name: reconcile - id: 407 - kind: action - action: reconcile - params: [] - - name: resume - id: 3 - kind: instance - action: resume - params: [] - - name: suspend - id: 2 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/13_s3bucket.yaml b/generated/prod/resources_yaml/13_s3bucket.yaml deleted file mode 100644 index 68e506c..0000000 --- a/generated/prod/resources_yaml/13_s3bucket.yaml +++ /dev/null @@ -1,90 +0,0 @@ -name: s3bucket -service_id: 13 -service_display_name: S3 бакет -service_short_name: s3bucket -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 51 - kind: instance - action: create - params: - - id: 124 - code: s3UserUid - data_type: uuid - required: true - ref_svc_id: 12 - descr: UID Корневой услуги S3. На основе него будет создан бакет. - - id: 125 - code: bucketName - data_type: string - required: true - descr: Имя бакета s3 - - id: 126 - code: maxSize - required: true - default: "-1" - descr: Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes - man: Максимальный размер бакета s3. Если поставить 0, будет безлимит. Размер указывается в Bytes - - id: 127 - code: readAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. - - id: 128 - code: listAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. - - id: 129 - code: corsAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS - - id: 130 - code: placement - data_type: string - required: true - default: HOT - value_list: - - HOT - - COLD - descr: Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения - - name: delete - id: 52 - kind: instance - action: delete - params: [] diff --git a/generated/prod/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml b/generated/prod/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml deleted file mode 100644 index da26530..0000000 --- a/generated/prod/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml +++ /dev/null @@ -1,176 +0,0 @@ -name: vc_mgmt_sthutrval_cluster -service_id: 148 -service_display_name: Менеджмент Kubernetes кластер Штурвал -service_short_name: vcMgmtSthutrvalCluster -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 258 - kind: instance - action: create - params: - - id: 771 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 91 - code: resourceRealm - data_type: string - required: true - default: ngcloud.ru - value_list: - - ngcloud.ru - descr: Облако в котором будет развёрнут тенант - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - is_modifiable: false - - id: 89 - code: clusterName - data_type: string - required: true - default: example-ewwnrebe - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 772 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 90 - code: appVersion - data_type: string - required: true - default: 2.13.1 - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - id: 773 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 94 - code: count - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 92 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана - is_modifiable: false - - id: 93 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - is_modifiable: false - - id: 774 - code: workerConfiguration - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 40 - sub_params: - - id: 95 - code: groupName - data_type: string - required: true - default: workers-tknsoqh9 - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 96 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана - is_modifiable: false - - id: 97 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - is_modifiable: false - - id: 98 - code: count - data_type: integer > 0 - required: true - default: "2" - is_modifiable: false - - id: 775 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 99 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false - - id: 100 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: 'Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**' - is_modifiable: false - - id: 101 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - id: 102 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: 'Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**' - is_modifiable: false diff --git a/generated/prod/resources_yaml/149_valo_tenant.yaml b/generated/prod/resources_yaml/149_valo_tenant.yaml deleted file mode 100644 index f605758..0000000 --- a/generated/prod/resources_yaml/149_valo_tenant.yaml +++ /dev/null @@ -1,249 +0,0 @@ -name: valo_tenant -service_id: 149 -service_display_name: VALO Cloud -service_short_name: valoTenant -service_man: '# Инструкция по развертыванию Тенант Valo через платформу

## 1. Общая информация
Тенант Valo — логический изолированный экземпляр сервиса VALO Cloud, предоставляющий защищённое облачное хранилище, синхронизацию данных и доступ к документам с различных устройств. При создании тенант автоматически разворачивается в тестовом режиме сроком на 2 месяца. Перевод в промышленную эксплуатацию выполняется отдельной операцией.

### Доступные операции

* **create** — создание нового тенанта в тестовом режиме сроком на 2 месяца.
* **modify** — изменение настроек тенанта, включая перевод из ТЕСТ в ПРОД.
* **suspend** — выключение (приостановка работы) тенанта.
* **resume** — включение ранее приостановленного тенанта.
* **delete** — удаление тенанта; доступно только после выполнения операции suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создан тенант.
Пример значения: `valo.ngcloud.ru`.

### DNS-Префикс
Описание: Будет добавлено в зону valo.ngcloud.ru
Пример значения: `mysimpledomain`.

### Параметры хранилища
Описание: Сколько места будет выделено под использование в тенанте. Данные хранятся в S3. Размер указывается в Gigabytes
Пример значения: `10`.

### Настройки тенанта
Описание: Ряд конфигурации Valo-тенанта, такие как настройки пользователя и доступного функционала
Пример значения: `опционально в рамках настройки`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Достаточно развернуть тенант в ТЕСТ. Срок предоставляется в 2 месяца. По истечению срока тенант автоматически переводится в `suspend` на стороне ресурсной платформы

### Промышленное окружение
Для разблокировки используйте операцию `modify`

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Статус тенанта
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация
Тенант всегда создаётся в режиме ТЕСТ и не может быть сразу переведён в ПРОД без операции modify.
Удаление тенанта возможно только после его выключения с помощью операции suspend.
Срок тестового режима составляет 2 месяца с момента создания.
Операции suspend и resume используются для временного управления доступностью сервиса без потери конфигурации.
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 202 - kind: instance - action: create - params: - - id: 655 - code: resourceRealm - data_type: string - required: true - default: valo.ngcloud.ru - value_list: - - valo.ngcloud.ru - func: getAvailableResourceRealms - sort: 10 - - id: 656 - code: domain - data_type: string - required: true - regex: ^[a-z0-9](?:[a-z0-9-]{3,61}[a-z0-9])?$ - maxlength: 61 - minlength: 3 - descr: Будет добавлено в зону valo.ngcloud.ru - sort: 20 - - id: 657 - code: valoOptions - data_type: map-fixed - required: true - default: '{}' - sort: 40 - is_modifiable: true - sub_params: - - id: 23 - code: ffUserImport - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 24 - code: ffThemes - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 25 - code: ffPublicLink - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 26 - code: ffLogging - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 28 - code: userCount - data_type: integer > 0 - required: true - default: "10" - is_modifiable: false - - id: 20 - code: requestApproveMaxPeriod - data_type: integer > 0 - required: true - default: "2" - descr: Максимальное значение периода согласования (в днях) - is_modifiable: false - - id: 21 - code: ffOnlineEditor - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 22 - code: ffWatermark - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 658 - code: s3Storage - data_type: integer > 0 - required: true - default: "10" - maxvalue: 999 - minvalue: 1 - descr: Параметр указывается в `Gb` - man: Данные хранятся в отдельном экземпляре S3 - sort: 30 - is_modifiable: true - - name: delete - id: 206 - kind: instance - action: delete - params: [] - - name: modify - id: 203 - kind: instance - action: modify - params: - - id: 659 - code: valoOptions - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 35 - code: ffPublicLink - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 36 - code: ffLogging - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 29 - code: userCount - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 30 - code: requestApproveMaxPeriod - data_type: integer > 0 - required: true - default: "" - descr: Максимальное значение периода согласования (в днях) - is_modifiable: false - - id: 31 - code: ffOnlineEditor - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 32 - code: ffWatermark - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 33 - code: ffUserImport - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 34 - code: ffThemes - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 660 - code: s3Storage - data_type: integer > 0 - required: true - descr: Параметр указывается в `Gb` - man: Данные хранятся в отдельном экземпляре S3 - sort: 20 - - name: resume - id: 205 - kind: instance - action: resume - params: [] - - name: suspend - id: 204 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/150_k8s_sthutrval_cluster.yaml b/generated/prod/resources_yaml/150_k8s_sthutrval_cluster.yaml deleted file mode 100644 index 19de5ba..0000000 --- a/generated/prod/resources_yaml/150_k8s_sthutrval_cluster.yaml +++ /dev/null @@ -1,371 +0,0 @@ -name: k8s_sthutrval_cluster -service_id: 150 -service_display_name: Kubernetes кластер Штурвал -service_short_name: k8sSthutrvalCluster -service_man: Перед разворачиванием убедиться, что в облаке есть 2 свободных невыделенных IP в пуле -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 108 - kind: instance - action: create - params: - - id: 750 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 57 - code: vdcUid - data_type: string - required: true - default: "" - is_modifiable: false - - id: 58 - code: nsxtUid - data_type: string - required: true - default: "" - is_modifiable: false - - id: 59 - code: clusterName - data_type: string - required: true - default: example-wln2kdvp - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 751 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 61 - code: sizingPolicy - data_type: string - required: true - default: '[]' - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 62 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 63 - code: count - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 752 - code: workerConfiguration - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 40 - is_modifiable: true - sub_params: - - id: 65 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 66 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 67 - code: count - data_type: integer > 0 - required: true - default: "2" - is_modifiable: false - - id: 64 - code: groupName - data_type: string - required: true - default: workers-uoazzasx - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 753 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 50 - is_modifiable: true - sub_params: - - id: 68 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false - - id: 69 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 70 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - id: 71 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 755 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 72 - code: appVersion - data_type: string - required: true - default: 2.13.1 - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - name: create_user - id: 281 - kind: subresource - action: create - subresource: user - params: - - id: 869 - code: admins - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 10 - sub_params: - - id: 236 - code: email - data_type: string - required: true - default: "" - man: Пользователи будут добавлены, только если они зарегистрированы в личном кабинете - is_modifiable: false - - name: delete - id: 109 - kind: instance - action: delete - params: [] - - name: delete_user - id: 282 - kind: subresource - action: delete - subresource: user - params: - - id: 870 - code: admins - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 237 - code: email - data_type: string - required: true - default: "" - is_modifiable: false - - name: modify - id: 110 - kind: instance - action: modify - man: При изменении кол-ва узлов, удаления группы, если в этой группе есть поды на которых есть PV с named-дисками, удаляться ноды сами НЕ БУДУТ.
Убедитесь, что они будут смигрированы на другие ноды в процессе прежде чем группа узлов/узлы будут удалены - params: - - id: 756 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 73 - code: appVersion - data_type: string - required: true - default: "" - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - id: 757 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 75 - code: sizingDisk - data_type: integer > 0 - required: true - default: "" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 76 - code: count - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 74 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 758 - code: workerConfiguration - data_type: array-map-fixed - required: true - sort: 30 - sub_params: - - id: 77 - code: groupName - data_type: string - required: true - default: workers-rpsrrqwt - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 78 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 79 - code: sizingDisk - data_type: integer > 0 - required: true - default: "" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 80 - code: count - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 759 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 81 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false - - id: 82 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 83 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - id: 84 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: reconcile - id: 253 - kind: action - action: reconcile - params: [] - - name: resume - id: 174 - kind: instance - action: resume - params: [] - - name: suspend - id: 173 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/163_llm_ai.yaml b/generated/prod/resources_yaml/163_llm_ai.yaml deleted file mode 100644 index d749f60..0000000 --- a/generated/prod/resources_yaml/163_llm_ai.yaml +++ /dev/null @@ -1,189 +0,0 @@ -name: llm_ai -service_id: 163 -service_display_name: LLM -service_short_name: llmAi -service_man: '## 1. Общая информация

LLMaaS (Large Language Model as a Service) — это сервис для доступа к языковым моделям через OpenAI-совместимое API.
Платформа позволяет создавать организации (тенанты), команды, выпускать и управлять API-ключами, устанавливать лимиты на использование.

---

## Доступные операции

- **create** — Создание организации в LLMaaS
- **delete** — Удаление организации

- **create_team** — Создание команды внутри организации
- **modify_team** — Редактирование команды (модели, RPM, TPM)
- **delete_team** — Удаление команды

- **create_secrets** — Создание API-ключа
- **update_secrets** — Обновление параметров API-ключа
- **delete_secrets** — Удаление API-ключа

---

## 2. Параметры создания организации

Ниже перечислены параметры, которые необходимо указать при создании организации:

* **Платформа для развертывания**
Выбирается автоматически, изменять не нужно.

* **Период бюджета**
Срок жизни организации в днях.
Пример: `30`


---

## 3. Параметры создания команды

* **Имя команды**
Уникальное имя команды в рамках организации.
Пример: `production`

* **Модель**
Название модели, доступной для команды.
Пример: `gpt-oss-120b`

* **Лимит запросов в минуту (RPM)**
Максимальное количество запросов к API в минуту.
Пример: `60`

* **Лимит токенов в минуту (TPM)**
Максимальное количество токенов (входящих + исходящих) в минуту.
Пример: `100000`

---

## 4. Параметры редактирования команды

Для существующей команды можно изменить:

* **Модель**
Новая модель для команды.
Пример: `gpt-oss-20b`

* **Лимит запросов в минуту (RPM)**
Новое значение RPM.
Пример: `120`

* **Лимит токенов в минуту (TPM)**
Новое значение TPM.
Пример: `200000`

---

## 5. Параметры создания API-ключа

Ниже перечислены параметры, которые необходимо указать при создании API-ключа:

* **Имя ключа**
Уникальное имя ключа в рамках организации.
Пример: `prod-key`

* **Команда**
Имя команды для привязки ключа. Если не указано — ключ привязывается к команде `main`.
Пример: `production`

* **Модель**
Название модели, доступной для данного ключа.
Пример: `gpt-oss-120b`

* **Лимит запросов в минуту (RPM)**
Максимальное количество запросов к API в минуту.
Пример: `60`

* **Лимит токенов в минуту (TPM)**
Максимальное количество токенов (входящих + исходящих) в минуту.
Пример: `100000`

**Важно:** Лимиты API-ключа не могут превышать лимиты команды, к которой он привязан.
При превышении операция будет отклонена с ошибкой.
Перед созданием ключа убедитесь, что `RPM <= team.RPM` и `TPM <= team.TPM`.


---

## 6. Параметры обновления API-ключа

Для уже существующего ключа можно изменить параметры. Все параметры являются опциональными:

* **Имя ключа**
Имя обновляемого ключа.
Пример: `prod-key`

* **Модель**
Новое название модели.
Пример: `gpt-oss-20b`

* **Лимит запросов в минуту (RPM)**
Новое значение RPM.
Пример: `120`

* **Лимит токенов в минуту (TPM)**
Новое значение TPM.
Пример: `200000`

**Важно:** При обновлении лимитов API-ключа новые значения не могут превышать лимиты команды.
Если указать значения выше командных, операция завершится ошибкой.
Указывайте `RPM <= team.RPM` и `TPM <= team.TPM`.

Команда определяется автоматически по стейту услуги — изменить привязку к другой команде через эту операцию нельзя.

---

## 7. Параметры удаления API-ключа

* **Команда**
Имя команды, к которой привязан ключ.
Пример: `production`

* **Имя ключа**
Имя удаляемого ключа.
Пример: `prod-key`

---

## 8. Параметры удаления команды

* **Имя команды**
Имя удаляемой команды.
Пример: `production`

---

## 9. Параметры удаления организации

Удаление организации не требует дополнительных параметров — удаляется вся организация со всеми командами и ключами.

---

## 10. Выходные параметры

После успешного создания организации платформа возвращает:

* **connectionUrl**
URL для доступа к inference API (OpenAI-совместимый).
Пример: `https://api.aillm.ru`

---


## 12. Пример использования API

После получения API-ключа клиент может обращаться к LLMaaS напрямую:

**Запрос к chat completions:**

```bash
curl https://api.aillm.ru/v1/chat/completions \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d ''{
"model": "gpt-oss-120b",
"messages": [
{"role": "user", "content": "Привет!"}
]
}''
```

**Получение списка доступных моделей:**

```bash
curl https://api.aillm.ru/v1/models \
-H "Authorization: Bearer sk-..."
```
' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 271 - kind: instance - action: create - params: - - id: 826 - code: resourceRealm - data_type: string - required: true - default: llmAi.aillm.ru - value_list: - - llmAi.aillm.ru - func: getAvailableResourceRealms - - name: create_secrets - id: 274 - kind: subresource - action: create - subresource: secrets - params: - - id: 833 - code: models - data_type: array - required: true - sort: 20 - depends_on: teamName - - id: 834 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - minvalue: 1 - - id: 835 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - minvalue: 1 - - id: 857 - code: keyName - data_type: string - required: true - sort: 10 - - id: 859 - code: teamName - data_type: string - required: true - sort: 15 - - name: create_team - id: 278 - kind: subresource - action: create - subresource: team - params: - - id: 858 - code: teamName - data_type: string - required: true - - id: 860 - code: models - data_type: array - required: true - - id: 861 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - - id: 862 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - - name: delete - id: 273 - kind: instance - action: delete - params: [] - - name: delete_secrets - id: 275 - kind: subresource - action: delete - subresource: secrets - params: - - id: 836 - code: keyName - data_type: string - required: true - depends_on: teamName - - id: 876 - code: teamName - data_type: string - required: true - sort: 10 - - name: delete_team - id: 280 - kind: subresource - action: delete - subresource: team - params: - - id: 868 - code: teamName - data_type: string - required: true - - name: modify_team - id: 279 - kind: subresource - action: modify - subresource: team - params: - - id: 864 - code: teamName - data_type: string - required: true - - id: 865 - code: models - data_type: array - required: true - depends_on: teamName - - id: 873 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - minvalue: 1 - - id: 874 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - minvalue: 1 - - name: update_secrets - id: 277 - kind: subresource - action: update - subresource: secrets - man: Данная операция предназначена для управления лимитами и моделями - params: - - id: 852 - code: keyName - data_type: string - required: true - depends_on: teamName - - id: 853 - code: models - data_type: array - required: true - depends_on: teamName - - id: 854 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - - id: 855 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - - id: 875 - code: teamName - data_type: string - required: true - sort: 5 diff --git a/generated/prod/resources_yaml/19_vc_org.yaml b/generated/prod/resources_yaml/19_vc_org.yaml deleted file mode 100644 index cf9bdee..0000000 --- a/generated/prod/resources_yaml/19_vc_org.yaml +++ /dev/null @@ -1,140 +0,0 @@ -name: vc_org -service_id: 19 -service_display_name: Организация в Cloud Director -service_short_name: vcOrg -service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Url организации
Для быстрого доступа в тенант

### Статус организации
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)** привязанный к этой организации;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к vdc выше.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 136 - kind: instance - action: create - man: '`IaaS` - Доступ в Keycloak предоставляется пользователю, который создаёт услугу
`SaaS` - Без доступа к Keycloak' - params: - - id: 418 - code: resourceRealm - data_type: string - required: true - default: ngcloud.ru - value_list: - - ngcloud.ru - func: getAvailableResourceRealms - descr: Облако в котором будет развёрнут тенант - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - sort: 10 - - id: 556 - code: organizationType - data_type: string - required: true - default: iaas - value_list: - - iaas - - saas - man: Доступ к организации предоставляется только при выборе `iaas` - sort: 20 - - name: create_user - id: 283 - kind: subresource - action: create - subresource: user - params: - - id: 871 - code: admins - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 10 - sub_params: - - id: 238 - code: email - data_type: string - required: true - default: "" - man: Пользователи будут добавлены, только если они зарегистрированы в личном кабинете - is_modifiable: false - - name: delete - id: 137 - kind: instance - action: delete - man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого тенанта**

Если существуют инстансы VDC, Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' - params: [] - - name: delete_user - id: 284 - kind: subresource - action: delete - subresource: user - params: - - id: 872 - code: admins - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 239 - code: email - data_type: string - required: true - default: "" - is_modifiable: false - - name: modify - id: 207 - kind: instance - action: modify - man: Для возможности изменения параметров предварительно необходимо создать **Виртуальный датацентр (VDC)**, потом **Сетевой шлюз периметра (Edge Gateway)**
Также при удалении ipSpaces убедитесь, что все внешние IP удалены. В случае если хоть одна привязка vIP есть, данный ipSpace удалён **не будет** - params: - - id: 662 - code: vIPConfigure - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 39 - code: name - data_type: string - required: true - default: "" - is_modifiable: false - - id: 40 - code: count - data_type: integer > 0 - required: true - default: "" - descr: 'Пример: `1`' - is_modifiable: false - - name: reconcile - id: 249 - kind: action - action: reconcile - params: [] - - name: resume - id: 144 - kind: instance - action: resume - params: [] - - name: suspend - id: 143 - kind: instance - action: suspend - man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри тенанта - params: [] diff --git a/generated/prod/resources_yaml/1_dummy.yaml b/generated/prod/resources_yaml/1_dummy.yaml deleted file mode 100644 index 2bf61fe..0000000 --- a/generated/prod/resources_yaml/1_dummy.yaml +++ /dev/null @@ -1,336 +0,0 @@ -name: dummy -service_id: 1 -service_display_name: Болванка -service_short_name: dummy -service_man: '## Болванка для тестов.

А это просто много букв, чтобы посмотреть, как ведет себя текст при переполнении контейнера.


``` json
{
"iamServiceUrl": "https://auth-api-test.ngcloud.ru/api/v1/user",
"idpResponse": {
"prefix": {
"charset": "",
"status_text": "Request Time-out",
"text": true,
"errordetail": "",
"filecontent": "Connection Timeout",
"responseheader": {},
"header": "",
"statuscode": "408 Request Time-out",
"status_code": 408,
"mimetype": "Unable to determine MIME type of file."
}
}
}
```' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 18 - kind: instance - action: create - man: Это мануал к операции CREATE сервиса Болванка - params: - - id: 198 - code: durationMs - data_type: integer >= 0 - required: true - default: "0" - descr: перед завершением подождать (sleep) столько миллисекунд - man: перед завершением подождать (sleep) столько миллисекунд - sort: 20 - is_modifiable: true - - id: 199 - code: failAtStart - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, завершить джоб, ничего не записывая в cmdb - man: '**Операция не будет завершена**
Она будет в постоянном статусе PENDING, однако в Jenkins Job завершится с ошибкой' - sort: 30 - is_modifiable: true - - id: 200 - code: failInProgress - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - man: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - sort: 40 - is_modifiable: true - - id: 201 - code: whereFail - data_type: integer > 0 - required: false - default: "1" - value_list: - - "1" - - "2" - - "3" - descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - sort: 50 - is_modifiable: true - - id: 242 - code: resourceRealm - data_type: string - required: true - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы для подключения - sort: 10 - - id: 286 - code: bodymessage - data_type: string - required: false - descr: Дата, уходящая в Vault - man: Дата, уходящая в Vault - sort: 60 - is_modifiable: true - - id: 321 - code: mapExample - data_type: map - required: false - is_modifiable: true - sub_params: - - id: 1 - code: subparam1 - data_type: string - required: false - default: "" - descr: это для администратора - man: это для пользователя - is_modifiable: false - - id: 3 - code: secret - data_type: string - required: false - default: "" - descr: это секрет - man: значение параметра маскируется. Write Only - is_modifiable: false - is_sensitive: true - - id: 2 - code: subparam2 - data_type: string - required: false - default: "" - is_modifiable: false - - id: 322 - code: jsonExample - data_type: json - required: false - descr: Для тестирования интерфейса - man: Для тестирования интерфейса. Сюда можно писать корректный json - is_modifiable: true - - id: 396 - code: nestedRefExample - required: false - - id: 647 - code: mapFixed - data_type: map-fixed - required: false - descr: Новый тип поля - sub_params: - - id: 13 - code: bol1 - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Булевский, обязательный - man: Булевский, обязательный. Проверить, как воспринимается снятый флаг - is_modifiable: false - - id: 14 - code: minStr1 - data_type: string - required: true - default: abracadabra - value_list: - - abracadabra - descr: не меньше 10 символов - is_modifiable: false - - id: 10 - code: param1 - data_type: integer > 0 - required: true - default: "1" - descr: Integer, secret - man: integer > 0, sensitive data (secret) - is_modifiable: false - is_sensitive: true - - id: 11 - code: param2 - data_type: string - required: true - default: "1" - value_list: - - '["1"' - - '"2"' - - '"3"]' - descr: Дескрипшен - man: Выбор из списка 1,2,3 - is_modifiable: false - - id: 12 - code: param3 - data_type: integer > 0 - required: true - default: "1" - descr: целое положительное - man: int > 0, обязательный - is_modifiable: false - - id: 654 - code: arrayMapFixedExample - data_type: array-map-fixed - required: false - descr: Array of map-fixed - sub_params: - - id: 17 - code: param3 - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 18 - code: bol1 - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - is_modifiable: false - - id: 19 - code: minStr1 - data_type: string - required: true - default: "" - value_list: - - abracadabra - descr: не меньше 10 символов - is_modifiable: false - - id: 15 - code: param1 - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - is_sensitive: true - - id: 16 - code: param2 - data_type: string - required: true - default: "" - value_list: - - '["1"' - - '"2"' - - '"3"]' - is_modifiable: false - - id: 863 - code: arr - data_type: array - required: true - value_list: - - test - - "2" - - val1 - - name: delete - id: 71 - kind: instance - action: delete - params: [] - - name: modify - id: 92 - kind: instance - action: modify - params: - - id: 287 - code: durationMs - data_type: integer >= 0 - required: true - default: "0" - descr: перед завершением подождать (sleep) столько миллисекунд - man: перед завершением подождать (sleep) столько миллисекунд - sort: 10 - - id: 288 - code: failAtStart - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, завершить джоб, ничего не записывая в cmdb - sort: 20 - - id: 289 - code: failInProgress - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - sort: 30 - - id: 290 - code: whereFail - data_type: integer > 0 - required: false - value_list: - - "1" - - "2" - - "3" - descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - sort: 40 - - id: 291 - code: bodymessage - required: false - descr: Дата, уходящая в Vault - man: Дата, уходящая в Vault - sort: 50 - - id: 373 - code: mapExample - data_type: map - required: false - default: '{}' - sub_params: - - id: 4 - code: secret - data_type: string - required: true - default: "" - is_modifiable: false - is_sensitive: true - - id: 374 - code: jsonExample - required: false - - name: reconcile - id: 257 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 240 - kind: action - action: redeploy - params: [] - - name: resume - id: 94 - kind: instance - action: resume - params: [] - - name: suspend - id: 93 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/20_vc_org_saas.yaml b/generated/prod/resources_yaml/20_vc_org_saas.yaml deleted file mode 100644 index da0e150..0000000 --- a/generated/prod/resources_yaml/20_vc_org_saas.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: vc_org_saas -service_id: 20 -service_display_name: Организация [DEPRECATED] -service_short_name: vcOrgSaas -service_man: 'Верхнеуровневая сущность создаваемая для Заказчика.
Внутри Организация вы можете создать: Виртуальные датацентры, Каталоги.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 8 - kind: instance - action: create - man: Создание организации в Cloud Director
Id в клауд директоре создаётся на основе contragentCode - params: - - id: 185 - code: resourceRealm - data_type: string - required: true - default: ngcloud.ru - value_list: - - ngcloud.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы - sort: 30 - - name: delete - id: 23 - kind: instance - action: delete - params: [] - - name: resume - id: 119 - kind: instance - action: resume - params: [] - - name: suspend - id: 118 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/21_vc_vdc.yaml b/generated/prod/resources_yaml/21_vc_vdc.yaml deleted file mode 100644 index 6cd6879..0000000 --- a/generated/prod/resources_yaml/21_vc_vdc.yaml +++ /dev/null @@ -1,169 +0,0 @@ -name: vc_vdc -service_id: 21 -service_display_name: Виртуальный датацентр (vDC) -service_short_name: vc_vdc -service_man: '# Инструкция по развертыванию Виртуальный датацентр (vDC) через платформу

## 1. Общая информация

**Виртуальный датацентр (vDC)** — это логическая изолированная среда в VMware Cloud Director, предназначенная для размещения и управления виртуальными машинами, сетевой инфраструктурой и ресурсами хранения. Сервис обеспечивает выделение вычислительных ресурсов под конкретную организацию и предоставляет гибкие параметры масштабирования, контроля потребления и разделения ресурсов.

### Доступные операции

**create** — Создание виртуального датацентра (vDC) с заданными параметрами CPU, RAM, Storage и сетевой конфигурацией.
**delete** — Удаление vDC. Требуется предварительный перевод vDC в состояние *suspend*. Полное удаление возможно через 14 дней.
**modify** — Внесение изменений в параметры ресурсов (CPU/RAM/Storage), выделенных для vDC.
**suspend** — Заморозка vDC. Новые ресурсы или изменения внутри vDC становятся невозможными, работа ВМ не останавливается.
**resume** — Разморозка vDC. Восстановление возможности управления сущностями внутри vDC.

---

## 2. Параметры развертывания

Ниже перечислены входные параметры операции **create**.

### UUID Организации
Уникальный идентификатор организации в Cloud Director. Перед созданием vDC требуется предварительное создание услугу `Организация в Cloud Director`.

### Provider VDC в Cloud Director
Свойство ресурсной платформы, определяющее, какой пул физических ресурсов будет использоваться.

### Network Pool в Cloud Director
Определяет возможности сетевой конфигурации, включая создание Edge Gateway и выход во внешние сети.

### Гарантированная доля ЯДЕР
Гарантированный процент вычислительных ресурсов (CPU), закрепленный за vDC. Значение устанавливается разово при создании.

### Гарантированная доля ОЗУ
Гарантированный процент оперативной памяти, закрепленный за vDC. Также задаётся один раз при создании.

### Сколько аллоцировать на VDC ЯДЕР
Количество виртуальных ядер, выделяемых в рамках vDC.

### Сколько аллоцировать на VDC ОЗУ
Количество оперативной памяти в GB, выделяемой для vDC.

### Конфигурация Storage
Набор дисковых политик и их объёмов, выделяемых для vDC. Указывается имя политики и требуемый размер (GB).

---

## 3. Рекомендованные характеристики

### Тестовое окружение

- Количество ядер CPU: 10–20
- Аллоцируемая RAM: 16–64 GB
- Гарантированная доля CPU: 20%
- Гарантированная доля RAM: 20%
- Storage:
- Политика `SATA`
- Объём: 100–300 GB

### Промышленное окружение

- Количество ядер CPU: 16–128
- Аллоцируемая RAM: 64–512 GB
- Гарантированная доля CPU: 50%
- Гарантированная доля RAM: 50%
- Storage:
- Политики уровня `SSD`
- Объём: 1–10 TB, выбор несколько политик под разные нагрузки

---

## 4. Выходные параметры

Выходные параметры формируются операцией **create** (и используются другими операциями):

### Имя vDC
Уникальный идентификатор созданного виртуального датацентра. Используется в дальнейших операциях (modify, suspend, delete).

### Параметры выделенных ресурсов
Подтверждение итогового объёма CPU/RAM/Storage, выделенных vDC. Используются для учёта и контроля потребления.

---

## 5. Дополнительная информация

- Перед созданием vDC необходимо обеспечить наличие корректно созданной организации и доступных пула ресурсов ресурсной платформы.
- Гарантированная доля CPU и RAM задаётся **однократно при создании vDC**. После развертывания изменить эти значения невозможно; для изменения необходимо пересоздать vDC.
- Удаление vDC возможно только через **14 дней после перевода в состояние *suspend***. Удаление очищает все связанные сущности (ВМ, сети, каталоги), поэтому необходимо убедиться, что данные перенесены.
- Платформа Cloud Director использует концепцию изолированных организационных ресурсов, поэтому корректная настройка Provider VDC и Network Pool является обязательной при создании vDC.
- При изменении параметров (modify) изменяются только показатели CPU, RAM и Storage. Сетевые параметры и гарантированные доли остаются неизменными.
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 9 - kind: instance - action: create - params: - - id: 30 - code: organizationUid - data_type: uuid - required: true - ref_svc_id: 19 - man: Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` - sort: 10 - - id: 335 - code: providerVdc - data_type: string - required: true - man: Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
- sort: 30 - depends_on: organizationUid,networkProvider - - id: 361 - code: storageConfig - data_type: array-map-fixed - required: true - default: '[{}]' - man: Первая политика в списке указывается как "По умолчанию" - sort: 100 - is_modifiable: true - sub_params: - - id: 41 - code: name - data_type: string - required: true - default: "" - descr: getKeyListFromStruct(getResourceRealmConfig(getStringFromStruct(getInstanceState(params.organizationUid), "params.resourceRealm")), concat("vcd.hardware.providerVdcs['",params.providerVdc,"'].storage")) - is_modifiable: false - - id: 42 - code: size - data_type: integer > 0 - required: true - default: "100" - is_modifiable: false - - id: 397 - code: cpuGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - descr: '% резервирования' - man: Свойство нельзя изменить после создания - sort: 60 - - id: 557 - code: cpuAllocated - data_type: integer > 0 - required: true - default: "10" - maxvalue: 200 - minvalue: 1 - descr: шт - sort: 80 - is_modifiable: true - - id: 558 - code: memAllocated - data_type: integer > 0 - required: true - default: "20" - maxvalue: 4096 - minvalue: 1 - descr: gb - sort: 90 - is_modifiable: true - - id: 746 - code: networkProvider - data_type: string - required: true - sort: 20 - depends_on: organizationUid - - name: delete - id: 24 - kind: instance - action: delete - man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого VDC**

Если существуют инстансы Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' - params: [] - - name: modify - id: 112 - kind: instance - action: modify - params: - - id: 560 - code: cpuAllocated - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: шт - sort: 10 - - id: 561 - code: memAllocated - data_type: integer > 0 - required: true - maxvalue: 1024 - minvalue: 1 - descr: gb - sort: 20 - - id: 562 - code: storageConfig - data_type: array-map-fixed - required: true - man: Первая политика в списке указывается как "По умолчанию" - sort: 30 - sub_params: - - id: 43 - code: name - data_type: string - required: true - default: "" - is_modifiable: false - - id: 44 - code: size - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - name: reconcile - id: 250 - kind: action - action: reconcile - params: [] - - name: resume - id: 172 - kind: instance - action: resume - params: [] - - name: suspend - id: 171 - kind: instance - action: suspend - man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри VDC - params: [] diff --git a/generated/prod/resources_yaml/22_vc_nsxt.yaml b/generated/prod/resources_yaml/22_vc_nsxt.yaml deleted file mode 100644 index 158f59e..0000000 --- a/generated/prod/resources_yaml/22_vc_nsxt.yaml +++ /dev/null @@ -1,139 +0,0 @@ -name: vc_nsxt -service_id: 22 -service_display_name: Сетевой шлюз периметра (Edge) -service_short_name: vc_nsxt -service_man: '# Инструкция по развертыванию Сетевой шлюз периметра (Edge) через платформу

## 1. Общая информация

Сервис **Сетевой шлюз периметра (Edge)** предназначен для создания периметрового сетевого шлюза в рамках одного виртуального датацентра (vDC) или группы виртуальных датацентров (groupvDC).
При создании автоматически разворачивается routed-сеть с адресным пространством `10.10.102.0/24`.
Сервис обеспечивает сетевую изоляцию, маршрутизацию, а также может включать функциональность балансировщика нагрузки AVI (ALB) для последующей интеграции, включая поддержку кластеров Штурвал.

### Доступные операции

**create** — Создание нового Edge с привязкой к vDC или groupvDC и развёртыванием routed-сети.
**delete** — Удаление ранее созданного Edge. Недоступно при наличии зависимых услуг (vApp, VM, Кластер Штурвал).
**modify** — Изменение параметров и сетевых настроек существующего Edge.

---

## 2. Параметры развертывания

Ниже приведены параметры операции **create**.

### Тип родительской услуги
Определяет контекст размещения Edge.
Допустимые значения: `vdc`, `groupvdc`.
Использование:
- При выборе `vdc` обязателен параметр **UUID VDC**.
- При выборе `groupvdc` обязателен параметр **UUID Группы VDC**.

### UUID VDC
Идентификатор виртуального датацентра.
Необходимо предварительно создать vDC через услугу «Виртуальный датацентр (vDC)».
Указывается только при выборе родительского типа `vdc`.

### UUID Группы VDC
Идентификатор группы виртуальных датацентров.
Создаётся через услугу «Группа виртуальных датацентров (groupvDC)».
Используется при выборе родительского типа `groupvdc`.

### Включить ALB
Флаг активации AVI Load Balancer в Cloud Director.
Пример значения: `true`/`false`.
Нужен для развёртывания кластера Штурвал и возможности создания Virtual Services.

### Наименование segroup
Имя сервиса групп (segroup) на уровне ресурсной платформы.
Обязательно при включённом ALB.
Определяет группу, в которой будут выделяться пулы AVI для Virtual Services.

### virtualServicesCount
Количество резервируемых виртуальных сервисов (Virtual Services) на AVI.
Типичное значение: 1–10 в зависимости от нагрузки.

---

## 3. Рекомендованные характеристики

### Тестовое окружение (Test)

- Тип родительской услуги: `vdc`
- virtualServicesCount: 1–2
- ALB: выключен по умолчанию (включать только при необходимости тестирования Штурвала)
- segroup: задаётся только при включённом ALB
- VDC гарантии CPU/RAM: минимальные (поскольку изменить их после создания нельзя)

### Промышленное окружение (Production)

- Тип родительской услуги: `groupvdc` при работе с распределёнными нагрузками или `vdc` для локализованных проектов
- virtualServicesCount: 3–10, исходя из предполагаемого количества публикаций
- ALB: включён, если требуется высокая доступность сервисов или используется кластер Штурвал
- segroup: рекомендуется использовать выделенную группу под проект
- VDC гарантии CPU/RAM: повышенные, с учётом того, что изменить их после создания невозможно без пересоздания vDC

---

## 4. Выходные параметры

### Имя Edge
Уникальное название созданного сетевого шлюза, используется для ссылок и дальнейших операций.

### Имя routed-сети
Автоматически созданная routed-сеть с подсетью `10.10.102.0/24`, используется для подключения ресурсов.

### Параметры Edge
Набор параметров, указанных пользователем при создании: тип услуги, ALB, segroup, virtualServicesCount и др.
Используются в операциях modify и для анализа состояния сервиса.

---

## 5. Дополнительная информация

- В рамках сервиса **один раз при создании задаётся гарантированная доля CPU/RAM vDC**. Изменить её невозможно — требуется пересоздание vDC.
- Удаление Edge недоступно при наличии зависимых ресурсов (vApp, VM, кластер Штурвал).
- При использовании ALB необходимо корректно указывать segroup, чтобы обеспечить корректное выделение пулов AVI.
- При выборе режима `groupvdc` следует учитывать распределение нагрузки между несколькими vDC.
- SNAT для всех ресурсов внутри Edge можно активировать через операцию modify (параметры выделения VIP и ipSpace).

' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 10 - kind: instance - action: create - params: - - id: 8 - code: vdcUid - data_type: string - required: false - ref_svc_id: 21 - descr: UUID Услуги `Виртуальный датацентр (vDC)` - man: 'Необходимо, если выбран тип подключаемой VDC: `vdc`' - sort: 10 - - id: 340 - code: needEnableAVI - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Включение Load Balancer - man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать - sort: 30 - is_modifiable: true - - id: 341 - code: virtualServicesCount - data_type: integer > 0 - required: false - default: "1" - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые **резервируются** на AVI - man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` - sort: 50 - is_modifiable: true - - id: 621 - code: vdcType - data_type: string - required: true - default: vdc - value_list: - - vdc - - vdcGroup - descr: Тип родительской услуги - man: При выборе vdc обеспечивает сетевую доступность только в рамках этого vdc
При выборе groupvdc обеспечивает сетевую доступность между всеми vdc, которые включены в groupvdc - sort: 0 - - id: 622 - code: vdcGroupUid - data_type: string - required: false - ref_svc_id: 29 - descr: UUID Услуги `Группа виртуальных датацентров (groupvDC)` - man: 'Необходимо, если выбран тип подключаемой VDC: `groupvdc`' - sort: 20 - - id: 825 - code: qosProfile - required: false - descr: Параметр пока не работает
Должен возвращать поле из ресурсной платформы типа vc из .vcd.hardware.gatewayQoSProfiles

Как по идее должен работать.
Нужно заполнить или CFS vdcUid, или vdcGroupUid
Идеально конечно проверять `if (params.vdcType == "vdc" && params.vdcUid != "")` или `if (params.vdcType == "vdcGroup" && params.vdcGroupUid != "")`

Далее надо пойти по стейту vdc -> org -> resPlatform, взять gatewayQoSProfiles
Или пойти по стейту vdcgroup -> vdc -> org -> resPlatform, взять gatewayQoSProfiles

Регулярку могу написать (Виталя) - man: if (vdcUid != '") {
наборфункций1
}

elif (vdcGroupUid != "") {
наборфункций2
}

else {
return "Необходимо выбрать vdc или vdcgroup"
} - sort: 60 - depends_on: vdcUid,vdcGroupUid - is_modifiable: true - - name: delete - id: 25 - kind: instance - action: delete - man: Невозможно удалить если routed-сеть в Edge связана с:
- vApp
- VM
- Kubernetes-кластер Штурвал

Запустить операцию возможно, но будет ошибка - params: [] - - name: modify - id: 111 - kind: instance - action: modify - man: Для создания SNAT Правила убедитесь, что в организации есть свободные IP
Редактировать кол-во свободных IP можно в услуге `Организация в Cloud Director` -> `modify` - params: - - id: 368 - code: needEnableAVI - data_type: boolean - required: false - value_list: - - "false" - - "true" - descr: Включение Load Balancer - man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать - sort: 10 - - id: 369 - code: virtualServicesCount - data_type: integer > 0 - required: false - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые выделяются на AVI - man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` - sort: 30 - - id: 372 - code: ipSpaceName - data_type: string - required: false - descr: Имя ip Space для внешнего IP - man: Необходимо указывать, если включён параметр `Выделить VIP для SNAT` - sort: 50 - - id: 856 - code: qosProfile - data_type: string - required: false - sort: 40 - - name: reconcile - id: 252 - kind: action - action: reconcile - params: [] diff --git a/generated/prod/resources_yaml/23_vc_vm.yaml b/generated/prod/resources_yaml/23_vc_vm.yaml deleted file mode 100644 index 470873d..0000000 --- a/generated/prod/resources_yaml/23_vc_vm.yaml +++ /dev/null @@ -1,153 +0,0 @@ -name: vc_vm -service_id: 23 -service_display_name: VM в Cloud Director (старый формат) (vc_vm) -service_short_name: vc_vm -service_man: Виртуальная машина без каких-либо особенностей.
Данная услуга запускает vc_ip для выдачи белого адреса и подключения к ВМ -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 26 - kind: instance - action: create - params: - - id: 33 - code: vappUid - data_type: uuid - required: true - ref_svc_id: 26 - descr: InstanceUid vApp. vApp должен быть создан - man: InstanceUid vApp. vApp должен быть создан - sort: 10 - - id: 35 - code: vmName - data_type: string - required: true - default: vm0001 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: Имя ВМ. Не поддерживает спец-символы - man: Имя ВМ. Не поддерживает спец-символы - sort: 20 - - id: 36 - code: vmCpu - data_type: string - required: true - default: "2" - descr: CPU - man: CPU - sort: 30 - is_modifiable: true - - id: 37 - code: vmRam - data_type: string - required: true - default: "2" - descr: RAM (GB) - man: RAM (GB) - sort: 40 - is_modifiable: true - - id: 38 - code: vmDisksize - data_type: string - required: true - default: "10" - descr: Размер диска под сервис (GB) - man: Размер диска под сервис (GB) - sort: 50 - is_modifiable: true - - id: 176 - code: templateName - data_type: string - required: true - default: RockyLinux_9-16G - value_list: - - RockyLinux_9-16G - - OracleLinux_9-16G - - Ubuntu_24-20G - - Ubuntu_22-20G - descr: ОС - man: ОС - sort: 60 - - id: 177 - code: accessIpList - data_type: string - required: true - default: 79.111.119.78/32 185.247.187.1/32 - descr: Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов - man: IP адреса в CIDR формате через пробел для настройки аксес листов - sort: 70 - is_modifiable: true - - id: 406 - code: ipSpaceName - data_type: string - required: true - descr: Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) - - name: delete - id: 29 - kind: instance - action: delete - params: [] - - name: modify - id: 68 - kind: instance - action: modify - man: Операция автоматически останавливает ВМ. - params: - - id: 186 - code: vmCpu - data_type: integer > 0 - required: false - descr: CPU - man: CPU - sort: 10 - - id: 187 - code: vmRam - data_type: integer > 0 - required: false - descr: RAM - man: RAM - sort: 20 - - id: 188 - code: vmDisksize - data_type: integer > 0 - required: false - descr: Размер диска под сервис (GB) - man: Размер диска под сервис (GB) - sort: 30 - - id: 189 - code: accessIpList - data_type: string - required: false - descr: IP адреса в CIDR формате через пробел для настройки аксес листов - man: IP адреса в CIDR формате через пробел для настройки аксес листов - sort: 40 - - name: resume - id: 28 - kind: instance - action: resume - params: [] - - name: suspend - id: 27 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/25_vcexternalip.yaml b/generated/prod/resources_yaml/25_vcexternalip.yaml deleted file mode 100644 index 7d28de6..0000000 --- a/generated/prod/resources_yaml/25_vcexternalip.yaml +++ /dev/null @@ -1,165 +0,0 @@ -name: vcexternalip -service_id: 25 -service_display_name: Публичные IP адреса -service_short_name: vcexternalip -service_man: '**Обычно эта услуга разворачивается автоматически при развёртывании более сложных услуг**
Вручную создавать можно, однако услуга требует много параметров, о которых можно просто не знать

Резервировать публичные IP адреса можно только после создания NGcloud Organization, NGcloud Виртуальный датацентр, NGcloud Каталог, Сетевой шлюз периметра.
Вы можете назначить IP адрес для существующего экземпляра сервиса.' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 56 - kind: instance - action: create - params: - - id: 140 - code: serviceUid - data_type: string - required: true - sort: 20 - is_modifiable: true - - id: 141 - code: dnatCreate - data_type: boolean - required: true - default: "true" - descr: Необходимо создать DNAT-правило. - sort: 40 - is_modifiable: true - - id: 142 - code: internalPortAccess - data_type: string - required: true - default: "5432" - descr: Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
- sort: 60 - is_modifiable: true - - id: 143 - code: snatCreate - data_type: boolean - required: false - descr: Необходимо создать SNAT-правило.
- sort: 50 - is_modifiable: true - - id: 144 - code: internalAddrAccess - data_type: string - required: true - descr: Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true - sort: 70 - is_modifiable: true - - id: 324 - code: fromServiceNamespace - data_type: string - required: true - descr: Namespace составной услуги (для отображения правил FW) - sort: 80 - - id: 325 - code: fromServiceCloudEdgeName - data_type: string - required: false - descr: Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 90 - - id: 326 - code: fromServiceCloudVdcName - required: false - descr: Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 100 - - id: 327 - code: fromServiceCloudOrgName - data_type: string - required: false - descr: Org составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 110 - - id: 328 - code: fromServiceCloudVmwareUrl - data_type: string - required: false - descr: Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 120 - - id: 336 - code: ipSpaceName - data_type: string - required: true - descr: Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1) - sort: 30 - is_modifiable: true - - id: 488 - code: resourceRealm - data_type: string - required: true - default: ngcloud.ru - value_list: - - ngcloud.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 200 - - id: 624 - code: fromServiceCloudEdgeScope - data_type: string - required: false - sort: 130 - - id: 626 - code: fromServiceVdcGroupName - data_type: string - required: false - sort: 140 - - name: delete - id: 57 - kind: instance - action: delete - params: [] - - name: modify - id: 197 - kind: instance - action: modify - params: - - id: 627 - code: serviceUid - data_type: string - required: true - sort: 20 - - id: 628 - code: ipSpaceName - data_type: string - required: true - sort: 30 - - id: 629 - code: dnatCreate - data_type: boolean - required: true - sort: 40 - - id: 630 - code: snatCreate - data_type: boolean - required: true - sort: 50 - - id: 631 - code: internalPortAccess - data_type: string - required: true - sort: 60 - - id: 632 - code: internalAddrAccess - data_type: string - required: true - sort: 70 diff --git a/generated/prod/resources_yaml/26_vapp.yaml b/generated/prod/resources_yaml/26_vapp.yaml deleted file mode 100644 index fadb13f..0000000 --- a/generated/prod/resources_yaml/26_vapp.yaml +++ /dev/null @@ -1,74 +0,0 @@ -name: vapp -service_id: 26 -service_display_name: Виртуальный каталог ВМ (vApp) -service_short_name: vapp -service_man: '# Инструкция по развертыванию Виртуальный каталог ВМ (vApp) через платформу

## 1. Общая информация

**Виртуальный каталог ВМ (vApp)** — это логическая каталог, объединённая в единый управляемый объект. Сервис предназначен для совместного развертывания, управления жизненным циклом и сетевого взаимодействия набора связанных ВМ. Основные функции включают:
- централизованное управление включением, выключением и приостановкой группы ВМ;
- унифицированный сетевой доступ через привязанный Edge-шлюз;

### Доступные операции

**create** — Создание нового vApp с привязкой к заданным услугам VDC и Edge
**delete** — Удаление существующего vApp. Требуется предварительный перевод vApp в состояние *suspend*. Полное удаление возможно через 14 дней.
**suspend** — Остановка vApp (выключение всех ВМ внутри каталога)
**resume** — Запуск vApp (включение всех ВМ внутри каталога)

---

## 2. Параметры развертывания

Параметры берутся из операции **create**.

### UUID Услуги VDC
Уникальный идентификатор услуги «Виртуальный датацентр (vDC)».
Используется для определения вычислительной инфраструктуры, в которой будет развернут vApp.

### UUID Услуги Edge
Уникальный идентификатор услуги «Сетевой шлюз периметра (Edge)».
Обеспечивает маршрутизацию и сетевое подключение. Может быть привязан к группе VDC, поэтому выбирается отдельно.

### Имя vApp
Строгое имя, соответствующее маске формирования DNS-имени.
Используется как часть в DNS-имени при создании виртуальных машин.

---

## 3. Рекомендованные характеристики
Так как является промежуточным этапом для создания ВМ, все характеристики описываются в услуге `Виртуальная машина`

---

## 4. Выходные параметры

### Параметры vApp
Заданные при создании vApp

---

## 5. Дополнительная информация

- Привязка к VDC и Edge осуществляется раздельно, поскольку один Edge может обслуживать несколько VDC (через услугу `Группа виртуальных датацентров (groupvDC)`). Это гарантирует возможность развернуть ВМ в единой сети, но в разных виртуальных датацентрах.
- Перед удалением vApp необходимо вручную перевести его в состояние **suspend**. Удаление возможно только спустя 14 дней после этого шага.
- Операция delete удаляет весь vApp полностью, включая все виртуальные машины, связанные с ним.
- Рекомендуется заранее планировать сетевые связи и назначения DNS-имён для обеспечения корректной работы сервисов, развернутых внутри vApp.
- При проектировании инфраструктуры следует учитывать зависимости между Edge-шлюзом, VDC и размещаемыми ВМ, чтобы избежать ограничений при масштабировании и интеграции.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 69 - kind: instance - action: create - params: - - id: 190 - code: nsxtUid - data_type: uuid - required: true - ref_svc_id: 22 - descr: UUID Услуги `Сетевой шлюз периметра (Edge)` - man: К какой сети может быть подключен виртуальный каталог - sort: 20 - - id: 191 - code: vappName - data_type: string - required: true - default: 3ecobacg - regex: ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$ - minlength: 3 - descr: Уникальное в рамках организации - man: Имеет строгую маску `^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$`

Используется в формировании DNS-Имени (для виртуальных машин) - sort: 30 - - id: 623 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - descr: UUID Услуги `Виртуальный датацентр (vDC)` - man: В каких ресурсах будет создан виртуальный каталог - sort: 10 - - name: delete - id: 70 - kind: instance - action: delete - params: [] - - name: resume - id: 209 - kind: instance - action: resume - params: [] - - name: suspend - id: 208 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/27_vc_vm_v2.yaml b/generated/prod/resources_yaml/27_vc_vm_v2.yaml deleted file mode 100644 index 2b1d235..0000000 --- a/generated/prod/resources_yaml/27_vc_vm_v2.yaml +++ /dev/null @@ -1,71 +0,0 @@ -name: vc_vm_v2 -service_id: 27 -service_display_name: VM в Cloud Director (vc_vmV2) -service_short_name: vc_vm_v2 -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 100 - kind: instance - action: create - params: - - id: 301 - code: serverListJson - data_type: json - required: true - default: '[{}]' - maxlength: 65536 - minlength: 10 - descr: Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) - sort: 20 - is_modifiable: true - - name: delete - id: 101 - kind: instance - action: delete - params: [] - - name: modify - id: 104 - kind: instance - action: modify - params: - - id: 302 - code: serverListJson - data_type: json - required: true - default: '[{}]' - maxlength: 65536 - minlength: 10 - descr: Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) - sort: 10 - - name: resume - id: 102 - kind: instance - action: resume - params: [] - - name: suspend - id: 103 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/28_vc_vm_v3.yaml b/generated/prod/resources_yaml/28_vc_vm_v3.yaml deleted file mode 100644 index f05d808..0000000 --- a/generated/prod/resources_yaml/28_vc_vm_v3.yaml +++ /dev/null @@ -1,278 +0,0 @@ -name: vc_vm_v3 -service_id: 28 -service_display_name: Виртуальная машина -service_short_name: vc_vm_v3 -service_man: '# Документация по разворачиванию ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\ложь, определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed" (В нем не будет работать сбор метрик zabbix).

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 132 - kind: instance - action: create - man: '# Документация по использованию услуги развёртывания ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\лож определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed".

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' - params: - - id: 407 - code: vappUid - data_type: uuid - required: true - ref_svc_id: 26 - sort: 10 - - id: 408 - code: vmName - data_type: string - required: true - default: web01 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - sort: 20 - - id: 409 - code: vmCpu - data_type: integer > 0 - required: true - default: "1" - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 30 - is_modifiable: true - - id: 410 - code: vmRam - data_type: integer > 0 - required: true - default: "1" - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 40 - is_modifiable: true - - id: 411 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 50 - is_modifiable: true - - id: 412 - code: ipSpaceName - data_type: string - required: true - sort: 60 - depends_on: vappUid - is_modifiable: true - - id: 413 - code: accessIpList - data_type: json - required: false - regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 70 - is_modifiable: true - - id: 414 - code: imageVm - data_type: string - required: true - sort: 90 - depends_on: vappUid - - id: 415 - code: cloudInit - data_type: yaml - required: false - descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. - sort: 200 - - id: 416 - code: userLogin - required: true - default: myuser - descr: Учётка SSH - sort: 180 - - id: 417 - code: userPublicKey - data_type: string - required: true - descr: Публичная часть SSH-Ключа для пользователя - sort: 190 - - id: 448 - code: accessPortList - data_type: string - required: true - default: '["22"]' - descr: 'По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX' - sort: 80 - is_modifiable: true - - name: delete - id: 133 - kind: instance - action: delete - params: [] - - name: modify - id: 159 - kind: instance - action: modify - params: - - id: 493 - code: vmCpu - data_type: integer > 0 - required: false - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 20 - - id: 494 - code: vmRam - data_type: integer > 0 - required: false - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 30 - - id: 495 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 40 - - id: 496 - code: ipSpaceName - data_type: string - required: false - value_list: - - no-needed - - internet-antiddos-v1 - - internet-no-antiddos-v1 - - internet-ipv4-v1 - descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev - sort: 50 - - id: 497 - code: accessIpList - data_type: json - required: false - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 60 - - id: 498 - code: accessPortList - data_type: json - required: false - descr: Формат массива
По каким портам открыть доступ к ВМ.
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX - sort: 70 - - name: reconcile - id: 286 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 164 - kind: action - action: redeploy - params: - - id: 509 - code: vmName - data_type: string - required: true - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - sort: 10 - - id: 510 - code: vmCpu - data_type: integer > 0 - required: true - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 20 - - id: 511 - code: vmRam - data_type: integer > 0 - required: true - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 30 - - id: 512 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 40 - - id: 513 - code: ipSpaceName - data_type: string - required: false - value_list: - - no-needed - - internet-antiddos-v1 - - internet-no-antiddos-v1 - - internet-ipv4-v1 - descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev - sort: 50 - - id: 514 - code: accessIpList - data_type: json - required: false - regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 60 - - id: 515 - code: imageVm - data_type: string - required: true - value_list: - - RockyLinux_9-16G-cloudinit - - Ubuntu_22-20G - - Debian_13-20G - sort: 80 - - id: 516 - code: userLogin - data_type: string - required: true - descr: Учётка SSH - sort: 180 - - id: 517 - code: userPublicKey - data_type: string - required: true - descr: Публичная часть SSH-Ключа для пользователя - sort: 190 - - id: 518 - code: cloudInit - data_type: yaml - required: false - descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. - sort: 200 - - id: 520 - code: accessPortList - data_type: json - required: false - descr: По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX - sort: 70 - - name: resume - id: 135 - kind: instance - action: resume - params: [] - - name: suspend - id: 134 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/29_vc_vdc_group.yaml b/generated/prod/resources_yaml/29_vc_vdc_group.yaml deleted file mode 100644 index 21afd35..0000000 --- a/generated/prod/resources_yaml/29_vc_vdc_group.yaml +++ /dev/null @@ -1,74 +0,0 @@ -name: vc_vdc_group -service_id: 29 -service_display_name: Группа датацентров -service_short_name: vcVdcGroup -service_man: '# Инструкция по развертыванию Группа виртуальных датацентров (groupvDC) через платформу

## 1. Общая информация

Сервис **Группа виртуальных датацентров (groupvDC)** предназначен для логического объединения нескольких виртуальных датацентров (vDC) в единую группу. Это позволяет растягивать SDN между разными vDC и централизованно управлять сетевой связностью. Управление инфраструктурой осуществляется через Cloud Director.
Сервис применяется в случаях, когда требуется обеспечить единое сетевое пространство для распределенных виртуальных датацентров.

### Доступные операции

**create** — Создание новой группы виртуальных датацентров.
**delete** — Удаление существующей группы vDC.
**add_vdc** — Добавление дополнительного виртуального датацентра в группу.
**remove_vdc** — Удаление ранее добавленного виртуального датацентра из группы.

---

## 2. Параметры развертывания

Параметры операции **create**:

### UUID VDC
Идентификатор виртуального датацентра, который будет являться первичным в группе.
Описание: требуется предварительно создать виртуальный датацентр с помощью услуги `Виртуальный датацентр (vDC)`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Для тестового окружения создавать группу VDC нет необходимости, так как это создаёт дополнительный слой абстракции в Cloud Director

### Промышленное окружение
- Для промышленного окружения, если в тенанте существуют разные VDC, создание groupvdc позволит снизить сетевые издержки (за счёт общей сети)

---

## 4. Выходные параметры

### Параметры groupvdc
Набор параметров, указанных пользователем при создании

---

## 5. Дополнительная информация
- Группа vDC используется для растягивания SDN между несколькими виртуальными датацентрами. Управление всеми сетевыми элементами внутри группы выполняется Cloud Director.
- Удалить первичный vDC (тот, что был указан при операции **create**) невозможно. Для его удаления необходимо удалить всю группу и создать новую.
- Перед добавлением любого vDC в группу он должен быть предварительно развернут через сервис `Виртуальный датацентр (vDC)`.
- Сети создаются не в этой услуге, а в услуге `Сетевой шлюз периметра (Edge)`. В ней необходимо будет выбрать тип `groupvdc`' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: add_vdc - id: 192 - kind: subresource - action: add - subresource: vdc - man: '**Не нужно** выбирать UUID VDC, который уже был добавлен в группу' - params: - - id: 619 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - sort: 10 - - name: create - id: 191 - kind: instance - action: create - man: '**VDC Группа** нужна для объединения всех VDC в организации в одну логическую группу.
Это позволит, к примеру, создать **общую сеть** для всех VDC в организации

- Для создания группы VDC необходимо указать **инстанс** услуги VDC (`Виртуальный датацентр`)
- **Важный момент** - удалить эту VDC из группы нельзя. Если такая потребность появится, необходимо будет **ПОЛНОСТЬЮ** удалить группу VDC и уже потом создать новую' - params: - - id: 618 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - sort: 10 - - name: delete - id: 195 - kind: instance - action: delete - params: [] - - name: reconcile - id: 251 - kind: action - action: reconcile - params: [] - - name: remove_vdc - id: 193 - kind: subresource - action: remove - subresource: vdc - params: - - id: 620 - code: vdcName - data_type: string - required: true - sort: 10 diff --git a/generated/prod/resources_yaml/2_template.yaml b/generated/prod/resources_yaml/2_template.yaml deleted file mode 100644 index 6cbc676..0000000 --- a/generated/prod/resources_yaml/2_template.yaml +++ /dev/null @@ -1,221 +0,0 @@ -name: template -service_id: 2 -service_display_name: Темплейт k8s -service_short_name: template -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 74 - kind: instance - action: create - params: - - id: 813 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 173 - code: memory - data_type: integer > 0 - required: true - default: "128" - is_modifiable: false - - id: 174 - code: replicas - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 172 - code: cpu - data_type: integer > 0 - required: true - default: "100" - is_modifiable: false - - id: 175 - code: disk - data_type: integer > 0 - required: true - default: "5" - is_modifiable: false - - id: 814 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 176 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - is_modifiable: false - - id: 815 - code: accessConfiguration - data_type: map-fixed - required: true - is_modifiable: true - sub_params: - - id: 182 - code: slaveAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - id: 177 - code: type - data_type: string - required: true - default: ingress - value_list: - - ingress - - service - is_modifiable: false - - id: 178 - code: domain - data_type: string - required: true - default: abracadabra-8htr2l6c - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 179 - code: masterIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 180 - code: masterAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - id: 181 - code: slaveIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - name: delete - id: 75 - kind: instance - action: delete - params: [] - - name: modify - id: 76 - kind: instance - action: modify - params: - - id: 816 - code: clusterConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 184 - code: memory - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 186 - code: replicas - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 183 - code: cpu - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 185 - code: disk - data_type: integer >= 0 - required: true - default: "" - is_modifiable: false - - id: 817 - code: accessConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 187 - code: type - data_type: string - required: true - default: "" - value_list: - - ingress - - service - is_modifiable: false - - id: 188 - code: domain - data_type: string - required: true - default: "" - is_modifiable: false - - id: 189 - code: masterIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 191 - code: masterAccessList - data_type: json - required: true - default: "" - is_modifiable: false - - id: 190 - code: slaveIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 192 - code: slaveAccessList - data_type: json - required: true - default: "" - is_modifiable: false - - name: reconcile - id: 298 - kind: action - action: reconcile - params: [] - - name: resume - id: 98 - kind: instance - action: resume - params: [] - - name: suspend - id: 97 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/50_nextcloud.yaml b/generated/prod/resources_yaml/50_nextcloud.yaml deleted file mode 100644 index 0e22774..0000000 --- a/generated/prod/resources_yaml/50_nextcloud.yaml +++ /dev/null @@ -1,483 +0,0 @@ -name: nextcloud -service_id: 50 -service_display_name: Nextcloud -service_short_name: nextcloud -service_man: 'Ресурсная платформа: k8s. Автоматизация: helm chart' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 16 - kind: instance - action: create - man: Для установки необходимо:
- ресурсная платформа, где разворачивается Nextcloud, обязана совпадать с ресурсными платформами, где развёрнут Redis и PostgreSQL - params: - - id: 1055 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 492 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 493 - code: psqlEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 494 - code: redisEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 495 - code: psqlUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета` - is_modifiable: false - - id: 496 - code: redisUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета` - is_modifiable: false - - id: 1056 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 497 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 498 - code: memory - data_type: integer > 0 - required: true - default: "1024" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 499 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - is_modifiable: false - - id: 500 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 1057 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 501 - code: domain - data_type: string - required: true - default: nextcloud-f6pb9obd - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1058 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 502 - code: version - data_type: string - required: true - default: 33.0.4 - value_list: - - 33.0.4 - is_modifiable: false - - id: 503 - code: documentServer - data_type: string - required: true - default: collabora - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - id: 549 - code: logLevel - data_type: string - required: true - default: INFO - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - name: create_backup - id: 404 - kind: subresource - action: create - subresource: backup - man: Перед запуском обязательно надо перевести экземпляр Nextcloud в статус 'suspended' - params: - - id: 1077 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 578 - code: veleroUid - data_type: string - required: true - default: "" - man: Предварительно необходимо создать инстанс услуги `Velero`
Из этого инстанса будут взяты точки восстановления
Если восстановление производится на другую ресурсную платформу, важно чтобы предварительно все компоненты связующие были либо перенесены, либо были доступны снаружи - is_modifiable: false - - name: delete - id: 17 - kind: instance - action: delete - man: DEV-Ветка удаления Nextcloud - params: [] - - name: modify - id: 67 - kind: instance - action: modify - params: - - id: 1059 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 504 - code: cpu - data_type: integer > 0 - required: true - default: "" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 505 - code: memory - data_type: integer > 0 - required: true - default: "" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 506 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - is_modifiable: false - - id: 507 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 1060 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 508 - code: version - data_type: string - required: true - default: "" - value_list: - - 33.0.4 - is_modifiable: false - - id: 509 - code: documentServer - data_type: string - required: true - default: "" - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - id: 550 - code: logLevel - data_type: string - required: true - default: "" - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - name: reconcile - id: 390 - kind: action - action: reconcile - params: [] - - name: recovery - id: 406 - kind: action - action: recovery - params: - - id: 1078 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 557 - code: psqlEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 558 - code: redisEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 560 - code: psqlUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета`
Если откат производится на ту же версию Nextcloud, в теории возможно развернуться в существующую базу

Предпочтительно создать отдельный инстанс PostgreSQL, который был восстановлен также через Recovery - is_modifiable: false - - id: 561 - code: redisUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета` - is_modifiable: false - - id: 556 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 570 - code: veleroUid - data_type: string - required: true - default: "" - man: Из этого инстанса будут взяты точки восстановления
Если восстановление производится на другую ресурсную платформу, важно чтобы предварительно все компоненты связующие были либо перенесены, либо были доступны снаружи - is_modifiable: false - - id: 559 - code: recoveryPoint - data_type: string - required: true - default: "" - is_modifiable: false - - id: 1079 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 564 - code: replicas - data_type: integer > 0 - required: true - default: 1,2 - is_modifiable: false - - id: 565 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 562 - code: cpu - data_type: integer > 0 - required: true - default: "" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 563 - code: memory - data_type: integer > 0 - required: true - default: "" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 1080 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 566 - code: domain - data_type: string - required: true - default: nextcloud-zzopjlus - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1081 - code: appConfiguration - data_type: map-fixed - required: true - man: Версия Nextcloud будет взята из бэкапа - sort: 40 - sub_params: - - id: 567 - code: documentServer - data_type: string - required: true - default: collabora - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - id: 568 - code: logLevel - data_type: string - required: true - default: NONE - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - id: 569 - code: version - data_type: string - required: false - default: "" - man: Версия Nextcloud будет исправлена на ту версию, которая указана в бэкапе - is_modifiable: false - - name: restart - id: 66 - kind: action - action: restart - params: [] - - name: resume - id: 129 - kind: instance - action: resume - params: [] - - name: suspend - id: 128 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/81_superset.yaml b/generated/prod/resources_yaml/81_superset.yaml deleted file mode 100644 index 8d4b11e..0000000 --- a/generated/prod/resources_yaml/81_superset.yaml +++ /dev/null @@ -1,107 +0,0 @@ -name: superset -service_id: 81 -service_display_name: Apache Superset -service_short_name: superset -service_man: 'Apache Superset как сервис.
Ресурсная платформа: k8s.
Автоматизация: k8s operator.
Авторизация: 2FA keycloak (для доменного логина @mgmt.nubes.ru)' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 12 - kind: instance - action: create - man: DEV-Ветка сборки Superset - params: - - id: 14 - code: domain - data_type: string - required: true - default: superset-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 20 - code: emails - data_type: string - required: true - default: testuser1 - descr: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел - man: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел - sort: 70 - - id: 96 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро - man: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро - sort: 30 - - id: 97 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 40 - - id: 98 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - - id: 109 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 227 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 60 - - name: delete - id: 13 - kind: instance - action: delete - man: DEV-Ветка удаления Superset - params: [] diff --git a/generated/prod/resources_yaml/82_harbor.yaml b/generated/prod/resources_yaml/82_harbor.yaml deleted file mode 100644 index 3ab96c1..0000000 --- a/generated/prod/resources_yaml/82_harbor.yaml +++ /dev/null @@ -1,98 +0,0 @@ -name: harbor -service_id: 82 -service_display_name: Container Registry -service_short_name: harbor -service_man: 'Расширенный реестр для управления Docker-образами с enterprise-функциями безопасности, репликации и контроля доступа.
Container Registry позволяет хранить, развертывать и управлять Docker-образами контейнеров в инфраструктуре NGcloud.
Сервис можно использовать в процессе разработки контейнеров. Например, вы можете настроить систему автоматизации на работу с реестром в Container Registry в рамках процесса непрерывной интеграции. Контейнеры идеально подходят для запуска небольших, независимых и легко изменяемых сервисов.
Чтобы начать работу, создайте реестр и загрузите в него Docker-образ
Авторизация: keycloak' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 14 - kind: instance - action: create - man: DEV-Ветка сборки Harbor - params: - - id: 16 - code: domain - data_type: string - required: true - default: harbor-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 20 - - id: 110 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 30 - - id: 228 - code: resourceCPU - data_type: integer > 0 - required: true - default: "100" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 40 - - id: 229 - code: resourceMemory - data_type: integer > 0 - required: true - default: "200" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 50 - - id: 230 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 60 - - id: 231 - code: s3Uid - data_type: uuid - required: true - ref_svc_id: 12 - descr: UUID экземпляра услуги S3 - man: UUID экземпляра услуги S3 - sort: 10 - - name: delete - id: 15 - kind: instance - action: delete - man: DEV-Ветка удаления Harbor - params: [] diff --git a/generated/prod/resources_yaml/86_k8s_velero.yaml b/generated/prod/resources_yaml/86_k8s_velero.yaml deleted file mode 100644 index b3d6c3e..0000000 --- a/generated/prod/resources_yaml/86_k8s_velero.yaml +++ /dev/null @@ -1,196 +0,0 @@ -name: k8s_velero -service_id: 86 -service_display_name: Velero -service_short_name: k8sVelero -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 332 - kind: instance - action: create - params: - - id: 962 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 334 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 336 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 337 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - - "3" - is_modifiable: false - - id: 963 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 338 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - is_modifiable: false - - id: 964 - code: nodeAgentConfiguration - data_type: map-fixed - required: true - man: Реплик выставляется автоматически столько, сколько Worker-узлов в кластере - sort: 30 - is_modifiable: true - sub_params: - - id: 551 - code: cpu - data_type: integer > 0 - required: true - default: "2000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 552 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 1076 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 555 - code: s3BucketUid - data_type: uuid - required: true - default: "" - man: Экземпляр S3-Бакета обязателен
Учётная запись будет создана автоматически, если её нету - is_modifiable: false - - name: delete - id: 331 - kind: instance - action: delete - params: - - id: 1082 - code: deleteS3Bucket - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - sort: 10 - - name: modify - id: 334 - kind: instance - action: modify - params: - - id: 965 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 347 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 348 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - - "3" - is_modifiable: false - - id: 345 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 966 - code: nodeAgentConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 554 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 553 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - name: reconcile - id: 336 - kind: action - action: reconcile - params: [] - - name: resume - id: 335 - kind: instance - action: resume - params: [] - - name: suspend - id: 333 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/88_k8s_ziti_controller.yaml b/generated/prod/resources_yaml/88_k8s_ziti_controller.yaml deleted file mode 100644 index 58dd3d0..0000000 --- a/generated/prod/resources_yaml/88_k8s_ziti_controller.yaml +++ /dev/null @@ -1,93 +0,0 @@ -name: k8s_ziti_controller -service_id: 88 -service_display_name: k8sZitiController -service_short_name: k8sZitiController -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 99 - kind: instance - action: create - params: - - id: 294 - code: resourceCPU - data_type: integer > 0 - required: true - default: "300" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (milicores) - sort: 30 - - id: 295 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (MBytes) - sort: 40 - - id: 296 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - - id: 297 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 60 - - id: 298 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 20 - - id: 299 - code: domain - data_type: string - required: true - default: k8sZitiController-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - name: delete - id: 105 - kind: instance - action: delete - params: [] diff --git a/generated/prod/resources_yaml/89_flask.yaml b/generated/prod/resources_yaml/89_flask.yaml deleted file mode 100644 index a4e70fb..0000000 --- a/generated/prod/resources_yaml/89_flask.yaml +++ /dev/null @@ -1,238 +0,0 @@ -name: flask -service_id: 89 -service_display_name: Web-сервер с фреймворком Flask -service_short_name: flask -service_man: '# Инструкция по развертыванию Web-сервер с фреймворком Flask через платформу

## 1. Общая информация

Сервис **Web-сервер с фреймворком Flask** предназначен для развертывания веб-приложений на базе Flask.

### Доступные операции

create — включить сервис, развернуть веб-приложение с указанными параметрами.
delete — удалить сервис, только после suspend.
modify — изменить настройки сервиса и переразвернуть.
suspend — выключить веб-приложение, остановить обслуживание.
resume — включить веб-приложение после suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Имя домена
Описание: доменное имя для доступа к веб-приложению. Если указан FQDN, происходит проверка DNS на валидность А-записи. А-запись должна указывать на балансировщик ресурсной платформы. Если указано имя в рамках одного октета, оно будет добавлено в публичную DNS-зону.
Пример значения: строка, допустимые FQDN или имя в одном октете. Пример: example.com или myapp.

### Платформа для развертывания
Описание: кластер Kubernetes, в котором будет развернуто приложение.
Пример значения: идентификатор кластера Kubernetes.

### Квота ядра пода
Описание: количество CPU, выделяемое на под, в Milicores.
Пример значения: целое число, например 500 для 0.5 ядра.

### Квота памяти пода
Описание: количество памяти, выделяемое на под, в Megabytes.
Пример значения: целое число, например 512 для 512 MB.

### Кол-во реплик пода
Описание: количество инстансов пода для масштабирования.
Пример значения: целое число, шт, например 2.

### URL Проекта (gitpath)
Описание: полный HTTPS путь до проекта в Git-репозитории.
Пример значения: URL в формате https://github.com/myuser/myproject.git. Доступ к репозиторию должен быть публичным.

### URL HealthCheck
Описание: путь для проверки здоровья приложения внутри пода.
Пример значения: строка, путь, например /api/v1/health.

### Переменные окружения пода
Описание: структура данных, которая будет передана как переменные окружения в под.
Пример значения: ключ-значение, например {"ENV": "production"}.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Квота ядра пода: 300m (0.3 ядра)
- Квота памяти пода: 256 MB
- Кол-во реплик пода: 1

### Промышленное окружение
- Квота ядра пода: от 1000m (1 ядро) в зависимости от нагрузки
- Квота памяти пода: от 1024 MB (1 GB) с учетом потребностей приложения
- Кол-во реплик пода: от 2 для обеспечения отказоустойчивости

---

## 4. Выходные параметры
Операция create не возвращает явных выходных параметров. Успешное выполнение операции приводит к развертыванию сервиса, доступного по указанному домену.

---

## 5. Дополнительная информация
- В корне проекта должен находиться файл requirements.txt со списком зависимостей Python.
- Исходный код веб-приложения должен располагаться в директории site.
- Запуск веб-сервера осуществляется командой python app.py.
- Используется версия Python: python:3.12-slim.
- Git-репозиторий должен быть публично доступен для платформы.
- Операция delete возможна только после suspend.
- Операция modify приводит к переразвертыванию с новыми параметрами.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 376 - kind: instance - action: create - params: - - id: 1037 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 460 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1038 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 461 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 462 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 463 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1039 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 464 - code: domain - data_type: string - required: true - default: flask-whmcbvdz - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1040 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 467 - code: version - data_type: string - required: true - default: "3.12" - value_list: - - "3.12" - is_modifiable: false - - id: 465 - code: gitPath - data_type: string - required: true - default: https://github.com/Foxyhhd/Baldurs-Gate-test.git - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 466 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1041 - code: jsonEnv - data_type: map - required: false - default: '{}' - sort: 50 - is_modifiable: true - - name: delete - id: 379 - kind: instance - action: delete - params: [] - - name: modify - id: 375 - kind: instance - action: modify - params: - - id: 1042 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 468 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 469 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 470 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1043 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 473 - code: version - data_type: string - required: true - default: "" - value_list: - - "3.12" - is_modifiable: false - - id: 471 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 472 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1044 - code: jsonEnv - data_type: map - required: false - sort: 30 - - name: reconcile - id: 381 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 380 - kind: action - action: redeploy - params: - - id: 1045 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 474 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: resume - id: 378 - kind: instance - action: resume - params: [] - - name: suspend - id: 377 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/90_postgres.yaml b/generated/prod/resources_yaml/90_postgres.yaml deleted file mode 100644 index 0430e02..0000000 --- a/generated/prod/resources_yaml/90_postgres.yaml +++ /dev/null @@ -1,816 +0,0 @@ -name: postgres -service_id: 90 -service_display_name: PostgreSQL -service_short_name: postgres -service_man: '# Инструкция по развертыванию PostgreSQL через платформу

---
## 1. Общая информация

PostgreSQL — это надёжная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая расширенный набор функций, транзакции и хранение сложных типов данных. Развёртывание полностью автоматизировано платформой на базе Zalando Postgres Operator с использованием Kubernetes.

## Доступные операции

- **create** — Создание кластера
- **delete** — Удаление кластера
- **modify** — Модификация кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера
- **restart** — Перезапуск кластера
- **recovery** — Восстановление из резервной копии

- **create_user** — Создание пользователя
- **delete_user** — Удаление пользователя

- **create_database** — Создание базы данных
- **delete_database** — Удаление базы данных


---
## 2. Параметры развертывания кластера

Ниже перечислены параметры, которые необходимо указать при создании ресурса PostgreSQL:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество инстансов (реплик) в кластере PostgreSQL. Пример: `2` (master + slave).

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

* **Выделяет внешний IP для доступа к slave:**
Флаг для выделения внешнего IP для slave (реплики чтения). Работает, если slave присутствует в кластере.

* **Имя ipSpace для публикации внешнего IP slave:**
Если включен параметр для slave, то поле необходимо заполнить.

* **UUID экземпляра услуги S3:**
UUID экземпляра S3 для хранения резервных копий.

* **Версия PostgreSQL:**
Версия PostgreSQL для развертывания. Пример: `16`

* **Расписание бэкапов (ext_BACKUP_SCHEDULE):**
Необязательный. Cron-выражение для времени запуска бэкапов. По умолчанию: `0 0 * * *` (ежедневно в полночь).

* **Количество сохраняемых бэкапов (ext_BACKUP_NUM_TO_RETAIN):**
Необязательный. Количество сохраняемых бэкапов. По умолчанию: `7`

* **PgPooler (master / slave):**
Флаги включения PgBouncer для подключений на запись и чтение. При включении создаётся отдельный под.

* **Разрешить подключение без SSL (allowNoSSL):**
По умолчанию подключение с sslmode=require. При `true` разрешается подключение без SSL.

* **Автоскейлинг PV (autoScale):**
Включает автоскейлинг тома. Требует настройки autoScalePercentage, autoScaleTechWindow, autoScaleQuotaGb.

* **Дополнительные параметры PostgreSQL (jsonParameters):**
JSON с переменными конфигурации PostgreSQL. Документация: https://www.postgresql.org/docs/current/runtime-config.html

---
## 3. Выходные параметры

После успешного развертывания ресурса PostgreSQL платформа возвращает ряд параметров.

#### externalConnect

Внешние эндпоинты для подключения к кластеру:
- **master** — для операций записи (host: `write.${namespace}`, port: 5432)
- **slave** — для операций чтения (host: `read.${namespace}`, port: 5432)

Пример ответа платформы:

```json
{
"slave": {
"ip": "",
"fqdn": "",
"uuid": "",
"isExternal": false
},
"master": {
"ip": "<ip>",
"fqdn": "write.<namespace>.<cluster-domain>",
"uuid": "<uuid>",
"isExternal": false
}
}
```

Поля: `ip` — внешний IP; `fqdn` — полное имя хоста; `uuid` — идентификатор; `isExternal` — признак внешнего доступа.

#### internalConnect

Внутренние эндпоинты (FQDN) для подключения из других сервисов внутри той же сети Kubernetes.

Пример ответа платформы:

```json
{
"slave": "",
"master": {
"slave": "",
"master": {
"slave": "",
"master": "postgresqlk8s-master.<namespace>.svc.cluster.local"
}
}
}
```

Для подключения используйте значение `master.master.master` (host для записи) или соответствующее значение slave (host для чтения).

Для подключения используется порт **5432**.

---
## 4. Управление пользователями

### create_user — Создание пользователя

* **Имя пользователя (username):**
Имя создаваемого пользователя. Используется для подключения к базе и настройки прав.
Пример: `myapp_user`

* **Роль (role):**
Тип роли пользователя. Допустимые значения:
- `app_user` — базовые права для DML-операций (SELECT, INSERT, UPDATE, DELETE)
- `ddl_user` — расширенные права: createdb, createrole (управление базами и ролями)

Защищённые имена, которые нельзя использовать: `admin`, `postgres`, `standby`.

### delete_user — Удаление пользователя

* **Имя пользователя (username):**
Имя удаляемого пользователя. Пример: `myapp_user`

Пользователя нельзя удалить, если он является владельцем базы данных. Сначала удалите или передайте владение базами данных другому пользователю.

---
## 5. Управление базами данных

### create_database — Создание базы данных

* **Имя базы данных (dbName):**
Название создаваемой базы данных. Пример: `myapp_db`

* **Владелец базы данных (dbOwner):**
Имя пользователя-владельца базы. Должен существовать в кластере (создан через create_user). Пример: `myapp_user`

### delete_database — Удаление базы данных

* **Имя базы данных (dbName):**
Название удаляемой базы данных. Пример: `myapp_db`
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 19 - kind: instance - action: create - man: руководство - params: - - id: 788 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 104 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 105 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 106 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 107 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 789 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 108 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 790 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 111 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 112 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 109 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 110 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 791 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 113 - code: version - data_type: string - required: true - default: "17" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 116 - code: sslRequired - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 114 - code: poolerMaster - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 115 - code: poolerSlave - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 792 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 50 - is_modifiable: true - sub_params: - - id: 118 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 117 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 793 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - is_modifiable: true - sub_params: - - id: 119 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 120 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 121 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 794 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 123 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 124 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 125 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 122 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - name: create_backup - id: 264 - kind: subresource - action: create - subresource: backup - params: [] - - name: create_database - id: 245 - kind: subresource - action: create - subresource: database - params: - - id: 741 - code: dbName - data_type: string - required: true - regex: ^[A-Za-z0-9]+$ - - id: 742 - code: dbOwner - data_type: string - required: true - - name: create_user - id: 241 - kind: subresource - action: create - subresource: user - params: - - id: 733 - code: username - data_type: string - required: true - regex: ^[A-Za-z0-9]+$ - maxlength: 62 - minlength: 2 - - id: 740 - code: role - data_type: string - required: true - value_list: - - app_user - - ddl_user - descr: Выбрать роль из списка - man: '`app_user` — стандартный пользователь с базовым набором прав
`ddl_user` — пользователь с расширенными правами, включая управление ролями и базами данных' - - name: delete - id: 20 - kind: instance - action: delete - params: - - id: 457 - code: deleteS3Bucket - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Также удалить технический S3 Bucket с бэкапами? - sort: 10 - - name: delete_database - id: 246 - kind: subresource - action: delete - subresource: database - params: - - id: 743 - code: dbName - data_type: string - required: true - - name: delete_user - id: 244 - kind: subresource - action: delete - subresource: user - params: - - id: 739 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - name: modify - id: 48 - kind: instance - action: modify - params: - - id: 795 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 126 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 127 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 128 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 129 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 796 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 130 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 131 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 132 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 133 - code: slaveAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 797 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 134 - code: version - data_type: string - required: true - default: "" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 135 - code: sslRequired - data_type: boolean - required: true - default: false,true - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 136 - code: poolerMaster - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 137 - code: poolerSlave - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 798 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 40 - sub_params: - - id: 271 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 272 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 799 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 193 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 138 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 139 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 800 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 140 - code: enabled - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 141 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 142 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 143 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - name: reconcile - id: 263 - kind: action - action: reconcile - params: [] - - name: recovery - id: 47 - kind: action - action: recovery - params: - - id: 801 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 166 - code: recoveryPoint - data_type: string - required: true - default: "" - is_modifiable: false - - id: 144 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 802 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 145 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 146 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 147 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 148 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 803 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 150 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 151 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 152 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 149 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 804 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 155 - code: poolerMaster - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 156 - code: poolerSlave - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 153 - code: version - data_type: string - required: true - default: "17" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 154 - code: sslRequired - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 805 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 50 - sub_params: - - id: 475 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 476 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 806 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 159 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 160 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 161 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 807 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - sub_params: - - id: 164 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 165 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 162 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 163 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - name: restart - id: 54 - kind: action - action: restart - man: ВНИМАНИЕ - Рестарт предполагает УДАЛЕНИЕ подов.
Может быть полная поломка. Крайне осторожно запускать при работе - params: [] - - name: resume - id: 114 - kind: instance - action: resume - params: [] - - name: suspend - id: 115 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/91_redis.yaml b/generated/prod/resources_yaml/91_redis.yaml deleted file mode 100644 index 24c6258..0000000 --- a/generated/prod/resources_yaml/91_redis.yaml +++ /dev/null @@ -1,219 +0,0 @@ -name: redis -service_id: 91 -service_display_name: Redis -service_short_name: redis -service_man: 'Redis кластер. Под кеши.
Ресурсная платформа: k8s.
Автоматизация: k8s operator
port 6379' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 21 - kind: instance - action: create - params: - - id: 1061 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 510 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1062 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 511 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 512 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 513 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 514 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 1063 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 515 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 516 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 517 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 518 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: delete - id: 22 - kind: instance - action: delete - params: [] - - name: modify - id: 394 - kind: instance - action: modify - params: - - id: 1064 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 521 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 522 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 519 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 520 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 1065 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 523 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 524 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 525 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 526 - code: slaveAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: reconcile - id: 391 - kind: action - action: reconcile - params: [] - - name: resume - id: 393 - kind: instance - action: resume - params: [] - - name: suspend - id: 392 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/92_mongodb.yaml b/generated/prod/resources_yaml/92_mongodb.yaml deleted file mode 100644 index cd477f7..0000000 --- a/generated/prod/resources_yaml/92_mongodb.yaml +++ /dev/null @@ -1,194 +0,0 @@ -name: mongodb -service_id: 92 -service_display_name: MongoDB -service_short_name: mongodb -service_man: '# Инструкция по развертыванию MongoDB через платформу

## 1. Общая информация

MongoDB — документоориентированная распределённая база данных, обеспечивающая высокую доступность, горизонтальное масштабирование и гибкую модель хранения.

### Доступные операции с кластером

* **create** — создание базы
* **delete** — удаление базы данных
* **suspend** — остановка базы данных
* **resume** — запуск базы данных
* **modify** — изменение базы данных

### Доступные операции с пользователями

* **create_user** — создание пользователя
* **modify_user** — модификация пользователя
* **delete_user** — удаление пользователя

---

## 2. Параметры развертывания базы данных

Эти параметры необходимо указать при создании базы данных.

### Платформа для развертывания

Среда, в которой будет развернут кластер. Выбирается из предложенного списка.

### Количество узлов

Количество реплик в кластере MongoDB.
Оптимально выбирать нечётное значение, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

> **Примечание:** уменьшение количества узлов после создания кластера не рекомендуется.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных в гигабайтах.
Пример: `20`

### Имя ipSpace для публикации внешнего IP

Имя ipSpace, через который будет выделен внешний IP для доступа к master-узлу.
Укажите `no-needed`, если внешний адрес не требуется.
Пример: `internet-no-antiddos-v1`

### Список разрешённых IP-адресов

Список IP-адресов или подсетей, с которых разрешён доступ к кластеру.
Если оставить пустым — доступ открыт для всех источников.
Пример: `["10.0.0.0/24", "192.168.1.10/32"]`

---

## 3. Параметры модификации базы данных

Все параметры, которые вы указываете при модификации, являются необязательными — система автоматически подставит ранее сохранённые значения, и при необходимости вы можете изменить их на нужные.

### Количество узлов

Количество реплик в кластере MongoDB.
Оптимально выбирать нечётное значение, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

> **Примечание:** уменьшение количества узлов после создания кластера не рекомендуется.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных указывается в гигабайтах. Его можно увеличить при необходимости, однако уменьшить уже выделенный размер невозможно.
Пример: `20`

### Имя ipSpace для публикации внешнего IP

Имя ipSpace, через который будет выделен внешний IP для доступа к master-узлу.
Укажите `no-needed`, если внешний адрес не требуется.
Пример: `internet-no-antiddos-v1`

### Список разрешённых IP-адресов

Список IP-адресов или подсетей, с которых разрешён доступ к кластеру.
Если оставить пустым — доступ открыт для всех источников.
Пример: `["10.0.0.0/24", "192.168.1.10/32"]`

---

## 3. Параметры создания пользователя

### Имя пользователя

Указывается имя пользователя (любой регистр).
Пример: `readApp`

### Имя базы данных

Название базы, в которой будет создан пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read** — доступ только на чтение
* **readWrite** — чтение и изменение данных
* **dbOwner** — полный контроль над базой данных

---

## 4. Параметры модификации пользователя

### Имя пользователя

Необходимо выбрать существующего пользователя.
Если список пуст, требуется предварительно создать пользователя.

### Имя базы данных

База данных, к которой относится пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read**
* **readWrite**
* **dbOwner**

---

## 5. Параметры удаления пользователя

### Имя пользователя

Выберите пользователя из списка для удаления.

---

## 6. Выходные параметры

После успешного развертывания базы данных платформа возвращает ключевые параметры.

### internalConnect

Внутренний эндпоинт для подключения к кластеру из сервисов, работающих в той же сети.
Используется для внутренних интеграций и системных сервисов.

### externalConnect

Внешний FQDN для подключения к кластеру извне.
Порт подключения — **27017**.

---

## 7. Пример подключения

Перед подключением необходимо установить **mongosh**.

Команда подключения к базе данных:

```
mongosh "mongodb://<username>:<password>@<endpoint_db>:27017/<dbName>"
```

Где:

* `<username>` — имя пользователя
* `<password>` — пароль
* `<endpoint_db>` — internalConnect или externalConnect
* `<dbName>` — имя базы данных

## 8. Дополнительная информация

Сейчас соединение с базой работает без шифрования, и доступ к кластеру осуществляется только по логину и паролю.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 37 - kind: instance - action: create - params: - - id: 487 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `2`' - sort: 10 - is_modifiable: true - - id: 588 - code: resourceMemory - data_type: integer > 0 - required: true - minvalue: 512 - descr: 'Пример: `2048`' - is_modifiable: true - - id: 589 - code: resourceCPU - data_type: integer > 0 - required: true - descr: 'Пример: `1000`' - is_modifiable: true - - id: 590 - code: resourceDisk - data_type: integer > 0 - required: true - descr: 'Пример: `20`' - is_modifiable: true - - id: 595 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Выбрать платформу из списка - sort: 10 - - id: 646 - code: ipSpaceNameMaster - data_type: string - required: false - descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' - depends_on: resourceRealm - is_modifiable: true - - id: 889 - code: masterAccessList - data_type: json - required: true - default: '[]' - descr: Список IP-адресов, с которых разрешён доступ. Если оставить пустым — доступ открыт всем. - man: 'Передаётся как массив строк. Пример: ["192.168.0.0", "192.168.0.1"]' - is_modifiable: true - - name: create_user - id: 198 - kind: subresource - action: create - subresource: user - params: - - id: 635 - code: username - data_type: string - required: true - - id: 636 - code: dbName - data_type: string - required: true - - id: 643 - code: role - data_type: string - required: true - value_list: - - dbOwner - - read - - readWrite - - name: delete - id: 190 - kind: instance - action: delete - params: [] - - name: delete_user - id: 199 - kind: subresource - action: delete - subresource: user - params: - - id: 644 - code: username - data_type: string - required: true - - name: modify - id: 201 - kind: instance - action: modify - params: - - id: 648 - code: resourceInstances - data_type: integer > 0 - required: false - descr: 'Пример: `2`' - - id: 649 - code: resourceMemory - data_type: integer > 0 - required: false - minvalue: 512 - descr: 'Пример: `2048`' - - id: 650 - code: resourceCPU - data_type: integer > 0 - required: false - minvalue: 500 - descr: 'Пример: `1000`' - - id: 651 - code: resourceDisk - data_type: integer > 0 - required: false - descr: 'Пример: `20`' - - id: 653 - code: ipSpaceNameMaster - data_type: string - required: false - descr: 'Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`' - - id: 890 - code: masterAccessList - data_type: json - required: false - default: '[]' - descr: Список IP-адресов, с которых разрешён доступ. Если оставить пустым — доступ открыт всем. - man: 'Передаётся как массив строк. Пример: ["192.168.0.0", "192.168.0.1"]' - - name: modify_user - id: 200 - kind: subresource - action: modify - subresource: user - params: - - id: 640 - code: username - data_type: string - required: true - - id: 641 - code: dbName - data_type: string - required: true - - id: 642 - code: role - data_type: string - required: true - value_list: - - dbOwner - - read - - readWrite - - name: resume - id: 196 - kind: instance - action: resume - params: [] - - name: suspend - id: 194 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/93_rabbitmq.yaml b/generated/prod/resources_yaml/93_rabbitmq.yaml deleted file mode 100644 index 076dcd0..0000000 --- a/generated/prod/resources_yaml/93_rabbitmq.yaml +++ /dev/null @@ -1,167 +0,0 @@ -name: rabbitmq -service_id: 93 -service_display_name: RabbitMQ -service_short_name: rabbitmq -service_man: '# Инструкция по развертыванию RabbitMQ через платформу

---
## 1. Общая информация
RabbitMQ — это брокер сообщений с открытым исходным кодом, который обеспечивает надёжную доставку сообщений между приложениями.
Позволяет надёжно передавать сообщения между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **modify** — Изменение параметров кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса RabbitMQ:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик в кластере RabbitMQ. При значении больше 1 создаётся кластер с репликацией для обеспечения высокой доступности.
Пример: `1` (single node) или `3` (кластер с репликацией)

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных RabbitMQ в гигабайтах. Пример: `50`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-узлу. При включении создаётся Service типа LoadBalancer и настраивается VIP на порту 5672.
Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`


---
## 3. Модификация кластера
Операция **modify** позволяет изменить параметры существующего кластера RabbitMQ.

**Изменяемые параметры:**
* **Квота (megabyte) памяти пода** — можно увеличить или уменьшить
* **Квота (millicore) ядра пода** — можно увеличить или уменьшить
* **Размер (gigabyte) диска для базы данных** — можно только увеличить (уменьшение не поддерживается)
* **Количество узлов в сети** — можно только увеличить (уменьшение не поддерживается)
* **Выделяет внешний IP для доступа к master** — можно включить или выключить
* **Имя ipSpace для публикации внешнего IP master** — можно изменить при включенном внешнем IP

**Ограничения:**
* Уменьшение размера диска не поддерживается
* Уменьшение количества реплик не поддерживается

---
## 4. Выходные параметры
После успешного развертывания ресурса RabbitMQ платформа возвращает ряд параметров в разделе "Поля состояния (сгенерированные значения)":

**clusterDomain**
Домен кластера Kubernetes, в котором развернут RabbitMQ.
Пример: `cluster.local`

**inernalConnect**
Внутренние эндпоинты для подключения к кластеру RabbitMQ из других сервисов, развернутых внутри той же сети.
Содержит:
* **master** — FQDN для подключения к кластеру. Пример: `rabbitmqk8s.{namespace}.svc.{clusterDomain}`

**externalConnect**
Внешние эндпоинты для подключения к кластеру RabbitMQ извне.
Содержит объекты с полями:
* **admin** — доступ к веб-интерфейсу управления:
* `fqdn` — полное доменное имя для веб-интерфейса. Пример: `admin.{namespace}.{domain}`
* `ip` — IP-адрес
* **master** — доступ к AMQP протоколу (если включен внешний IP):
* `fqdn` — полное доменное имя для AMQP. Пример: `write.{namespace}.{domain}`
* `ip` — IP-адрес

**monitoring**
Информация о дашбордах мониторинга:
* **allDashboards** — URL для доступа ко всем дашбордам Grafana

Для подключения используются следующие порты:

* **5672** — AMQP протокол (внутренний и внешний доступ, если включен внешний IP)
* **15672** — веб-интерфейс управления (доступен через Ingress по HTTPS)

**Учётные данные**
Данные для подключения к RabbitMQ доступны в разделе "Учётные данные":
* **adminUser** — имя пользователя администратора
* **adminPass** — пароль администратора


---
## 5. Примеры подключения через консоль (rabbitmqadmin)

### Работа с очередями через rabbitmqadmin

Для работы с RabbitMQ через консоль используется утилита `rabbitmqadmin`. Установите её или используйте образ с предустановленной утилитой.

Задайте переменные окружения с учётными данными:

```bash
export RABBITMQ_USERNAME=<adminUser>
export RABBITMQ_PASSWORD=<adminPass>
export RABBIT_HOST=<host>
```

Где:
* `<adminUser>` и `<adminPass>` — учётные данные из раздела "Учётные данные"
* `<host>` — адрес для подключения:
* Для внешнего подключения: `externalConnect.admin.fqdn` или `externalConnect.master.fqdn`

**Создание очереди:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
declare queue name=healthcheck durable=false
```

**Листинг очередей:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
list queues
```

**Отправка сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
publish exchange=amq.default routing_key=healthcheck payload="ping-$(date +%s)"
```

**Чтение сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
get queue=healthcheck ackmode=ack_requeue_false
```

---
## 6. Дополнительная информация
Для подключения к кластеру RabbitMQ используйте полученные эндпоинты из раздела "Поля состояния (сгенерированные значения)" и учётные данные из раздела "Учётные данные".

Веб-интерфейс управления доступен по адресу, указанному в параметре `externalConnect.admin.fqdn` через HTTPS.

Для внутреннего подключения используйте адрес из `inernalConnect.master` на порту 5672.

---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 38 - kind: instance - action: create - params: - - id: 116 - code: resourceCPU - data_type: integer > 0 - required: true - default: "1000" - maxvalue: 4000 - minvalue: 500 - descr: 'Пример: `2000`' - sort: 20 - is_modifiable: true - - id: 117 - code: resourceMemory - data_type: integer > 0 - required: true - default: "1124" - maxvalue: 8192 - minvalue: 512 - descr: 'Пример: `2048`' - sort: 30 - is_modifiable: true - - id: 118 - code: resourceDisk - data_type: integer > 0 - required: true - default: "30" - maxvalue: 999 - minvalue: 5 - descr: 'Пример: `20`' - sort: 40 - is_modifiable: true - - id: 119 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Выбрать платформу из списка - sort: 10 - - id: 224 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `1`' - sort: 50 - is_modifiable: true - - id: 225 - code: needExternalAddressMaster - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`' - sort: 60 - is_modifiable: true - - id: 543 - code: ipSpaceNameMaster - data_type: string - required: false - descr: 'Пример: `internet-no-antiddos-v1`' - is_modifiable: true - - name: delete - id: 39 - kind: instance - action: delete - params: [] - - name: modify - id: 106 - kind: instance - action: modify - params: - - id: 304 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 500 - descr: 'Пример: `2000`' - sort: 10 - - id: 305 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 512 - descr: 'Пример: `2048`' - sort: 20 - - id: 306 - code: resourceDisk - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: 'Пример: `20`' - sort: 10 - - id: 307 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `1`' - sort: 40 - - id: 308 - code: needExternalAddressMaster - data_type: boolean - required: true - value_list: - - "false" - - "true" - descr: Выделяет внешний (белый) IP для доступа к master - sort: 50 - - id: 545 - code: ipSpaceNameMaster - data_type: string - required: false - descr: 'Пример: `internet-no-antiddos-v1`' - - name: resume - id: 117 - kind: instance - action: resume - params: [] - - name: suspend - id: 116 - kind: instance - action: suspend - man: Пока не работает из за того что RabbitmqCluster нельзя занулить - params: [] diff --git a/generated/prod/resources_yaml/94_lucee.yaml b/generated/prod/resources_yaml/94_lucee.yaml deleted file mode 100644 index eb0ac9f..0000000 --- a/generated/prod/resources_yaml/94_lucee.yaml +++ /dev/null @@ -1,246 +0,0 @@ -name: lucee -service_id: 94 -service_display_name: Lucee -service_short_name: lucee -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 49 - kind: instance - action: create - params: - - id: 1019 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 430 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1020 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 433 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 431 - code: cpu - data_type: string - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 432 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 1021 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 434 - code: domain - data_type: string - required: true - default: lucee-un0nzrwz - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1022 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 435 - code: version - data_type: string - required: true - default: "6.0" - value_list: - - "5.4" - - "6.0" - is_modifiable: false - - id: 436 - code: gitPath - data_type: string - required: true - default: https://github.com/xahys/testlucee - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 437 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1023 - code: jsonEnv - data_type: map - required: false - default: '{}' - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 50 - is_modifiable: true - - name: delete - id: 50 - kind: instance - action: delete - params: [] - - name: modify - id: 55 - kind: instance - action: modify - params: - - id: 1024 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 440 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 438 - code: cpu - data_type: string - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 439 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 1025 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 442 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 443 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 441 - code: version - data_type: string - required: true - default: "" - value_list: - - "6.0" - - "5.4" - is_modifiable: false - - id: 1026 - code: jsonEnv - data_type: map - required: false - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 30 - - name: reconcile - id: 373 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 53 - kind: action - action: redeploy - params: - - id: 1027 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 444 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: restart - id: 163 - kind: action - action: restart - params: [] - - name: resume - id: 95 - kind: instance - action: resume - params: [] - - name: suspend - id: 96 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/95_nodejs.yaml b/generated/prod/resources_yaml/95_nodejs.yaml deleted file mode 100644 index dc03203..0000000 --- a/generated/prod/resources_yaml/95_nodejs.yaml +++ /dev/null @@ -1,246 +0,0 @@ -name: nodejs -service_id: 95 -service_display_name: NodeJS -service_short_name: nodejs -service_man: Развертывает контейнер в k8s кластере с node, который можно использовать для запуска своих приложений.
Приложение должно быть доступно по ссылки в git репозитории, которая является входным параметром. -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 58 - kind: instance - action: create - params: - - id: 1028 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 445 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1029 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 446 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 447 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 448 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1030 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 449 - code: domain - data_type: string - required: true - default: node-mt4rik7o - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1031 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 450 - code: version - data_type: string - required: true - default: "23" - value_list: - - "18" - - "20" - - "22" - - "23" - is_modifiable: false - - id: 451 - code: gitPath - data_type: string - required: true - default: https://github.com/xahys/testnode.git - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 452 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1032 - code: jsonEnv - data_type: map - required: false - default: '{}' - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 50 - is_modifiable: true - - name: delete - id: 61 - kind: instance - action: delete - params: [] - - name: modify - id: 59 - kind: instance - action: modify - params: - - id: 1033 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 453 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 454 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 455 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1034 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 456 - code: version - data_type: string - required: true - default: "" - value_list: - - "18" - - "20" - - "22" - - "23" - is_modifiable: false - - id: 457 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 458 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1035 - code: jsonEnv - data_type: map - required: false - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 30 - - name: reconcile - id: 374 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 60 - kind: action - action: redeploy - params: - - id: 1036 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 459 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: resume - id: 121 - kind: instance - action: resume - params: [] - - name: suspend - id: 120 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/96_pgadmin.yaml b/generated/prod/resources_yaml/96_pgadmin.yaml deleted file mode 100644 index f18f5c2..0000000 --- a/generated/prod/resources_yaml/96_pgadmin.yaml +++ /dev/null @@ -1,164 +0,0 @@ -name: pgadmin -service_id: 96 -service_display_name: pgAdmin -service_short_name: pgadmin -service_man: Инструмент с графическим интерфейсом для управления базами данных PostgreSQL -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 62 - kind: instance - action: create - params: - - id: 164 - code: domain - data_type: string - required: true - default: pgadmin-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 165 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (milicores). 1000Mili = 1 Ядро - man: Квота ядра пода (milicores). 1000Mili = 1 Ядро - sort: 30 - is_modifiable: true - - id: 166 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 512 - descr: Квота памяти пода (MBytes) - man: Квота памяти пода (MBytes) - sort: 40 - is_modifiable: true - - id: 167 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - is_modifiable: true - - id: 169 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 170 - code: login - data_type: string - required: true - regex: ^([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)$ - maxlength: 255 - minlength: 3 - descr: Логин для подключения к pgAdmin, в формате email (login@email.net) - man: Логин для подключения к pgAdmin, в формате email (login@email.net) - sort: 60 - - id: 171 - code: password - data_type: string - required: true - descr: Пароль для подключения к pgAdmin - man: Пароль для подключения к pgAdmin - sort: 70 - is_sensitive: true - - name: delete - id: 63 - kind: instance - action: delete - params: [] - - name: modify - id: 64 - kind: instance - action: modify - params: - - id: 172 - code: resourceCPU - data_type: integer > 0 - required: false - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - - id: 173 - code: resourceMemory - data_type: integer > 0 - required: false - maxvalue: 8192 - minvalue: 512 - descr: Квота памяти пода (MBytes) - man: Квота памяти пода (MBytes) - - id: 174 - code: resourceDisk - data_type: integer > 0 - required: false - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - - name: redeploy - id: 65 - kind: action - action: redeploy - params: - - id: 277 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - - name: resume - id: 123 - kind: instance - action: resume - params: [] - - name: suspend - id: 122 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/97_nodered.yaml b/generated/prod/resources_yaml/97_nodered.yaml deleted file mode 100644 index 4c5fb63..0000000 --- a/generated/prod/resources_yaml/97_nodered.yaml +++ /dev/null @@ -1,95 +0,0 @@ -name: nodered -service_id: 97 -service_display_name: NodeRed -service_short_name: nodered -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 72 - kind: instance - action: create - params: - - id: 193 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 30 - - id: 194 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - sort: 40 - - id: 195 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 60 - - id: 196 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 197 - code: domain - data_type: string - required: true - default: nodered-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru - man: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru - sort: 10 - - id: 323 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 50 - - name: delete - id: 73 - kind: instance - action: delete - params: [] diff --git a/generated/prod/resources_yaml/98_http.yaml b/generated/prod/resources_yaml/98_http.yaml deleted file mode 100644 index 5119475..0000000 --- a/generated/prod/resources_yaml/98_http.yaml +++ /dev/null @@ -1,161 +0,0 @@ -name: http -service_id: 98 -service_display_name: Простой HTTP контейнер -service_short_name: http -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 77 - kind: instance - action: create - params: - - id: 212 - code: domain - data_type: string - required: true - default: http-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 213 - code: resourceCPU - data_type: integer > 0 - required: true - default: "100" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili) - man: Квота ядра пода (Указывается в mili) - sort: 30 - is_modifiable: true - - id: 214 - code: resourceMemory - data_type: integer > 0 - required: true - default: "200" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 40 - is_modifiable: true - - id: 215 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество реплик приложения. Трафик будет распределяться между подами в режиме Round-Robin - sort: 50 - is_modifiable: true - - id: 216 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 217 - code: registryPath - data_type: string - required: true - default: nexus-sa.tst.nubes.ru/docker-nubes/jolt - descr: Путь до image, который будет установлен как deployment - man: Путь до image, который будет установлен как deployment - sort: 60 - - id: 241 - code: jsonEnv - data_type: map - required: false - man: Массив переменных, будут переданы в env приложения - sort: 70 - - id: 782 - code: ingressAnnotations - data_type: map - required: false - default: '{}' - man: Формат ключ/значение
Поддерживает пока простые аннотации - sort: 80 - - name: delete - id: 78 - kind: instance - action: delete - params: [] - - name: modify - id: 81 - kind: instance - action: modify - params: - - id: 218 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 100 - - id: 219 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 128 - - id: 220 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - - name: redeploy - id: 79 - kind: action - action: redeploy - params: - - id: 272 - code: resourceRealm - data_type: string - required: true - default: k8s-4-ext-nubes-ru - value_list: - - k8s-4-ext-nubes-ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 10 - - name: resume - id: 130 - kind: instance - action: resume - params: [] - - name: suspend - id: 131 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/99_gitea.yaml b/generated/prod/resources_yaml/99_gitea.yaml deleted file mode 100644 index 25b5b95..0000000 --- a/generated/prod/resources_yaml/99_gitea.yaml +++ /dev/null @@ -1,169 +0,0 @@ -name: gitea -service_id: 99 -service_display_name: Gitea -service_short_name: gitea -service_man: '# Инструкция по развертыванию Gitea через платформу

---
## 1. Общая информация
Gitea — это лёгкий, быстрый и самодостаточный сервис хостинга Git-репозиториев с веб-интерфейсом.
Позволяет создавать и управлять репозиториями, организациями, командами, а также настраивать CI/CD через интеграции.
Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание сервиса Gitea
- **delete** — Удаление сервиса Gitea
- **modify** — Модификация сервиса Gitea
- **suspend** — Остановка сервиса Gitea
- **resume** — Запуск сервиса Gitea

---
## 2. Параметры развертывания
Ниже перечислены параметры, которые необходимо указать при создании ресурса Gitea:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развёрнут сервис). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик подов Gitea. Пример: `1`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `1024`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Размер (gigabyte) диска для базы данных:**
Размер PVC для хранения репозиториев и данных Gitea в гигабайтах. Пример: `10`

* **FQDN / Домен:**
Доменное имя, по которому будет доступен сервис. Пример: `git.example.com`

* **Инстанс услуги PostgreSQL:**
UUID услуги k8sPgsql, в которой будет создана база данных `gitea` и пользователь `techgitea`.

* **Дополнительные параметры сервера (iniServerParams):**
JSON-объект с дополнительными параметрами секции `[server]` в `app.ini`. Пример:
```json
{"MAX_CONTENT_LENGTH": "524288000"}
```

* **Дополнительные annotations для Ingress (ingressExtraAnnotations):**
JSON-объект с дополнительными annotations для Ingress-контроллера. Пример:
```json
{"nginx.ingress.kubernetes.io/proxy-body-size": "500m"}
```

---
## 3. Особенности подключения и работы с репозиториями

### 3.1. Доступ только по HTTPS
Сервис развёртывается с **rootless-образом** Gitea. SSH-сервер внутри и он **доступен только внутри кластера Kubernetes**.

**Внешний доступ к репозиториям осуществляется исключительно по HTTPS через Ingress.**

```bash
# Пример клонирования
https://<домен>/<пользователь>/<репозиторий>.git
```

### 3.2. Настройка git для передачи логина и пароля
Для работы с приватными репозиториями через HTTPS необходимо передать учётные данные. Рекомендуется использовать один из следующих способов:

**Встроить креды в URL remote**
```bash
git remote set-url origin https://<логин>:<пароль>@<домен>/<пользователь>/<репозиторий>.git
```
> **Важно:** Логин и пароль администратора создаются автоматически при развертывании и хранятся в Vault.

### 3.3. Работа с большими файлами
По умолчанию `MAX_CONTENT_LENGTH` установлен в **500 МБ**, а Ingress пропускает pack-файлы до **500 МБ**.

Если необходимо запушить файлы близкие к лимиту, увеличьте HTTP-буфер git на стороне клиента:
```bash
git config http.postBuffer 524288000
```

Для файлов **более 500 МБ** рекомендуется использовать **Git LFS**.

---
## 4. Выходные параметры
После успешного развертывания ресурса Gitea платформа возвращает ряд параметров.

Ключевым из них является:

**connectionUrl**
Это внешний URL для доступа к веб-интерфейсу и Git-репозиториям по HTTPS. Пример:
```
https://git.example.com
```

**internalConnect**
Внутренний эндпоинт для подключения к PostgreSQL из других сервисов внутри кластера.

---
## 5. Дополнительная информация
- База данных `gitea` и пользователь `techgitea` создаются автоматически в указанной услуге PostgreSQL.
- DNS-запись и TLS-сертификат (Let''s Encrypt) настраиваются автоматически на основе указанного домена.
- Для изменения параметров сервера Gitea (`app.ini`) используйте операцию **modify** с полем `iniServerParams`.
- Для добавления дополнительных Ingress-annotations используйте операцию **modify** с полем `ingressExtraAnnotations`.
---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 82 - kind: instance - action: create - params: - - id: 232 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 300 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 40 - is_modifiable: true - - id: 233 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 256 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 50 - is_modifiable: true - - id: 234 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 60 - is_modifiable: true - - id: 235 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 70 - is_modifiable: true - - id: 237 - code: domain - data_type: string - required: true - default: gitea-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 20 - - id: 238 - code: psqlUid - data_type: uuid - required: true - ref_svc_id: 90 - descr: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически - man: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически - sort: 10 - - id: 818 - code: ingressExtraAnnotations - data_type: map - required: false - is_modifiable: true - - id: 821 - code: iniServerParams - data_type: map - required: false - is_modifiable: true - - name: delete - id: 83 - kind: instance - action: delete - params: [] - - name: modify - id: 91 - kind: instance - action: modify - params: - - id: 259 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 10 - - id: 260 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 256 - descr: Квота памяти пода (Указывается в Megabytes) - sort: 20 - - id: 261 - code: resourceDisk - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 30 - - id: 262 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 40 - - id: 819 - code: ingressExtraAnnotations - data_type: map - required: false - man: 'Примеры:
- `nginx.ingress.kubernetes.io/proxy-body-size: 500m` (Размер пакета)
- `nginx.ingress.kubernetes.io/proxy-read-timeout: "600"` (Время ожидания при чтении)
- `nginx.ingress.kubernetes.io/proxy-send-timeout: "600"` (Время ожидания отправке)' - - id: 820 - code: iniServerParams - data_type: map - required: false - man: 'Примеры:
- `MAX_CONTENT_LENGTH: 524288000(~500мб)` (Размер пакета в байтах)' - - name: resume - id: 127 - kind: instance - action: resume - params: [] - - name: suspend - id: 126 - kind: instance - action: suspend - params: [] diff --git a/generated/prod/resources_yaml/embed.go b/generated/prod/resources_yaml/embed.go deleted file mode 100644 index 72e3ff2..0000000 --- a/generated/prod/resources_yaml/embed.go +++ /dev/null @@ -1,8 +0,0 @@ -package resources_yaml - -import "embed" - -// Files embeds unified service YAML specs for provider runtime lookup. -// -//go:embed *.yaml -var Files embed.FS diff --git a/generated/test/go/100_openwhisk_resource.go b/generated/test/go/100_openwhisk_resource.go deleted file mode 100644 index 3c65789..0000000 --- a/generated/test/go/100_openwhisk_resource.go +++ /dev/null @@ -1,322 +0,0 @@ -package resources_gen - -import ( - "context" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: openwhisk -// Service ID: 100 - -var _ resource.Resource = &OpenwhiskResource{} -var _ resource.ResourceWithModifyPlan = &OpenwhiskResource{} -var _ resource.ResourceWithImportState = &OpenwhiskResource{} - -type OpenwhiskResource struct { - client *core.UniversalClient -} - -type OpenwhiskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewOpenwhiskResource() resource.Resource { - return &OpenwhiskResource{} -} - -func (r *OpenwhiskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_openwhisk" -} - -func (r *OpenwhiskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *OpenwhiskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *OpenwhiskModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{} - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 100, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *OpenwhiskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{} - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 100, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 100, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *OpenwhiskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 100, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *OpenwhiskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan OpenwhiskModel - var state OpenwhiskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 100, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *OpenwhiskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state OpenwhiskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *OpenwhiskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *OpenwhiskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/109_zones_v2_record_resource.go b/generated/test/go/109_zones_v2_record_resource.go deleted file mode 100644 index b875e09..0000000 --- a/generated/test/go/109_zones_v2_record_resource.go +++ /dev/null @@ -1,366 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: zones_v2 -// Subresource: record - -var _ resource.Resource = &ZonesV2RecordResource{} - -// ZonesV2RecordResource manages a subresource via operations. -type ZonesV2RecordResource struct { - client *core.UniversalClient -} - -type ZonesV2RecordModel struct { - ID types.String `tfsdk:"id"` - ZonesV2ID types.String `tfsdk:"zones_v2_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - RecordInput types.String `tfsdk:"record_input"` - RecordName types.String `tfsdk:"record_name"` - RecordTTL types.Int64 `tfsdk:"record_t_t_l"` - RecordType types.String `tfsdk:"record_type"` -} - -func NewZonesV2RecordResource() resource.Resource { - return &ZonesV2RecordResource{} -} - -func (r *ZonesV2RecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_zones_v2_record" -} - -func (r *ZonesV2RecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "zones_v2_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "record_input": schema.StringAttribute{Required: true, MarkdownDescription: "Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции"}, - "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны в конце проставляется автоматически Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена"}, - "record_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("A")}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ZonesV2RecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ZonesV2RecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.RecordInput.IsNull() || plan.RecordInput.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if plan.RecordName.IsNull() || plan.RecordName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.ZonesV2ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - } - identityCodes := []string{ - "recordName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("record", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - "recordTTL": resources_core.FormatInt64(plan.RecordTTL), - "recordType": resources_core.FormatString(plan.RecordType), - "recordInput": resources_core.FormatString(plan.RecordInput), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_record", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_record", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ZonesV2RecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2RecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ZonesV2RecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_record" == "" { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ZonesV2ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - "recordTTL": resources_core.FormatInt64(plan.RecordTTL), - "recordType": resources_core.FormatString(plan.RecordType), - "recordInput": resources_core.FormatString(plan.RecordInput), - }) - - idParams := map[string]string{ - "recordName": resources_core.FormatString(plan.RecordName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_record", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "record", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ZonesV2RecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ZonesV2RecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_record" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ZonesV2ID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "recordName": resources_core.FormatString(state.RecordName), - } - identityCodes := []string{ - "recordName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("record", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "recordName": resources_core.FormatString(state.RecordName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_record", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ZonesV2RecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/109_zones_v2_resource.go b/generated/test/go/109_zones_v2_resource.go deleted file mode 100644 index 52dbc10..0000000 --- a/generated/test/go/109_zones_v2_resource.go +++ /dev/null @@ -1,484 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: zones_v2 -// Service ID: 109 - -var _ resource.Resource = &ZonesV2Resource{} -var _ resource.ResourceWithModifyPlan = &ZonesV2Resource{} -var _ resource.ResourceWithImportState = &ZonesV2Resource{} - -type ZonesV2Resource struct { - client *core.UniversalClient -} - -type ZonesV2Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Records types.String `tfsdk:"records"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ZoneExpire types.Int64 `tfsdk:"zone_expire"` - ZoneKind types.String `tfsdk:"zone_kind"` - ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` - ZoneName types.String `tfsdk:"zone_name"` - ZoneRefresh types.Int64 `tfsdk:"zone_refresh"` - ZoneRetry types.Int64 `tfsdk:"zone_retry"` - ZoneTTL types.Int64 `tfsdk:"zone_t_t_l"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewZonesV2Resource() resource.Resource { - return &ZonesV2Resource{} -} - -func (r *ZonesV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_zones_v2" -} - -func (r *ZonesV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "records": schema.StringAttribute{Required: true}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("powerdns"), MarkdownDescription: "Не изменяется после создания."}, - "zone_expire": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600000), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, - "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, - "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, - "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, - "zone_refresh": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, - "zone_retry": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7200), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, - "zone_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(86400), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ZonesV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ZonesV2Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneExpire.IsNull() && !plan.ZoneExpire.IsUnknown() && !state.ZoneExpire.IsNull() && !state.ZoneExpire.IsUnknown() { - if plan.ZoneExpire.ValueInt64() != state.ZoneExpire.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_expire", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneKind.ValueString(), state.ZoneKind.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneMastersForSlave.ValueString(), state.ZoneMastersForSlave.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneName.ValueString(), state.ZoneName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneRefresh.IsNull() && !plan.ZoneRefresh.IsUnknown() && !state.ZoneRefresh.IsNull() && !state.ZoneRefresh.IsUnknown() { - if plan.ZoneRefresh.ValueInt64() != state.ZoneRefresh.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_refresh", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneRetry.IsNull() && !plan.ZoneRetry.IsUnknown() && !state.ZoneRetry.IsNull() && !state.ZoneRetry.IsUnknown() { - if plan.ZoneRetry.ValueInt64() != state.ZoneRetry.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_retry", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTTL.IsNull() && !plan.ZoneTTL.IsUnknown() && !state.ZoneTTL.IsNull() && !state.ZoneTTL.IsUnknown() { - if plan.ZoneTTL.ValueInt64() != state.ZoneTTL.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить zone_t_t_l", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Records.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "records is required.") - return - } - if config.Records.IsUnknown() { - return - } - if config.ZoneName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - if config.ZoneName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 761: resources_core.FormatString(config.ZoneName), - 762: resources_core.FormatString(config.ZoneKind), - 763: resources_core.FormatString(config.ZoneMastersForSlave), - 764: resources_core.FormatInt64(config.ZoneRefresh), - 765: resources_core.FormatInt64(config.ZoneRetry), - 766: resources_core.FormatInt64(config.ZoneExpire), - 767: resources_core.FormatInt64(config.ZoneTTL), - 768: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 109, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ZonesV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Records.IsNull() || data.Records.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "records is required.") - return - } - if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 761: resources_core.FormatString(data.ZoneName), - 762: resources_core.FormatString(data.ZoneKind), - 763: resources_core.FormatString(data.ZoneMastersForSlave), - 764: resources_core.FormatInt64(data.ZoneRefresh), - 765: resources_core.FormatInt64(data.ZoneRetry), - 766: resources_core.FormatInt64(data.ZoneExpire), - 767: resources_core.FormatInt64(data.ZoneTTL), - 768: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 109, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 109, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 109, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ZonesV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ZonesV2Model - var state ZonesV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.Records.IsNull() != state.Records.IsNull() || plan.Records.IsUnknown() != state.Records.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Records.IsNull() && !plan.Records.IsUnknown() && !state.Records.IsNull() && !state.Records.IsUnknown() { - if !strings.EqualFold(plan.Records.ValueString(), state.Records.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 769: resources_core.FormatString(plan.Records), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 109, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "records", Field: "Records", Type: "array-map-fixed"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneExpire", Field: "ZoneExpire", Type: "int64"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneRefresh", Field: "ZoneRefresh", Type: "int64"}, - {Code: "zoneRetry", Field: "ZoneRetry", Type: "int64"}, - {Code: "zoneTTL", Field: "ZoneTTL", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ZonesV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ZonesV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ZonesV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ZonesV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/110_dnszone_resource.go b/generated/test/go/110_dnszone_resource.go deleted file mode 100644 index b6842a5..0000000 --- a/generated/test/go/110_dnszone_resource.go +++ /dev/null @@ -1,495 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnszone -// Service ID: 110 - -var _ resource.Resource = &DnszoneResource{} -var _ resource.ResourceWithModifyPlan = &DnszoneResource{} -var _ resource.ResourceWithImportState = &DnszoneResource{} - -type DnszoneResource struct { - client *core.UniversalClient -} - -type DnszoneModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ZoneEmail types.String `tfsdk:"zone_email"` - ZoneKind types.String `tfsdk:"zone_kind"` - ZoneMastersForSlave types.String `tfsdk:"zone_masters_for_slave"` - ZoneName types.String `tfsdk:"zone_name"` - ZoneTimeToExpire types.String `tfsdk:"zone_time_to_expire"` - ZoneTimeToLive types.String `tfsdk:"zone_time_to_live"` - ZoneTimeToRefresh types.String `tfsdk:"zone_time_to_refresh"` - ZoneTimeToRetry types.String `tfsdk:"zone_time_to_retry"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDnszoneResource() resource.Resource { - return &DnszoneResource{} -} - -func (r *DnszoneResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnszone" -} - -func (r *DnszoneResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("powerdns"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "zone_email": schema.StringAttribute{Required: true, MarkdownDescription: "Контактный e-mail Не изменяется после создания."}, - "zone_kind": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("NATIVE"), MarkdownDescription: "Тип зоны Не изменяется после создания."}, - "zone_masters_for_slave": schema.StringAttribute{Optional: true, MarkdownDescription: "Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов Не изменяется после создания."}, - "zone_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя зоны Не изменяется после создания."}, - "zone_time_to_expire": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3600000"), MarkdownDescription: "Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным Не изменяется после создания."}, - "zone_time_to_live": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время, в течение которого информация будет кешироваться другими DNS-серверами Не изменяется после создания."}, - "zone_time_to_refresh": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("86400"), MarkdownDescription: "Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов Не изменяется после создания."}, - "zone_time_to_retry": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("7200"), MarkdownDescription: "Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DnszoneResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DnszoneModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneEmail.IsNull() && !plan.ZoneEmail.IsUnknown() && !state.ZoneEmail.IsNull() && !state.ZoneEmail.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneEmail.ValueString(), state.ZoneEmail.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_email", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneKind.IsNull() && !plan.ZoneKind.IsUnknown() && !state.ZoneKind.IsNull() && !state.ZoneKind.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneKind.ValueString(), state.ZoneKind.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_kind", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneMastersForSlave.IsNull() && !plan.ZoneMastersForSlave.IsUnknown() && !state.ZoneMastersForSlave.IsNull() && !state.ZoneMastersForSlave.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneMastersForSlave.ValueString(), state.ZoneMastersForSlave.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_masters_for_slave", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneName.IsNull() && !plan.ZoneName.IsUnknown() && !state.ZoneName.IsNull() && !state.ZoneName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneName.ValueString(), state.ZoneName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToExpire.IsNull() && !plan.ZoneTimeToExpire.IsUnknown() && !state.ZoneTimeToExpire.IsNull() && !state.ZoneTimeToExpire.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToExpire.ValueString(), state.ZoneTimeToExpire.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_expire", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToLive.IsNull() && !plan.ZoneTimeToLive.IsUnknown() && !state.ZoneTimeToLive.IsNull() && !state.ZoneTimeToLive.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToLive.ValueString(), state.ZoneTimeToLive.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_live", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToRefresh.IsNull() && !plan.ZoneTimeToRefresh.IsUnknown() && !state.ZoneTimeToRefresh.IsNull() && !state.ZoneTimeToRefresh.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToRefresh.ValueString(), state.ZoneTimeToRefresh.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_refresh", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneTimeToRetry.IsNull() && !plan.ZoneTimeToRetry.IsUnknown() && !state.ZoneTimeToRetry.IsNull() && !state.ZoneTimeToRetry.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneTimeToRetry.ValueString(), state.ZoneTimeToRetry.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_time_to_retry", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ZoneEmail.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") - return - } - if config.ZoneEmail.IsUnknown() { - return - } - if config.ZoneName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - if config.ZoneName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 66: resources_core.FormatString(config.ZoneEmail), - 67: resources_core.FormatString(config.ZoneName), - 68: resources_core.FormatString(config.ZoneKind), - 69: resources_core.FormatString(config.ZoneMastersForSlave), - 70: resources_core.FormatString(config.ZoneTimeToRefresh), - 71: resources_core.FormatString(config.ZoneTimeToRetry), - 72: resources_core.FormatString(config.ZoneTimeToExpire), - 73: resources_core.FormatString(config.ZoneTimeToLive), - 284: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 110, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DnszoneResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ZoneEmail.IsNull() || data.ZoneEmail.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_email is required.") - return - } - if data.ZoneName.IsNull() || data.ZoneName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 66: resources_core.FormatString(data.ZoneEmail), - 67: resources_core.FormatString(data.ZoneName), - 68: resources_core.FormatString(data.ZoneKind), - 69: resources_core.FormatString(data.ZoneMastersForSlave), - 70: resources_core.FormatString(data.ZoneTimeToRefresh), - 71: resources_core.FormatString(data.ZoneTimeToRetry), - 72: resources_core.FormatString(data.ZoneTimeToExpire), - 73: resources_core.FormatString(data.ZoneTimeToLive), - 284: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 110, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 110, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnszoneResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 110, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DnszoneResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnszoneModel - var state DnszoneModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 110, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "zoneEmail", Field: "ZoneEmail", Type: "string"}, - {Code: "zoneKind", Field: "ZoneKind", Type: "string"}, - {Code: "zoneMastersForSlave", Field: "ZoneMastersForSlave", Type: "string"}, - {Code: "zoneName", Field: "ZoneName", Type: "string"}, - {Code: "zoneTimeToExpire", Field: "ZoneTimeToExpire", Type: "string"}, - {Code: "zoneTimeToLive", Field: "ZoneTimeToLive", Type: "string"}, - {Code: "zoneTimeToRefresh", Field: "ZoneTimeToRefresh", Type: "string"}, - {Code: "zoneTimeToRetry", Field: "ZoneTimeToRetry", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnszoneResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DnszoneModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DnszoneResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DnszoneResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/111_dnsrecord_resource.go b/generated/test/go/111_dnsrecord_resource.go deleted file mode 100644 index 4b988a5..0000000 --- a/generated/test/go/111_dnsrecord_resource.go +++ /dev/null @@ -1,546 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dnsrecord -// Service ID: 111 - -var _ resource.Resource = &DnsrecordResource{} -var _ resource.ResourceWithModifyPlan = &DnsrecordResource{} -var _ resource.ResourceWithImportState = &DnsrecordResource{} - -type DnsrecordResource struct { - client *core.UniversalClient -} - -type DnsrecordModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - RecordInput types.String `tfsdk:"record_input"` - RecordName types.String `tfsdk:"record_name"` - RecordTTL types.Int64 `tfsdk:"record_t_t_l"` - RecordType types.String `tfsdk:"record_type"` - ZoneUid types.String `tfsdk:"zone_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDnsrecordResource() resource.Resource { - return &DnsrecordResource{} -} - -func (r *DnsrecordResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dnsrecord" -} - -func (r *DnsrecordResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "record_input": schema.StringAttribute{Required: true, MarkdownDescription: "Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP"}, - "record_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя записи. Имя зоны в конце проставляется автоматически Не изменяется после создания."}, - "record_t_t_l": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3600), MarkdownDescription: "Time-to-Leave записи. Раз в какое время запись будет обновлена Не изменяется после создания."}, - "record_type": schema.StringAttribute{Required: true, MarkdownDescription: "Тип DNS-записи Не изменяется после создания."}, - "zone_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID экземпляра услуги DNS-зоны Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DnsrecordResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DnsrecordModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.RecordName.IsNull() && !plan.RecordName.IsUnknown() && !state.RecordName.IsNull() && !state.RecordName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RecordName.ValueString(), state.RecordName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить record_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RecordTTL.IsNull() && !plan.RecordTTL.IsUnknown() && !state.RecordTTL.IsNull() && !state.RecordTTL.IsUnknown() { - if plan.RecordTTL.ValueInt64() != state.RecordTTL.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить record_t_t_l", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RecordType.IsNull() && !plan.RecordType.IsUnknown() && !state.RecordType.IsNull() && !state.RecordType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RecordType.ValueString(), state.RecordType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить record_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ZoneUid.ValueString(), state.ZoneUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить zone_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.RecordInput.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if config.RecordInput.IsUnknown() { - return - } - if config.RecordName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - if config.RecordName.IsUnknown() { - return - } - if config.RecordType.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") - return - } - if config.RecordType.IsUnknown() { - return - } - if config.ZoneUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") - return - } - if config.ZoneUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.ZoneUid.IsNull() && !config.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, config.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != config.ZoneUid.ValueString() { - config.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - params := map[int]string{ - 75: resources_core.FormatString(config.ZoneUid), - 76: resources_core.FormatString(config.RecordType), - 77: resources_core.FormatString(config.RecordName), - 78: resources_core.FormatString(config.RecordInput), - 450: resources_core.FormatInt64(config.RecordTTL), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 111, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DnsrecordResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.RecordInput.IsNull() || data.RecordInput.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_input is required.") - return - } - if data.RecordName.IsNull() || data.RecordName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_name is required.") - return - } - if data.RecordType.IsNull() || data.RecordType.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "record_type is required.") - return - } - if data.ZoneUid.IsNull() || data.ZoneUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "zone_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalZoneUid := data.ZoneUid - if !data.ZoneUid.IsNull() && !data.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, data.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedZoneUid != "" && resolvedZoneUid != data.ZoneUid.ValueString() { - data.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 75: resources_core.FormatString(data.ZoneUid), - 76: resources_core.FormatString(data.RecordType), - 77: resources_core.FormatString(data.RecordName), - 78: resources_core.FormatString(data.RecordInput), - 450: resources_core.FormatInt64(data.RecordTTL), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 111, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 111, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, state.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { - state.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalZoneUid.IsNull() && !originalZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - if strings.EqualFold(state.ZoneUid.ValueString(), originalZoneUid.ValueString()) { - state.ZoneUid = originalZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnsrecordResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 111, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, newState.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != newState.ZoneUid.ValueString() { - newState.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() && !newState.ZoneUid.IsNull() && !newState.ZoneUid.IsUnknown() { - if strings.EqualFold(newState.ZoneUid.ValueString(), state.ZoneUid.ValueString()) { - newState.ZoneUid = state.ZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DnsrecordResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DnsrecordModel - var state DnsrecordModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.RecordInput.IsNull() != state.RecordInput.IsNull() || plan.RecordInput.IsUnknown() != state.RecordInput.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.RecordInput.IsNull() && !plan.RecordInput.IsUnknown() && !state.RecordInput.IsNull() && !state.RecordInput.IsUnknown() { - if !strings.EqualFold(plan.RecordInput.ValueString(), state.RecordInput.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 256: resources_core.FormatString(plan.RecordInput), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 111, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "recordInput", Field: "RecordInput", Type: "string"}, - {Code: "recordName", Field: "RecordName", Type: "string"}, - {Code: "recordTTL", Field: "RecordTTL", Type: "int64"}, - {Code: "recordType", Field: "RecordType", Type: "string"}, - {Code: "zoneUid", Field: "ZoneUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - resolvedZoneUid, err := r.client.ResolveRefSvcParamValue(ctx, 109, state.ZoneUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve zone_uid", err.Error()) - } else if resolvedZoneUid != "" && resolvedZoneUid != state.ZoneUid.ValueString() { - state.ZoneUid = types.StringValue(resolvedZoneUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.ZoneUid.IsNull() && !plan.ZoneUid.IsUnknown() && !state.ZoneUid.IsNull() && !state.ZoneUid.IsUnknown() { - if strings.EqualFold(state.ZoneUid.ValueString(), plan.ZoneUid.ValueString()) { - state.ZoneUid = plan.ZoneUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DnsrecordResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DnsrecordModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DnsrecordResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DnsrecordResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/112_tenant_resource.go b/generated/test/go/112_tenant_resource.go deleted file mode 100644 index dff6bb7..0000000 --- a/generated/test/go/112_tenant_resource.go +++ /dev/null @@ -1,372 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: tenant -// Service ID: 112 - -var _ resource.Resource = &TenantResource{} -var _ resource.ResourceWithModifyPlan = &TenantResource{} -var _ resource.ResourceWithImportState = &TenantResource{} - -type TenantResource struct { - client *core.UniversalClient -} - -type TenantModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - GrafanaOrgName types.String `tfsdk:"grafana_org_name"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewTenantResource() resource.Resource { - return &TenantResource{} -} - -func (r *TenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_tenant" -} - -func (r *TenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "grafana_org_name": schema.StringAttribute{Required: true, MarkdownDescription: "WZ-Код клиента Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("grafana.ngcloud.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *TenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *TenantModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.GrafanaOrgName.IsNull() && !plan.GrafanaOrgName.IsUnknown() && !state.GrafanaOrgName.IsNull() && !state.GrafanaOrgName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.GrafanaOrgName.ValueString(), state.GrafanaOrgName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить grafana_org_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.GrafanaOrgName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") - return - } - if config.GrafanaOrgName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 239: resources_core.FormatString(config.GrafanaOrgName), - 240: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 112, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.GrafanaOrgName.IsNull() || data.GrafanaOrgName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "grafana_org_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 239: resources_core.FormatString(data.GrafanaOrgName), - 240: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 112, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 112, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 112, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *TenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TenantModel - var state TenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 112, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "grafanaOrgName", Field: "GrafanaOrgName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state TenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *TenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *TenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/113_vc_complex_resource.go b/generated/test/go/113_vc_complex_resource.go deleted file mode 100644 index 8f06254..0000000 --- a/generated/test/go/113_vc_complex_resource.go +++ /dev/null @@ -1,563 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_complex -// Service ID: 113 - -var _ resource.Resource = &VcComplexResource{} -var _ resource.ResourceWithModifyPlan = &VcComplexResource{} -var _ resource.ResourceWithImportState = &VcComplexResource{} - -type VcComplexResource struct { - client *core.UniversalClient -} - -type VcComplexModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - CpuAllocated types.String `tfsdk:"cpu_allocated"` - CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - MemAllocated types.Int64 `tfsdk:"mem_allocated"` - MemGuaranteed types.Int64 `tfsdk:"mem_guaranteed"` - NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` - NeedExternalAddressSNAT types.Bool `tfsdk:"need_external_address_s_n_a_t"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SegroupName types.String `tfsdk:"segroup_name"` - StorageConfig types.String `tfsdk:"storage_config"` - VIPCount types.Int64 `tfsdk:"v_i_p_count"` - VappName types.String `tfsdk:"vapp_name"` - VdcNetworkPool types.String `tfsdk:"vdc_network_pool"` - VdcProviderGateway types.String `tfsdk:"vdc_provider_gateway"` - VirtualServicesCount types.String `tfsdk:"virtual_services_count"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcComplexResource() resource.Resource { - return &VcComplexResource{} -} - -func (r *VcComplexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_complex" -} - -func (r *VcComplexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "cpu_allocated": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "шт Не изменяется после создания."}, - "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("internet-ipv4-v1"), MarkdownDescription: "Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT Не изменяется после создания."}, - "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "gb Не изменяется после создания."}, - "mem_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "Не изменяется после создания."}, - "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает AVI в Cloud Director Не изменяется после создания."}, - "need_external_address_s_n_a_t": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP на Edge. Используется для SNAT всего трафика Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "segroup_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("SEGROUP-SANDBOX-CL1-SHARED-01"), MarkdownDescription: "Наименование segroup, в котором будут выделяться пулы на AVI Не изменяется после создания."}, - "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{\"name\": \"\", \"size\": 1024}]"), MarkdownDescription: "Описывается как массив объектов, где описывается имя политики и её размер Не изменяется после создания.", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "v_i_p_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "ш Не изменяется после создания."}, - "vapp_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя vApp Не изменяется после создания."}, - "vdc_network_pool": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nsxt-sandbox-geneve-np"), MarkdownDescription: "Не изменяется после создания."}, - "vdc_provider_gateway": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox-v1cl1-pvdc"), MarkdownDescription: "Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String Не изменяется после создания."}, - "virtual_services_count": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcComplexResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcComplexModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.CpuAllocated.ValueString(), state.CpuAllocated.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить cpu_allocated", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { - if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { - if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить mem_allocated", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MemGuaranteed.IsNull() && !plan.MemGuaranteed.IsUnknown() && !state.MemGuaranteed.IsNull() && !state.MemGuaranteed.IsUnknown() { - if plan.MemGuaranteed.ValueInt64() != state.MemGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить mem_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { - if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить need_enable_a_v_i", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NeedExternalAddressSNAT.IsNull() && !plan.NeedExternalAddressSNAT.IsUnknown() && !state.NeedExternalAddressSNAT.IsNull() && !state.NeedExternalAddressSNAT.IsUnknown() { - if plan.NeedExternalAddressSNAT.ValueBool() != state.NeedExternalAddressSNAT.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить need_external_address_s_n_a_t", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.SegroupName.IsNull() && !plan.SegroupName.IsUnknown() && !state.SegroupName.IsNull() && !state.SegroupName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.SegroupName.ValueString(), state.SegroupName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить segroup_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.StorageConfig.ValueString(), state.StorageConfig.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить storage_config", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VIPCount.IsNull() && !plan.VIPCount.IsUnknown() && !state.VIPCount.IsNull() && !state.VIPCount.IsUnknown() { - if plan.VIPCount.ValueInt64() != state.VIPCount.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить v_i_p_count", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappName.ValueString(), state.VappName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcNetworkPool.IsNull() && !plan.VdcNetworkPool.IsUnknown() && !state.VdcNetworkPool.IsNull() && !state.VdcNetworkPool.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcNetworkPool.ValueString(), state.VdcNetworkPool.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_network_pool", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcProviderGateway.IsNull() && !plan.VdcProviderGateway.IsUnknown() && !state.VdcProviderGateway.IsNull() && !state.VdcProviderGateway.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcProviderGateway.ValueString(), state.VdcProviderGateway.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_provider_gateway", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VirtualServicesCount.ValueString(), state.VirtualServicesCount.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить virtual_services_count", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VappName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") - return - } - if config.VappName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 332: resources_core.FormatString(config.ResourceRealm), - 334: resources_core.FormatString(config.VappName), - 358: resources_core.FormatString(config.VdcProviderGateway), - 359: resources_core.FormatString(config.VirtualServicesCount), - 360: resources_core.FormatString(config.IpSpaceName), - 399: resources_core.FormatString(config.StorageConfig), - 400: resources_core.FormatString(config.VdcNetworkPool), - 401: resources_core.FormatInt64(config.CpuGuaranteed), - 402: resources_core.FormatInt64(config.MemGuaranteed), - 403: resources_core.FormatString(config.SegroupName), - 404: resources_core.FormatBool(config.NeedExternalAddressSNAT), - 405: resources_core.FormatBool(config.NeedEnableAVI), - 566: resources_core.FormatString(config.CpuAllocated), - 567: resources_core.FormatInt64(config.MemAllocated), - 568: resources_core.FormatInt64(config.VIPCount), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 113, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcComplexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VappName.IsNull() || data.VappName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 332: resources_core.FormatString(data.ResourceRealm), - 334: resources_core.FormatString(data.VappName), - 358: resources_core.FormatString(data.VdcProviderGateway), - 359: resources_core.FormatString(data.VirtualServicesCount), - 360: resources_core.FormatString(data.IpSpaceName), - 399: resources_core.FormatString(data.StorageConfig), - 400: resources_core.FormatString(data.VdcNetworkPool), - 401: resources_core.FormatInt64(data.CpuGuaranteed), - 402: resources_core.FormatInt64(data.MemGuaranteed), - 403: resources_core.FormatString(data.SegroupName), - 404: resources_core.FormatBool(data.NeedExternalAddressSNAT), - 405: resources_core.FormatBool(data.NeedEnableAVI), - 566: resources_core.FormatString(data.CpuAllocated), - 567: resources_core.FormatInt64(data.MemAllocated), - 568: resources_core.FormatInt64(data.VIPCount), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 113, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 113, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcComplexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 113, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcComplexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcComplexModel - var state VcComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 113, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "string"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "memGuaranteed", Field: "MemGuaranteed", Type: "int64"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "needExternalAddressSNAT", Field: "NeedExternalAddressSNAT", Type: "bool"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "segroupName", Field: "SegroupName", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "string"}, - {Code: "vIPCount", Field: "VIPCount", Type: "int64"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcNetworkPool", Field: "VdcNetworkPool", Type: "string"}, - {Code: "vdcProviderGateway", Field: "VdcProviderGateway", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcComplexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcComplexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcComplexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/114_gitea_complex_resource.go b/generated/test/go/114_gitea_complex_resource.go deleted file mode 100644 index 6fb5f15..0000000 --- a/generated/test/go/114_gitea_complex_resource.go +++ /dev/null @@ -1,322 +0,0 @@ -package resources_gen - -import ( - "context" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: gitea_complex -// Service ID: 114 - -var _ resource.Resource = &GiteaComplexResource{} -var _ resource.ResourceWithModifyPlan = &GiteaComplexResource{} -var _ resource.ResourceWithImportState = &GiteaComplexResource{} - -type GiteaComplexResource struct { - client *core.UniversalClient -} - -type GiteaComplexModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewGiteaComplexResource() resource.Resource { - return &GiteaComplexResource{} -} - -func (r *GiteaComplexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_gitea_complex" -} - -func (r *GiteaComplexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *GiteaComplexResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *GiteaComplexModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *GiteaComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan GiteaComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{} - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 114, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *GiteaComplexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data GiteaComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 114, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{} - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 114, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 114, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaComplexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state GiteaComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 114, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *GiteaComplexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan GiteaComplexModel - var state GiteaComplexModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 114, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaComplexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state GiteaComplexModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *GiteaComplexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *GiteaComplexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/115_mariadb_database_resource.go b/generated/test/go/115_mariadb_database_resource.go deleted file mode 100644 index ecce971..0000000 --- a/generated/test/go/115_mariadb_database_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Subresource: database - -var _ resource.Resource = &MariadbDatabaseResource{} - -// MariadbDatabaseResource manages a subresource via operations. -type MariadbDatabaseResource struct { - client *core.UniversalClient -} - -type MariadbDatabaseModel struct { - ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` -} - -func NewMariadbDatabaseResource() resource.Resource { - return &MariadbDatabaseResource{} -} - -func (r *MariadbDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_database" -} - -func (r *MariadbDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mariadb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("db-bdwhrjlz"), MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MariadbDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/115_mariadb_resource.go b/generated/test/go/115_mariadb_resource.go deleted file mode 100644 index 16798a3..0000000 --- a/generated/test/go/115_mariadb_resource.go +++ /dev/null @@ -1,447 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Service ID: 115 - -var _ resource.Resource = &MariadbResource{} -var _ resource.ResourceWithModifyPlan = &MariadbResource{} -var _ resource.ResourceWithImportState = &MariadbResource{} - -type MariadbResource struct { - client *core.UniversalClient -} - -// MariadbAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type MariadbAccessConfigurationModel struct { - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` -} - -// MariadbAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type MariadbAutoscaleConfigurationModel struct { - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` -} - -// MariadbBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type MariadbBackupConfigurationModel struct { - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` -} - -// MariadbClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type MariadbClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// MariadbMariadbConfigurationModel — вложенная модель для map-fixed параметра mariadbConfiguration. -type MariadbMariadbConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -// MariadbStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type MariadbStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type MariadbModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *MariadbAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *MariadbAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *MariadbBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *MariadbClusterConfigurationModel `tfsdk:"cluster_configuration"` - MariadbConfiguration *MariadbMariadbConfigurationModel `tfsdk:"mariadb_configuration"` - StartupConfiguration *MariadbStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewMariadbResource() resource.Resource { - return &MariadbResource{} -} - -func (r *MariadbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb" -} - -func (r *MariadbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(720)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать"}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "mariadb_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("9.4.0")}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *MariadbModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 877: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 878: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 879: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString())}), - 880: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.MariadbConfiguration.Version.ValueString())}), - 881: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString())}), - 882: resources_core.BuildJSON(map[string]string{"quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 115, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *MariadbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 877: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 878: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 879: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString())}), - 880: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.MariadbConfiguration.Version.ValueString())}), - 881: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString())}), - 882: resources_core.BuildJSON(map[string]string{"quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 115, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 115, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 115, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *MariadbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbModel - var state MariadbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 883: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 884: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - 885: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.MariadbConfiguration.Version.ValueString())}), - 886: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 887: resources_core.BuildJSON(map[string]string{"percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64()), "enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 115, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "mariadbConfiguration", Field: "MariadbConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *MariadbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *MariadbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/115_mariadb_user_resource.go b/generated/test/go/115_mariadb_user_resource.go deleted file mode 100644 index 5f74ba8..0000000 --- a/generated/test/go/115_mariadb_user_resource.go +++ /dev/null @@ -1,367 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mariadb -// Subresource: user - -var _ resource.Resource = &MariadbUserResource{} - -// MariadbUserResource manages a subresource via operations. -type MariadbUserResource struct { - client *core.UniversalClient -} - -type MariadbUserModel struct { - ID types.String `tfsdk:"id"` - MariadbID types.String `tfsdk:"mariadb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - DbName types.String `tfsdk:"db_name"` - Password types.String `tfsdk:"password"` - Username types.String `tfsdk:"username"` -} - -func NewMariadbUserResource() resource.Resource { - return &MariadbUserResource{} -} - -func (r *MariadbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mariadb_user" -} - -func (r *MariadbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mariadb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("%"), MarkdownDescription: "Пример: `%` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо указать массив существующих баз Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "password": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("dz8vtf8o"), MarkdownDescription: "Секрет будет записан в хранилище секретов и не будет отображаться публично Не изменяется после создания. Изменение требует пересоздания.", Sensitive: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("user2ktdtvd8"), MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MariadbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MariadbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "password": resources_core.FormatString(plan.Password), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "password", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "password": resources_core.FormatString(plan.Password), - "dbName": resources_core.FormatString(plan.DbName), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MariadbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MariadbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MariadbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "password": resources_core.FormatString(plan.Password), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MariadbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MariadbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MariadbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(state.AccessHosts), - "dbName": resources_core.FormatString(state.DbName), - "password": resources_core.FormatString(state.Password), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "password", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MariadbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/116_kafka_resource.go b/generated/test/go/116_kafka_resource.go deleted file mode 100644 index 746405d..0000000 --- a/generated/test/go/116_kafka_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Service ID: 116 - -var _ resource.Resource = &KafkaResource{} -var _ resource.ResourceWithModifyPlan = &KafkaResource{} -var _ resource.ResourceWithImportState = &KafkaResource{} - -type KafkaResource struct { - client *core.UniversalClient -} - -// KafkaAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type KafkaAccessConfigurationModel struct { - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - AllocationExternal types.Bool `tfsdk:"allocation_external" json:"allocationExternal"` -} - -// KafkaClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type KafkaClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// KafkaStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type KafkaStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type KafkaModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *KafkaAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *KafkaClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *KafkaStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewKafkaResource() resource.Resource { - return &KafkaResource{} -} - -func (r *KafkaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka" -} - -func (r *KafkaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "allocation_external": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048)}, - "replicas": schema.Int64Attribute{Required: true}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *KafkaModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1084: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1085: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1086: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "allocationExternal": fmt.Sprintf("%v", config.AccessConfiguration.AllocationExternal.ValueBool())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 116, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *KafkaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1084: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1085: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1086: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "allocationExternal": fmt.Sprintf("%v", data.AccessConfiguration.AllocationExternal.ValueBool())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 116, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 116, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 116, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *KafkaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaModel - var state KafkaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1087: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 1088: resources_core.BuildJSON(map[string]string{"allocationExternal": fmt.Sprintf("%v", plan.AccessConfiguration.AllocationExternal.ValueBool()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 116, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *KafkaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *KafkaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/116_kafka_topic_resource.go b/generated/test/go/116_kafka_topic_resource.go deleted file mode 100644 index 3f289b3..0000000 --- a/generated/test/go/116_kafka_topic_resource.go +++ /dev/null @@ -1,364 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Subresource: topic - -var _ resource.Resource = &KafkaTopicResource{} - -// KafkaTopicResource manages a subresource via operations. -type KafkaTopicResource struct { - client *core.UniversalClient -} - -type KafkaTopicModel struct { - ID types.String `tfsdk:"id"` - KafkaID types.String `tfsdk:"kafka_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - NameTopic types.String `tfsdk:"name_topic"` - Partitions types.Int64 `tfsdk:"partitions"` - Replicas types.Int64 `tfsdk:"replicas"` -} - -func NewKafkaTopicResource() resource.Resource { - return &KafkaTopicResource{} -} - -func (r *KafkaTopicResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka_topic" -} - -func (r *KafkaTopicResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "kafka_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций можно увеличить, но уменьшить их нельзя."}, - "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество реплик не может привышать количество реплик брокера"}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaTopicResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan KafkaTopicModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if plan.Partitions.IsNull() || plan.Partitions.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") - return - } - if plan.Replicas.IsNull() || plan.Replicas.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - } - identityCodes := []string{ - "nameTopic", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "partitions": resources_core.FormatInt64(plan.Partitions), - "replicas": resources_core.FormatInt64(plan.Replicas), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_topic", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaTopicResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaTopicResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaTopicModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_topic" == "" { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "partitions": resources_core.FormatInt64(plan.Partitions), - "replicas": resources_core.FormatInt64(plan.Replicas), - }) - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_topic", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "topic", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaTopicResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaTopicModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_topic" == "" { - return - } - - instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "nameTopic": resources_core.FormatString(state.NameTopic), - } - identityCodes := []string{ - "nameTopic", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("topic", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(state.NameTopic), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_topic", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *KafkaTopicResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/116_kafka_user_resource.go b/generated/test/go/116_kafka_user_resource.go deleted file mode 100644 index 52ea73c..0000000 --- a/generated/test/go/116_kafka_user_resource.go +++ /dev/null @@ -1,373 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: kafka -// Subresource: user - -var _ resource.Resource = &KafkaUserResource{} - -// KafkaUserResource manages a subresource via operations. -type KafkaUserResource struct { - client *core.UniversalClient -} - -type KafkaUserModel struct { - ID types.String `tfsdk:"id"` - KafkaID types.String `tfsdk:"kafka_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - Group types.String `tfsdk:"group"` - NameTopic types.String `tfsdk:"name_topic"` - Operations types.String `tfsdk:"operations"` - Username types.String `tfsdk:"username"` -} - -func NewKafkaUserResource() resource.Resource { - return &KafkaUserResource{} -} - -func (r *KafkaUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_kafka_user" -} - -func (r *KafkaUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "kafka_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("*"), MarkdownDescription: "Пример: `192.168.1.0/24` или `*`"}, - "group": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("*"), MarkdownDescription: "Пример: `order-consumers` или `*`"}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`"}, - "operations": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `Create, Describe, Read, Write`"}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `username-admin` Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *KafkaUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan KafkaUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.NameTopic.IsNull() || plan.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if plan.Operations.IsNull() || plan.Operations.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "operations is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "nameTopic": resources_core.FormatString(plan.NameTopic), - "operations": resources_core.FormatString(plan.Operations), - "group": resources_core.FormatString(plan.Group), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *KafkaUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan KafkaUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_user" == "" { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.KafkaID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "nameTopic": resources_core.FormatString(plan.NameTopic), - "operations": resources_core.FormatString(plan.Operations), - "group": resources_core.FormatString(plan.Group), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "username": resources_core.FormatString(plan.Username), - }) - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *KafkaUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state KafkaUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.KafkaID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *KafkaUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/117_nifi_resource.go b/generated/test/go/117_nifi_resource.go deleted file mode 100644 index c2886ce..0000000 --- a/generated/test/go/117_nifi_resource.go +++ /dev/null @@ -1,533 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nifi -// Service ID: 117 - -var _ resource.Resource = &NifiResource{} -var _ resource.ResourceWithModifyPlan = &NifiResource{} -var _ resource.ResourceWithImportState = &NifiResource{} - -type NifiResource struct { - client *core.UniversalClient -} - -type NifiModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KafkaUid types.String `tfsdk:"kafka_uid"` - NameTopic types.String `tfsdk:"name_topic"` - Partitions types.Int64 `tfsdk:"partitions"` - Replicas types.Int64 `tfsdk:"replicas"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNifiResource() resource.Resource { - return &NifiResource{} -} - -func (r *NifiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nifi" -} - -func (r *NifiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "kafka_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Выбрать кластер из списка Не изменяется после создания."}, - "name_topic": schema.StringAttribute{Required: true, MarkdownDescription: "Пример: `orders-events` Не изменяется после создания."}, - "partitions": schema.Int64Attribute{Required: true, MarkdownDescription: "Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50`"}, - "replicas": schema.Int64Attribute{Required: true, MarkdownDescription: "Количество партиций ≤ (число брокеров). Пример `2`"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NifiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NifiModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить kafka_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NameTopic.IsNull() && !plan.NameTopic.IsUnknown() && !state.NameTopic.IsNull() && !state.NameTopic.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NameTopic.ValueString(), state.NameTopic.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить name_topic", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.KafkaUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if config.KafkaUid.IsUnknown() { - return - } - if config.NameTopic.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if config.NameTopic.IsUnknown() { - return - } - if config.Partitions.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") - return - } - if config.Partitions.IsUnknown() { - return - } - if config.Replicas.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") - return - } - if config.Replicas.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { - config.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - params := map[int]string{ - 471: resources_core.FormatString(config.KafkaUid), - 472: resources_core.FormatInt64(config.Partitions), - 473: resources_core.FormatInt64(config.Replicas), - 474: resources_core.FormatString(config.NameTopic), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 117, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NifiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if data.NameTopic.IsNull() || data.NameTopic.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "name_topic is required.") - return - } - if data.Partitions.IsNull() || data.Partitions.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "partitions is required.") - return - } - if data.Replicas.IsNull() || data.Replicas.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "replicas is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalKafkaUid := data.KafkaUid - if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { - data.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 117, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 471: resources_core.FormatString(data.KafkaUid), - 472: resources_core.FormatInt64(data.Partitions), - 473: resources_core.FormatInt64(data.Replicas), - 474: resources_core.FormatString(data.NameTopic), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 117, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 117, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "nameTopic", Field: "NameTopic", Type: "string"}, - {Code: "partitions", Field: "Partitions", Type: "int64"}, - {Code: "replicas", Field: "Replicas", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalKafkaUid.IsNull() && !originalKafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), originalKafkaUid.ValueString()) { - state.KafkaUid = originalKafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NifiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 117, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "nameTopic", Field: "NameTopic", Type: "string"}, - {Code: "partitions", Field: "Partitions", Type: "int64"}, - {Code: "replicas", Field: "Replicas", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { - newState.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() && !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - if strings.EqualFold(newState.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - newState.KafkaUid = state.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NifiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NifiModel - var state NifiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.Partitions.IsNull() != state.Partitions.IsNull() || plan.Partitions.IsUnknown() != state.Partitions.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Partitions.IsNull() && !plan.Partitions.IsUnknown() && !state.Partitions.IsNull() && !state.Partitions.IsUnknown() { - if plan.Partitions.ValueInt64() != state.Partitions.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.Replicas.IsNull() != state.Replicas.IsNull() || plan.Replicas.IsUnknown() != state.Replicas.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Replicas.IsNull() && !plan.Replicas.IsUnknown() && !state.Replicas.IsNull() && !state.Replicas.IsUnknown() { - if plan.Replicas.ValueInt64() != state.Replicas.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 477: resources_core.FormatInt64(plan.Partitions), - 478: resources_core.FormatInt64(plan.Replicas), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 117, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "nameTopic", Field: "NameTopic", Type: "string"}, - {Code: "partitions", Field: "Partitions", Type: "int64"}, - {Code: "replicas", Field: "Replicas", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), plan.KafkaUid.ValueString()) { - state.KafkaUid = plan.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NifiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NifiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NifiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NifiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/119_akhq_resource.go b/generated/test/go/119_akhq_resource.go deleted file mode 100644 index 8ce242b..0000000 --- a/generated/test/go/119_akhq_resource.go +++ /dev/null @@ -1,554 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: akhq -// Service ID: 119 - -var _ resource.Resource = &AkhqResource{} -var _ resource.ResourceWithModifyPlan = &AkhqResource{} -var _ resource.ResourceWithImportState = &AkhqResource{} - -type AkhqResource struct { - client *core.UniversalClient -} - -type AkhqModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - KafkaUid types.String `tfsdk:"kafka_uid"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewAkhqResource() resource.Resource { - return &AkhqResource{} -} - -func (r *AkhqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_akhq" -} - -func (r *AkhqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "kafka_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Выбрать кластер из списка"}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `1000` Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `1024` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *AkhqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *AkhqModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Domain.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if config.Domain.IsUnknown() { - return - } - if config.KafkaUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if config.KafkaUid.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.KafkaUid.IsNull() && !config.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, config.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != config.KafkaUid.ValueString() { - config.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - params := map[int]string{ - 550: resources_core.FormatString(config.ResourceRealm), - 551: resources_core.FormatInt64(config.ResourceMemory), - 552: resources_core.FormatInt64(config.ResourceCPU), - 553: resources_core.FormatString(config.Domain), - 554: resources_core.FormatInt64(config.ResourceInstances), - 555: resources_core.FormatString(config.KafkaUid), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 119, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *AkhqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Domain.IsNull() || data.Domain.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if data.KafkaUid.IsNull() || data.KafkaUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kafka_uid is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalKafkaUid := data.KafkaUid - if !data.KafkaUid.IsNull() && !data.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, data.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedKafkaUid != "" && resolvedKafkaUid != data.KafkaUid.ValueString() { - data.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 550: resources_core.FormatString(data.ResourceRealm), - 551: resources_core.FormatInt64(data.ResourceMemory), - 552: resources_core.FormatInt64(data.ResourceCPU), - 553: resources_core.FormatString(data.Domain), - 554: resources_core.FormatInt64(data.ResourceInstances), - 555: resources_core.FormatString(data.KafkaUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 119, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 119, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalKafkaUid.IsNull() && !originalKafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), originalKafkaUid.ValueString()) { - state.KafkaUid = originalKafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *AkhqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 119, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, newState.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != newState.KafkaUid.ValueString() { - newState.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() && !newState.KafkaUid.IsNull() && !newState.KafkaUid.IsUnknown() { - if strings.EqualFold(newState.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - newState.KafkaUid = state.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *AkhqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan AkhqModel - var state AkhqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.KafkaUid.IsNull() != state.KafkaUid.IsNull() || plan.KafkaUid.IsUnknown() != state.KafkaUid.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if !strings.EqualFold(plan.KafkaUid.ValueString(), state.KafkaUid.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 565: resources_core.FormatString(plan.KafkaUid), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 119, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "kafkaUid", Field: "KafkaUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - resolvedKafkaUid, err := r.client.ResolveRefSvcParamValue(ctx, 116, state.KafkaUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve kafka_uid", err.Error()) - } else if resolvedKafkaUid != "" && resolvedKafkaUid != state.KafkaUid.ValueString() { - state.KafkaUid = types.StringValue(resolvedKafkaUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.KafkaUid.IsNull() && !plan.KafkaUid.IsUnknown() && !state.KafkaUid.IsNull() && !state.KafkaUid.IsUnknown() { - if strings.EqualFold(state.KafkaUid.ValueString(), plan.KafkaUid.ValueString()) { - state.KafkaUid = plan.KafkaUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *AkhqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state AkhqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *AkhqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *AkhqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/120_clickhouse_database_resource.go b/generated/test/go/120_clickhouse_database_resource.go deleted file mode 100644 index cc5c42e..0000000 --- a/generated/test/go/120_clickhouse_database_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Subresource: database - -var _ resource.Resource = &ClickhouseDatabaseResource{} - -// ClickhouseDatabaseResource manages a subresource via operations. -type ClickhouseDatabaseResource struct { - client *core.UniversalClient -} - -type ClickhouseDatabaseModel struct { - ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` -} - -func NewClickhouseDatabaseResource() resource.Resource { - return &ClickhouseDatabaseResource{} -} - -func (r *ClickhouseDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_database" -} - -func (r *ClickhouseDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "clickhouse_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("db01"), MarkdownDescription: "Пример: `db01` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - } - identityCodes := []string{ - "dbName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ClickhouseDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/120_clickhouse_resource.go b/generated/test/go/120_clickhouse_resource.go deleted file mode 100644 index b5c560d..0000000 --- a/generated/test/go/120_clickhouse_resource.go +++ /dev/null @@ -1,487 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Service ID: 120 - -var _ resource.Resource = &ClickhouseResource{} -var _ resource.ResourceWithModifyPlan = &ClickhouseResource{} -var _ resource.ResourceWithImportState = &ClickhouseResource{} - -type ClickhouseResource struct { - client *core.UniversalClient -} - -// ClickhouseAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type ClickhouseAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// ClickhouseAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type ClickhouseAutoscaleConfigurationModel struct { - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` -} - -// ClickhouseBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type ClickhouseBackupConfigurationModel struct { - Retain types.Int64 `tfsdk:"retain" json:"retain"` - Schedule types.String `tfsdk:"schedule" json:"schedule"` - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` -} - -// ClickhouseClickhouseConfigurationModel — вложенная модель для map-fixed параметра clickhouseConfiguration. -type ClickhouseClickhouseConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` -} - -// ClickhouseClickhouseKeeperConfigurationModel — вложенная модель для map-fixed параметра clickhouseKeeperConfiguration. -type ClickhouseClickhouseKeeperConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// ClickhouseClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type ClickhouseClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Shards types.Int64 `tfsdk:"shards" json:"shards"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// ClickhouseStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type ClickhouseStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type ClickhouseModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *ClickhouseAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *ClickhouseAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *ClickhouseBackupConfigurationModel `tfsdk:"backup_configuration"` - ClickhouseConfiguration *ClickhouseClickhouseConfigurationModel `tfsdk:"clickhouse_configuration"` - ClickhouseFilesConfiguration types.String `tfsdk:"clickhouse_files_configuration"` - ClickhouseKeeperConfiguration *ClickhouseClickhouseKeeperConfigurationModel `tfsdk:"clickhouse_keeper_configuration"` - ClusterConfiguration *ClickhouseClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *ClickhouseStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewClickhouseResource() resource.Resource { - return &ClickhouseResource{} -} - -func (r *ClickhouseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse" -} - -func (r *ClickhouseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7)}, - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - }}, - "clickhouse_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("25.10")}, - }}, - "clickhouse_files_configuration": schema.StringAttribute{Optional: true, MarkdownDescription: "Формат:
- path: somethings3.xml
xmlConfig: '<clickhouse>......</clickhouse>'"}, - "clickhouse_keeper_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "shards": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда"}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ClickhouseModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 837: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 838: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", config.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 839: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - 840: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 841: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.ClickhouseConfiguration.Version.ValueString())}), - 842: resources_core.BuildJSON(map[string]string{"retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString()), "s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString())}), - 843: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64())}), - 851: resources_core.FormatString(config.ClickhouseFilesConfiguration), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 120, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ClickhouseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 837: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 838: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "shards": fmt.Sprintf("%d", data.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 839: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - 840: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 841: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.ClickhouseConfiguration.Version.ValueString())}), - 842: resources_core.BuildJSON(map[string]string{"retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString()), "s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString())}), - 843: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64())}), - 851: resources_core.FormatString(data.ClickhouseFilesConfiguration), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 120, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 120, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 120, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ClickhouseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseModel - var state ClickhouseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ClickhouseFilesConfiguration.IsNull() != state.ClickhouseFilesConfiguration.IsNull() || plan.ClickhouseFilesConfiguration.IsUnknown() != state.ClickhouseFilesConfiguration.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ClickhouseFilesConfiguration.IsNull() && !plan.ClickhouseFilesConfiguration.IsUnknown() && !state.ClickhouseFilesConfiguration.IsNull() && !state.ClickhouseFilesConfiguration.IsUnknown() { - if !strings.EqualFold(plan.ClickhouseFilesConfiguration.ValueString(), state.ClickhouseFilesConfiguration.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 844: resources_core.BuildJSON(map[string]string{"shards": fmt.Sprintf("%d", plan.ClusterConfiguration.Shards.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 845: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClickhouseKeeperConfiguration.Replicas.ValueInt64())}), - 846: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - 847: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.ClickhouseConfiguration.Version.ValueString())}), - 848: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 849: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - 850: resources_core.FormatString(plan.ClickhouseFilesConfiguration), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 120, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clickhouseConfiguration", Field: "ClickhouseConfiguration", Type: "map-fixed"}, - {Code: "clickhouseFilesConfiguration", Field: "ClickhouseFilesConfiguration", Type: "array-map-fixed"}, - {Code: "clickhouseKeeperConfiguration", Field: "ClickhouseKeeperConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ClickhouseResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ClickhouseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/120_clickhouse_user_resource.go b/generated/test/go/120_clickhouse_user_resource.go deleted file mode 100644 index 8dc28f7..0000000 --- a/generated/test/go/120_clickhouse_user_resource.go +++ /dev/null @@ -1,359 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: clickhouse -// Subresource: user - -var _ resource.Resource = &ClickhouseUserResource{} - -// ClickhouseUserResource manages a subresource via operations. -type ClickhouseUserResource struct { - client *core.UniversalClient -} - -type ClickhouseUserModel struct { - ID types.String `tfsdk:"id"` - ClickhouseID types.String `tfsdk:"clickhouse_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessHosts types.String `tfsdk:"access_hosts"` - DbName types.String `tfsdk:"db_name"` - Username types.String `tfsdk:"username"` -} - -func NewClickhouseUserResource() resource.Resource { - return &ClickhouseUserResource{} -} - -func (r *ClickhouseUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_clickhouse_user" -} - -func (r *ClickhouseUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "clickhouse_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_hosts": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("::/0"), MarkdownDescription: "Пример: `::/0` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myusername"), MarkdownDescription: "Пример: `myusername` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ClickhouseUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan ClickhouseUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "accessHosts": resources_core.FormatString(plan.AccessHosts), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ClickhouseUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ClickhouseUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.ClickhouseID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(plan.AccessHosts), - "dbName": resources_core.FormatString(plan.DbName), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *ClickhouseUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ClickhouseUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.ClickhouseID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "accessHosts": resources_core.FormatString(state.AccessHosts), - "dbName": resources_core.FormatString(state.DbName), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "accessHosts", - "dbName", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *ClickhouseUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/12_s3_resource.go b/generated/test/go/12_s3_resource.go deleted file mode 100644 index 2d179e4..0000000 --- a/generated/test/go/12_s3_resource.go +++ /dev/null @@ -1,421 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 -// Service ID: 12 - -var _ resource.Resource = &S3Resource{} -var _ resource.ResourceWithModifyPlan = &S3Resource{} -var _ resource.ResourceWithImportState = &S3Resource{} - -type S3Resource struct { - client *core.UniversalClient -} - -type S3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DisplayName types.String `tfsdk:"display_name"` - MaxBucketsPerUser types.Int64 `tfsdk:"max_buckets_per_user"` - MaxObjectsPerBucket types.Int64 `tfsdk:"max_objects_per_bucket"` - MaxSizeGbPerUser types.Int64 `tfsdk:"max_size_gb_per_user"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewS3Resource() resource.Resource { - return &S3Resource{} -} - -func (r *S3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3" -} - -func (r *S3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "display_name": schema.StringAttribute{Required: true, MarkdownDescription: "Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента Не изменяется после создания."}, - "max_buckets_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на пользователя. Количество бакетов пользователя. Передаём руками"}, - "max_objects_per_bucket": schema.Int64Attribute{Optional: true, MarkdownDescription: "Квота на бакет. Кол-во объектов. Передаём руками"}, - "max_size_gb_per_user": schema.Int64Attribute{Optional: true, MarkdownDescription: "maxSizeGb. Передаём руками"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ceph.tst.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *S3Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.DisplayName.IsNull() && !plan.DisplayName.IsUnknown() && !state.DisplayName.IsNull() && !state.DisplayName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.DisplayName.ValueString(), state.DisplayName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить display_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.DisplayName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") - return - } - if config.DisplayName.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 163: resources_core.FormatString(config.ResourceRealm), - 320: resources_core.FormatString(config.DisplayName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 12, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *S3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.DisplayName.IsNull() || data.DisplayName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "display_name is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 163: resources_core.FormatString(data.ResourceRealm), - 320: resources_core.FormatString(data.DisplayName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 12, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 12, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 12, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *S3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3Model - var state S3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.MaxSizeGbPerUser.IsNull() != state.MaxSizeGbPerUser.IsNull() || plan.MaxSizeGbPerUser.IsUnknown() != state.MaxSizeGbPerUser.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxSizeGbPerUser.IsNull() && !plan.MaxSizeGbPerUser.IsUnknown() && !state.MaxSizeGbPerUser.IsNull() && !state.MaxSizeGbPerUser.IsUnknown() { - if plan.MaxSizeGbPerUser.ValueInt64() != state.MaxSizeGbPerUser.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MaxObjectsPerBucket.IsNull() != state.MaxObjectsPerBucket.IsNull() || plan.MaxObjectsPerBucket.IsUnknown() != state.MaxObjectsPerBucket.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxObjectsPerBucket.IsNull() && !plan.MaxObjectsPerBucket.IsUnknown() && !state.MaxObjectsPerBucket.IsNull() && !state.MaxObjectsPerBucket.IsUnknown() { - if plan.MaxObjectsPerBucket.ValueInt64() != state.MaxObjectsPerBucket.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MaxBucketsPerUser.IsNull() != state.MaxBucketsPerUser.IsNull() || plan.MaxBucketsPerUser.IsUnknown() != state.MaxBucketsPerUser.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MaxBucketsPerUser.IsNull() && !plan.MaxBucketsPerUser.IsUnknown() && !state.MaxBucketsPerUser.IsNull() && !state.MaxBucketsPerUser.IsUnknown() { - if plan.MaxBucketsPerUser.ValueInt64() != state.MaxBucketsPerUser.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 50: resources_core.FormatInt64(plan.MaxSizeGbPerUser), - 51: resources_core.FormatInt64(plan.MaxObjectsPerBucket), - 52: resources_core.FormatInt64(plan.MaxBucketsPerUser), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 12, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "displayName", Field: "DisplayName", Type: "string"}, - {Code: "maxBucketsPerUser", Field: "MaxBucketsPerUser", Type: "int64"}, - {Code: "maxObjectsPerBucket", Field: "MaxObjectsPerBucket", Type: "int64"}, - {Code: "maxSizeGbPerUser", Field: "MaxSizeGbPerUser", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *S3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *S3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/12_s3_sub_user_resource.go b/generated/test/go/12_s3_sub_user_resource.go deleted file mode 100644 index b8e0168..0000000 --- a/generated/test/go/12_s3_sub_user_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3 -// Subresource: sub_user - -var _ resource.Resource = &S3SubUserResource{} - -// S3SubUserResource manages a subresource via operations. -type S3SubUserResource struct { - client *core.UniversalClient -} - -type S3SubUserModel struct { - ID types.String `tfsdk:"id"` - S3ID types.String `tfsdk:"s3_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SubUserAccess types.String `tfsdk:"sub_user_access"` - SubUserName types.String `tfsdk:"sub_user_name"` -} - -func NewS3SubUserResource() resource.Resource { - return &S3SubUserResource{} -} - -func (r *S3SubUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3_sub_user" -} - -func (r *S3SubUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "s3_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "sub_user_access": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "sub_user_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3SubUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan S3SubUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.SubUserAccess.IsNull() || plan.SubUserAccess.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "sub_user_access is required.") - return - } - if plan.SubUserName.IsNull() || plan.SubUserName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "sub_user_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - "subUserName": resources_core.FormatString(plan.SubUserName), - } - identityCodes := []string{ - "subUserAccess", - "subUserName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "subUserName": resources_core.FormatString(plan.SubUserName), - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_sub_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *S3SubUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3SubUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3SubUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.S3ID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(plan.SubUserAccess), - "subUserName": resources_core.FormatString(plan.SubUserName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "sub_user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *S3SubUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3SubUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_sub_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.S3ID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "subUserAccess": resources_core.FormatString(state.SubUserAccess), - "subUserName": resources_core.FormatString(state.SubUserName), - } - identityCodes := []string{ - "subUserAccess", - "subUserName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("sub_user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "subUserName": resources_core.FormatString(state.SubUserName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_sub_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *S3SubUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/13_s3bucket_resource.go b/generated/test/go/13_s3bucket_resource.go deleted file mode 100644 index 644ffb7..0000000 --- a/generated/test/go/13_s3bucket_resource.go +++ /dev/null @@ -1,478 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: s3bucket -// Service ID: 13 - -var _ resource.Resource = &S3bucketResource{} -var _ resource.ResourceWithModifyPlan = &S3bucketResource{} -var _ resource.ResourceWithImportState = &S3bucketResource{} - -type S3bucketResource struct { - client *core.UniversalClient -} - -type S3bucketModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - BucketName types.String `tfsdk:"bucket_name"` - CorsAll types.Bool `tfsdk:"cors_all"` - ListAll types.Bool `tfsdk:"list_all"` - MaxSize types.String `tfsdk:"max_size"` - Placement types.String `tfsdk:"placement"` - ReadAll types.Bool `tfsdk:"read_all"` - S3UserUid types.String `tfsdk:"s3_user_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewS3bucketResource() resource.Resource { - return &S3bucketResource{} -} - -func (r *S3bucketResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_s3bucket" -} - -func (r *S3bucketResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "bucket_name": schema.StringAttribute{Required: true, MarkdownDescription: "Имя бакета s3 Не изменяется после создания."}, - "cors_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS Не изменяется после создания."}, - "list_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. Не изменяется после создания."}, - "max_size": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("-1"), MarkdownDescription: "Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes Не изменяется после создания."}, - "placement": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("HOT"), MarkdownDescription: "Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения Не изменяется после создания."}, - "read_all": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. Не изменяется после создания."}, - "s3_user_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UID Корневой услуги S3. На основе него будет создан бакет. Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *S3bucketResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *S3bucketModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.BucketName.IsNull() && !plan.BucketName.IsUnknown() && !state.BucketName.IsNull() && !state.BucketName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.BucketName.ValueString(), state.BucketName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить bucket_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.CorsAll.IsNull() && !plan.CorsAll.IsUnknown() && !state.CorsAll.IsNull() && !state.CorsAll.IsUnknown() { - if plan.CorsAll.ValueBool() != state.CorsAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить cors_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ListAll.IsNull() && !plan.ListAll.IsUnknown() && !state.ListAll.IsNull() && !state.ListAll.IsUnknown() { - if plan.ListAll.ValueBool() != state.ListAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить list_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.MaxSize.IsNull() && !plan.MaxSize.IsUnknown() && !state.MaxSize.IsNull() && !state.MaxSize.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.MaxSize.ValueString(), state.MaxSize.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить max_size", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Placement.IsNull() && !plan.Placement.IsUnknown() && !state.Placement.IsNull() && !state.Placement.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Placement.ValueString(), state.Placement.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить placement", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ReadAll.IsNull() && !plan.ReadAll.IsUnknown() && !state.ReadAll.IsNull() && !state.ReadAll.IsUnknown() { - if plan.ReadAll.ValueBool() != state.ReadAll.ValueBool() { - resp.Diagnostics.AddError("Нельзя изменить read_all", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.S3UserUid.IsNull() && !plan.S3UserUid.IsUnknown() && !state.S3UserUid.IsNull() && !state.S3UserUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.S3UserUid.ValueString(), state.S3UserUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить s3_user_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.BucketName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") - return - } - if config.BucketName.IsUnknown() { - return - } - if config.S3UserUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") - return - } - if config.S3UserUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.S3UserUid.IsNull() && !config.S3UserUid.IsUnknown() { - resolvedS3UserUid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3UserUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve s3_user_uid", err.Error()) - } else if resolvedS3UserUid != "" && resolvedS3UserUid != config.S3UserUid.ValueString() { - config.S3UserUid = types.StringValue(resolvedS3UserUid) - } - } - params := map[int]string{ - 124: resources_core.FormatString(config.S3UserUid), - 125: resources_core.FormatString(config.BucketName), - 126: resources_core.FormatString(config.MaxSize), - 127: resources_core.FormatBool(config.ReadAll), - 128: resources_core.FormatBool(config.ListAll), - 129: resources_core.FormatBool(config.CorsAll), - 130: resources_core.FormatString(config.Placement), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 13, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *S3bucketResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.BucketName.IsNull() || data.BucketName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "bucket_name is required.") - return - } - if data.S3UserUid.IsNull() || data.S3UserUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "s3_user_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 124: resources_core.FormatString(data.S3UserUid), - 125: resources_core.FormatString(data.BucketName), - 126: resources_core.FormatString(data.MaxSize), - 127: resources_core.FormatBool(data.ReadAll), - 128: resources_core.FormatBool(data.ListAll), - 129: resources_core.FormatBool(data.CorsAll), - 130: resources_core.FormatString(data.Placement), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 13, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 13, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3bucketResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 13, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *S3bucketResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan S3bucketModel - var state S3bucketModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 13, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "bucketName", Field: "BucketName", Type: "string"}, - {Code: "corsAll", Field: "CorsAll", Type: "bool"}, - {Code: "listAll", Field: "ListAll", Type: "bool"}, - {Code: "maxSize", Field: "MaxSize", Type: "string"}, - {Code: "placement", Field: "Placement", Type: "string"}, - {Code: "readAll", Field: "ReadAll", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *S3bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state S3bucketModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *S3bucketResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *S3bucketResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/148_vc_mgmt_sthutrval_cluster_resource.go b/generated/test/go/148_vc_mgmt_sthutrval_cluster_resource.go deleted file mode 100644 index cfd5712..0000000 --- a/generated/test/go/148_vc_mgmt_sthutrval_cluster_resource.go +++ /dev/null @@ -1,419 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_mgmt_sthutrval_cluster -// Service ID: 148 - -var _ resource.Resource = &VcMgmtSthutrvalClusterResource{} -var _ resource.ResourceWithModifyPlan = &VcMgmtSthutrvalClusterResource{} -var _ resource.ResourceWithImportState = &VcMgmtSthutrvalClusterResource{} - -type VcMgmtSthutrvalClusterResource struct { - client *core.UniversalClient -} - -// VcMgmtSthutrvalClusterAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type VcMgmtSthutrvalClusterAccessConfigurationModel struct { - NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i" json:"needExternalAddressAPI"` - AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i" json:"accessIpListAPI"` - NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress" json:"needExternalAddressIngress"` - AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress" json:"accessIpListIngress"` -} - -// VcMgmtSthutrvalClusterClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type VcMgmtSthutrvalClusterClusterConfigurationModel struct { - AppVersion types.String `tfsdk:"app_version" json:"appVersion"` -} - -// VcMgmtSthutrvalClusterControlPlaneConfigurationModel — вложенная модель для map-fixed параметра controlPlaneConfiguration. -type VcMgmtSthutrvalClusterControlPlaneConfigurationModel struct { - SizingPolicy types.String `tfsdk:"sizing_policy" json:"sizingPolicy"` - SizingDisk types.Int64 `tfsdk:"sizing_disk" json:"sizingDisk"` - Count types.Int64 `tfsdk:"count" json:"count"` -} - -// VcMgmtSthutrvalClusterStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type VcMgmtSthutrvalClusterStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` - ClusterName types.String `tfsdk:"cluster_name" json:"clusterName"` -} - -type VcMgmtSthutrvalClusterModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *VcMgmtSthutrvalClusterAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *VcMgmtSthutrvalClusterClusterConfigurationModel `tfsdk:"cluster_configuration"` - ControlPlaneConfiguration *VcMgmtSthutrvalClusterControlPlaneConfigurationModel `tfsdk:"control_plane_configuration"` - StartupConfiguration *VcMgmtSthutrvalClusterStartupConfigurationModel `tfsdk:"startup_configuration"` - WorkerConfiguration types.String `tfsdk:"worker_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcMgmtSthutrvalClusterResource() resource.Resource { - return &VcMgmtSthutrvalClusterResource{} -} - -func (r *VcMgmtSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_mgmt_sthutrval_cluster" -} - -func (r *VcMgmtSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false"}, - "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**"}, - "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress"}, - "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.13.1")}, - }}, - "control_plane_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "sizing_policy": schema.StringAttribute{Required: true, MarkdownDescription: "Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана"}, - "sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50)}, - "count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Облако в котором будет развёрнут тенант"}, - "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example-co5wmalx")}, - }}, - "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcMgmtSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.WorkerConfiguration.ValueString(), state.WorkerConfiguration.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить worker_configuration", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 771: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ClusterName.ValueString())}), - 772: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.AppVersion.ValueString())}), - 773: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", config.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", config.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", config.ControlPlaneConfiguration.Count.ValueInt64())}), - 774: resources_core.FormatString(config.WorkerConfiguration), - 775: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListIngress.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 148, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 148, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 771: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ClusterName.ValueString())}), - 772: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.AppVersion.ValueString())}), - 773: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", data.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", data.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", data.ControlPlaneConfiguration.Count.ValueInt64())}), - 774: resources_core.FormatString(data.WorkerConfiguration), - 775: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListIngress.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 148, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 148, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 148, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcMgmtSthutrvalClusterModel - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 148, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcMgmtSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcMgmtSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcMgmtSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcMgmtSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/149_valo_tenant_resource.go b/generated/test/go/149_valo_tenant_resource.go deleted file mode 100644 index 252f012..0000000 --- a/generated/test/go/149_valo_tenant_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: valo_tenant -// Service ID: 149 - -var _ resource.Resource = &ValoTenantResource{} -var _ resource.ResourceWithModifyPlan = &ValoTenantResource{} -var _ resource.ResourceWithImportState = &ValoTenantResource{} - -type ValoTenantResource struct { - client *core.UniversalClient -} - -// ValoTenantValoOptionsModel — вложенная модель для map-fixed параметра valoOptions. -type ValoTenantValoOptionsModel struct { - RequestApproveMaxPeriod types.Int64 `tfsdk:"request_approve_max_period" json:"requestApproveMaxPeriod"` - FfOnlineEditor types.Bool `tfsdk:"ff_online_editor" json:"ffOnlineEditor"` - FfWatermark types.Bool `tfsdk:"ff_watermark" json:"ffWatermark"` - FfUserImport types.Bool `tfsdk:"ff_user_import" json:"ffUserImport"` - FfThemes types.Bool `tfsdk:"ff_themes" json:"ffThemes"` - FfPublicLink types.Bool `tfsdk:"ff_public_link" json:"ffPublicLink"` - FfLogging types.Bool `tfsdk:"ff_logging" json:"ffLogging"` - UserCount types.Int64 `tfsdk:"user_count" json:"userCount"` -} - -type ValoTenantModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceRealm types.String `tfsdk:"resource_realm"` - S3Storage types.Int64 `tfsdk:"s3_storage"` - ValoOptions *ValoTenantValoOptionsModel `tfsdk:"valo_options"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewValoTenantResource() resource.Resource { - return &ValoTenantResource{} -} - -func (r *ValoTenantResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_valo_tenant" -} - -func (r *ValoTenantResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Required: true, MarkdownDescription: "Будет добавлено в зону valo.ngcloud.ru Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("valo.ngcloud.ru"), MarkdownDescription: "Не изменяется после создания."}, - "s3_storage": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Параметр указывается в `Gb`"}, - "valo_options": schema.SingleNestedAttribute{Optional: true, Computed: true, - Attributes: map[string]schema.Attribute{ - "request_approve_max_period": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2), MarkdownDescription: "Максимальное значение периода согласования (в днях)"}, - "ff_online_editor": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_watermark": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_user_import": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_themes": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_public_link": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "ff_logging": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Тарифная опция"}, - "user_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10)}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *ValoTenantResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *ValoTenantModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Domain.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - if config.Domain.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 655: resources_core.FormatString(config.ResourceRealm), - 656: resources_core.FormatString(config.Domain), - 657: resources_core.BuildJSON(map[string]string{"requestApproveMaxPeriod": fmt.Sprintf("%d", config.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", config.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", config.ValoOptions.FfWatermark.ValueBool()), "ffUserImport": fmt.Sprintf("%v", config.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", config.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", config.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", config.ValoOptions.FfLogging.ValueBool()), "userCount": fmt.Sprintf("%d", config.ValoOptions.UserCount.ValueInt64())}), - 658: resources_core.FormatInt64(config.S3Storage), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 149, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *ValoTenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Domain.IsNull() || data.Domain.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "domain is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 655: resources_core.FormatString(data.ResourceRealm), - 656: resources_core.FormatString(data.Domain), - 657: resources_core.BuildJSON(map[string]string{"requestApproveMaxPeriod": fmt.Sprintf("%d", data.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", data.ValoOptions.FfOnlineEditor.ValueBool()), "ffWatermark": fmt.Sprintf("%v", data.ValoOptions.FfWatermark.ValueBool()), "ffUserImport": fmt.Sprintf("%v", data.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", data.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", data.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", data.ValoOptions.FfLogging.ValueBool()), "userCount": fmt.Sprintf("%d", data.ValoOptions.UserCount.ValueInt64())}), - 658: resources_core.FormatInt64(data.S3Storage), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 149, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 149, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ValoTenantResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 149, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *ValoTenantResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan ValoTenantModel - var state ValoTenantModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.S3Storage.IsNull() != state.S3Storage.IsNull() || plan.S3Storage.IsUnknown() != state.S3Storage.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.S3Storage.IsNull() && !plan.S3Storage.IsUnknown() && !state.S3Storage.IsNull() && !state.S3Storage.IsUnknown() { - if plan.S3Storage.ValueInt64() != state.S3Storage.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 659: resources_core.BuildJSON(map[string]string{"ffWatermark": fmt.Sprintf("%v", plan.ValoOptions.FfWatermark.ValueBool()), "ffUserImport": fmt.Sprintf("%v", plan.ValoOptions.FfUserImport.ValueBool()), "ffThemes": fmt.Sprintf("%v", plan.ValoOptions.FfThemes.ValueBool()), "ffPublicLink": fmt.Sprintf("%v", plan.ValoOptions.FfPublicLink.ValueBool()), "ffLogging": fmt.Sprintf("%v", plan.ValoOptions.FfLogging.ValueBool()), "userCount": fmt.Sprintf("%d", plan.ValoOptions.UserCount.ValueInt64()), "requestApproveMaxPeriod": fmt.Sprintf("%d", plan.ValoOptions.RequestApproveMaxPeriod.ValueInt64()), "ffOnlineEditor": fmt.Sprintf("%v", plan.ValoOptions.FfOnlineEditor.ValueBool())}), - 660: resources_core.FormatInt64(plan.S3Storage), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 149, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "s3Storage", Field: "S3Storage", Type: "int64"}, - {Code: "valoOptions", Field: "ValoOptions", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *ValoTenantResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state ValoTenantModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *ValoTenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *ValoTenantResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/150_k8s_sthutrval_cluster_resource.go b/generated/test/go/150_k8s_sthutrval_cluster_resource.go deleted file mode 100644 index d77d291..0000000 --- a/generated/test/go/150_k8s_sthutrval_cluster_resource.go +++ /dev/null @@ -1,429 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_sthutrval_cluster -// Service ID: 150 - -var _ resource.Resource = &K8sSthutrvalClusterResource{} -var _ resource.ResourceWithModifyPlan = &K8sSthutrvalClusterResource{} -var _ resource.ResourceWithImportState = &K8sSthutrvalClusterResource{} - -type K8sSthutrvalClusterResource struct { - client *core.UniversalClient -} - -// K8sSthutrvalClusterAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type K8sSthutrvalClusterAccessConfigurationModel struct { - NeedExternalAddressAPI types.Bool `tfsdk:"need_external_address_a_p_i" json:"needExternalAddressAPI"` - AccessIpListAPI types.String `tfsdk:"access_ip_list_a_p_i" json:"accessIpListAPI"` - NeedExternalAddressIngress types.Bool `tfsdk:"need_external_address_ingress" json:"needExternalAddressIngress"` - AccessIpListIngress types.String `tfsdk:"access_ip_list_ingress" json:"accessIpListIngress"` -} - -// K8sSthutrvalClusterClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sSthutrvalClusterClusterConfigurationModel struct { - AppVersion types.String `tfsdk:"app_version" json:"appVersion"` -} - -// K8sSthutrvalClusterControlPlaneConfigurationModel — вложенная модель для map-fixed параметра controlPlaneConfiguration. -type K8sSthutrvalClusterControlPlaneConfigurationModel struct { - SizingPolicy types.String `tfsdk:"sizing_policy" json:"sizingPolicy"` - SizingDisk types.Int64 `tfsdk:"sizing_disk" json:"sizingDisk"` - Count types.Int64 `tfsdk:"count" json:"count"` -} - -// K8sSthutrvalClusterStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sSthutrvalClusterStartupConfigurationModel struct { - NsxtUid types.String `tfsdk:"nsxt_uid" json:"nsxtUid"` - ClusterName types.String `tfsdk:"cluster_name" json:"clusterName"` - VdcUid types.String `tfsdk:"vdc_uid" json:"vdcUid"` -} - -type K8sSthutrvalClusterModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *K8sSthutrvalClusterAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *K8sSthutrvalClusterClusterConfigurationModel `tfsdk:"cluster_configuration"` - ControlPlaneConfiguration *K8sSthutrvalClusterControlPlaneConfigurationModel `tfsdk:"control_plane_configuration"` - StartupConfiguration *K8sSthutrvalClusterStartupConfigurationModel `tfsdk:"startup_configuration"` - WorkerConfiguration types.String `tfsdk:"worker_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sSthutrvalClusterResource() resource.Resource { - return &K8sSthutrvalClusterResource{} -} - -func (r *K8sSthutrvalClusterResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster" -} - -func (r *K8sSthutrvalClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "need_external_address_a_p_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false"}, - "access_ip_list_a_p_i": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, - "need_external_address_ingress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Выделяет внешний IP для доступа к Ingress"}, - "access_ip_list_ingress": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.13.1")}, - }}, - "control_plane_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "sizing_policy": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр`"}, - "sizing_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(50), MarkdownDescription: "Дисковая политика берётся из услуги VDC, первый в списке"}, - "count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "nsxt_uid": schema.StringAttribute{Required: true}, - "cluster_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("example-biu4ghxt")}, - "vdc_uid": schema.StringAttribute{Required: true}, - }}, - "worker_configuration": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]")}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sSthutrvalClusterResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 750: resources_core.BuildJSON(map[string]string{"nsxtUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.NsxtUid.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ClusterName.ValueString()), "vdcUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.VdcUid.ValueString())}), - 751: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", config.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", config.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", config.ControlPlaneConfiguration.Count.ValueInt64())}), - 752: resources_core.FormatString(config.WorkerConfiguration), - 753: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", config.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", config.AccessConfiguration.AccessIpListIngress.ValueString())}), - 755: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", config.ClusterConfiguration.AppVersion.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 150, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sSthutrvalClusterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 750: resources_core.BuildJSON(map[string]string{"nsxtUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.NsxtUid.ValueString()), "clusterName": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ClusterName.ValueString()), "vdcUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.VdcUid.ValueString())}), - 751: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", data.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", data.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", data.ControlPlaneConfiguration.Count.ValueInt64())}), - 752: resources_core.FormatString(data.WorkerConfiguration), - 753: resources_core.BuildJSON(map[string]string{"needExternalAddressAPI": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", data.AccessConfiguration.NeedExternalAddressIngress.ValueBool()), "accessIpListIngress": fmt.Sprintf("\"%s\"", data.AccessConfiguration.AccessIpListIngress.ValueString())}), - 755: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", data.ClusterConfiguration.AppVersion.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 150, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 150, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 150, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sSthutrvalClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sSthutrvalClusterModel - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.WorkerConfiguration.IsNull() != state.WorkerConfiguration.IsNull() || plan.WorkerConfiguration.IsUnknown() != state.WorkerConfiguration.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.WorkerConfiguration.IsNull() && !plan.WorkerConfiguration.IsUnknown() && !state.WorkerConfiguration.IsNull() && !state.WorkerConfiguration.IsUnknown() { - if !strings.EqualFold(plan.WorkerConfiguration.ValueString(), state.WorkerConfiguration.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 756: resources_core.BuildJSON(map[string]string{"appVersion": fmt.Sprintf("\"%s\"", plan.ClusterConfiguration.AppVersion.ValueString())}), - 757: resources_core.BuildJSON(map[string]string{"sizingPolicy": fmt.Sprintf("\"%s\"", plan.ControlPlaneConfiguration.SizingPolicy.ValueString()), "sizingDisk": fmt.Sprintf("%d", plan.ControlPlaneConfiguration.SizingDisk.ValueInt64()), "count": fmt.Sprintf("%d", plan.ControlPlaneConfiguration.Count.ValueInt64())}), - 758: resources_core.FormatString(plan.WorkerConfiguration), - 759: resources_core.BuildJSON(map[string]string{"accessIpListIngress": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.AccessIpListIngress.ValueString()), "needExternalAddressAPI": fmt.Sprintf("%v", plan.AccessConfiguration.NeedExternalAddressAPI.ValueBool()), "accessIpListAPI": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.AccessIpListAPI.ValueString()), "needExternalAddressIngress": fmt.Sprintf("%v", plan.AccessConfiguration.NeedExternalAddressIngress.ValueBool())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 150, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "controlPlaneConfiguration", Field: "ControlPlaneConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - {Code: "workerConfiguration", Field: "WorkerConfiguration", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sSthutrvalClusterModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sSthutrvalClusterResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sSthutrvalClusterResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/150_k8s_sthutrval_cluster_user_resource.go b/generated/test/go/150_k8s_sthutrval_cluster_user_resource.go deleted file mode 100644 index 55faf0e..0000000 --- a/generated/test/go/150_k8s_sthutrval_cluster_user_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_sthutrval_cluster -// Subresource: user - -var _ resource.Resource = &K8sSthutrvalClusterUserResource{} - -// K8sSthutrvalClusterUserResource manages a subresource via operations. -type K8sSthutrvalClusterUserResource struct { - client *core.UniversalClient -} - -type K8sSthutrvalClusterUserModel struct { - ID types.String `tfsdk:"id"` - K8sSthutrvalClusterID types.String `tfsdk:"k8s_sthutrval_cluster_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Admins types.String `tfsdk:"admins"` -} - -func NewK8sSthutrvalClusterUserResource() resource.Resource { - return &K8sSthutrvalClusterUserResource{} -} - -func (r *K8sSthutrvalClusterUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_sthutrval_cluster_user" -} - -func (r *K8sSthutrvalClusterUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_sthutrval_cluster_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "admins": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sSthutrvalClusterUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sSthutrvalClusterUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sSthutrvalClusterUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sSthutrvalClusterUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sSthutrvalClusterUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sSthutrvalClusterID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(state.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(state.Admins), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sSthutrvalClusterUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/151_k8s_openbao_kv_resource.go b/generated/test/go/151_k8s_openbao_kv_resource.go deleted file mode 100644 index df5550e..0000000 --- a/generated/test/go/151_k8s_openbao_kv_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: kv - -var _ resource.Resource = &K8sOpenbaoKvResource{} - -// K8sOpenbaoKvResource manages a subresource via operations. -type K8sOpenbaoKvResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoKvModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KvName types.String `tfsdk:"kv_name"` -} - -func NewK8sOpenbaoKvResource() resource.Resource { - return &K8sOpenbaoKvResource{} -} - -func (r *K8sOpenbaoKvResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_kv" -} - -func (r *K8sOpenbaoKvResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "kv_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("kvname-yyz2zm23"), MarkdownDescription: "Будет создан `v2 тип движка`

Уникальное в рамках услуги Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoKvResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoKvModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - } - identityCodes := []string{ - "kvName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("kv", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_kv", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_kv", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoKvResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoKvResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoKvModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "kv", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoKvResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoKvModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_kv" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - } - identityCodes := []string{ - "kvName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("kv", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_kv", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoKvResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/151_k8s_openbao_policy_resource.go b/generated/test/go/151_k8s_openbao_policy_resource.go deleted file mode 100644 index ef23e6d..0000000 --- a/generated/test/go/151_k8s_openbao_policy_resource.go +++ /dev/null @@ -1,355 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: policy - -var _ resource.Resource = &K8sOpenbaoPolicyResource{} - -// K8sOpenbaoPolicyResource manages a subresource via operations. -type K8sOpenbaoPolicyResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoPolicyModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - PolicyName types.String `tfsdk:"policy_name"` - PolicySettings types.String `tfsdk:"policy_settings"` -} - -func NewK8sOpenbaoPolicyResource() resource.Resource { - return &K8sOpenbaoPolicyResource{} -} - -func (r *K8sOpenbaoPolicyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_policy" -} - -func (r *K8sOpenbaoPolicyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "policy_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("policy-r2ksc6sl"), MarkdownDescription: "Сделано для дальнейшего удобного использования Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "policy_settings": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoPolicyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.PolicySettings.IsNull() || plan.PolicySettings.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "policy_settings is required.") - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "policyName": resources_core.FormatString(plan.PolicyName), - "policySettings": resources_core.FormatString(plan.PolicySettings), - } - identityCodes := []string{ - "policyName", - "policySettings", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("policy", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "policySettings": resources_core.FormatString(plan.PolicySettings), - "policyName": resources_core.FormatString(plan.PolicyName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_policy", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_policy", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoPolicyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoPolicyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "policyName": resources_core.FormatString(plan.PolicyName), - "policySettings": resources_core.FormatString(plan.PolicySettings), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "policy", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoPolicyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoPolicyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_policy" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "policyName": resources_core.FormatString(state.PolicyName), - "policySettings": resources_core.FormatString(state.PolicySettings), - } - identityCodes := []string{ - "policyName", - "policySettings", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("policy", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "policyName": resources_core.FormatString(state.PolicyName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_policy", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoPolicyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/151_k8s_openbao_resource.go b/generated/test/go/151_k8s_openbao_resource.go deleted file mode 100644 index 92dcaf2..0000000 --- a/generated/test/go/151_k8s_openbao_resource.go +++ /dev/null @@ -1,468 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Service ID: 151 - -var _ resource.Resource = &K8sOpenbaoResource{} -var _ resource.ResourceWithModifyPlan = &K8sOpenbaoResource{} -var _ resource.ResourceWithImportState = &K8sOpenbaoResource{} - -type K8sOpenbaoResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AppVersion types.String `tfsdk:"app_version"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sOpenbaoResource() resource.Resource { - return &K8sOpenbaoResource{} -} - -func (r *K8sOpenbaoResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao" -} - -func (r *K8sOpenbaoResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "app_version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2.5.2"), MarkdownDescription: "Выбрать из списка"}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("bao-uqvjbvor"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Пример: `500`"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Пример: `10`"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(3), MarkdownDescription: "Пример: `3`"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Пример: `2048`"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes в котором будет развёрнуто приложение Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sOpenbaoModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 663: resources_core.FormatString(config.ResourceRealm), - 664: resources_core.FormatInt64(config.ResourceCPU), - 665: resources_core.FormatInt64(config.ResourceInstances), - 666: resources_core.FormatInt64(config.ResourceMemory), - 667: resources_core.FormatInt64(config.ResourceDisk), - 668: resources_core.FormatString(config.AppVersion), - 669: resources_core.FormatString(config.Domain), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 151, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sOpenbaoResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 151, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 663: resources_core.FormatString(data.ResourceRealm), - 664: resources_core.FormatInt64(data.ResourceCPU), - 665: resources_core.FormatInt64(data.ResourceInstances), - 666: resources_core.FormatInt64(data.ResourceMemory), - 667: resources_core.FormatInt64(data.ResourceDisk), - 668: resources_core.FormatString(data.AppVersion), - 669: resources_core.FormatString(data.Domain), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 151, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 151, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 151, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sOpenbaoResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoModel - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AppVersion.IsNull() != state.AppVersion.IsNull() || plan.AppVersion.IsUnknown() != state.AppVersion.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AppVersion.IsNull() && !plan.AppVersion.IsUnknown() && !state.AppVersion.IsNull() && !state.AppVersion.IsUnknown() { - if !strings.EqualFold(plan.AppVersion.ValueString(), state.AppVersion.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 670: resources_core.FormatInt64(plan.ResourceInstances), - 671: resources_core.FormatInt64(plan.ResourceCPU), - 672: resources_core.FormatInt64(plan.ResourceMemory), - 673: resources_core.FormatInt64(plan.ResourceDisk), - 674: resources_core.FormatString(plan.AppVersion), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 151, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "appVersion", Field: "AppVersion", Type: "string"}, - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sOpenbaoResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sOpenbaoResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/151_k8s_openbao_secrets_resource.go b/generated/test/go/151_k8s_openbao_secrets_resource.go deleted file mode 100644 index 161164e..0000000 --- a/generated/test/go/151_k8s_openbao_secrets_resource.go +++ /dev/null @@ -1,368 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_openbao -// Subresource: secrets - -var _ resource.Resource = &K8sOpenbaoSecretsResource{} - -// K8sOpenbaoSecretsResource manages a subresource via operations. -type K8sOpenbaoSecretsResource struct { - client *core.UniversalClient -} - -type K8sOpenbaoSecretsModel struct { - ID types.String `tfsdk:"id"` - K8sOpenbaoID types.String `tfsdk:"k8s_openbao_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KvName types.String `tfsdk:"kv_name"` - SecretData types.String `tfsdk:"secret_data"` - SecretPath types.String `tfsdk:"secret_path"` -} - -func NewK8sOpenbaoSecretsResource() resource.Resource { - return &K8sOpenbaoSecretsResource{} -} - -func (r *K8sOpenbaoSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_openbao_secrets" -} - -func (r *K8sOpenbaoSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "k8s_openbao_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "kv_name": schema.StringAttribute{Required: true, MarkdownDescription: "Выберите из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "secret_data": schema.StringAttribute{Required: true, MarkdownDescription: "Хотя бы один секрет необходимо добавить Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "secret_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("path/to/mysecret"), MarkdownDescription: "Пример: `path` или `road/to/path` Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sOpenbaoSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.KvName.IsNull() || plan.KvName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "kv_name is required.") - return - } - if plan.SecretData.IsNull() || plan.SecretData.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "secret_data is required.") - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretData": resources_core.FormatString(plan.SecretData), - "secretPath": resources_core.FormatString(plan.SecretPath), - } - identityCodes := []string{ - "kvName", - "secretData", - "secretPath", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretPath": resources_core.FormatString(plan.SecretPath), - "secretData": resources_core.FormatString(plan.SecretData), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sOpenbaoSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "kvName": resources_core.FormatString(plan.KvName), - "secretData": resources_core.FormatString(plan.SecretData), - "secretPath": resources_core.FormatString(plan.SecretPath), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *K8sOpenbaoSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sOpenbaoSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_secrets" == "" { - return - } - - instanceUID := strings.TrimSpace(state.K8sOpenbaoID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - "secretData": resources_core.FormatString(state.SecretData), - "secretPath": resources_core.FormatString(state.SecretPath), - } - identityCodes := []string{ - "kvName", - "secretData", - "secretPath", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "kvName": resources_core.FormatString(state.KvName), - "secretPath": resources_core.FormatString(state.SecretPath), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_secrets", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *K8sOpenbaoSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/163_llm_ai_resource.go b/generated/test/go/163_llm_ai_resource.go deleted file mode 100644 index 1037328..0000000 --- a/generated/test/go/163_llm_ai_resource.go +++ /dev/null @@ -1,345 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Service ID: 163 - -var _ resource.Resource = &LlmAiResource{} -var _ resource.ResourceWithModifyPlan = &LlmAiResource{} -var _ resource.ResourceWithImportState = &LlmAiResource{} - -type LlmAiResource struct { - client *core.UniversalClient -} - -type LlmAiModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewLlmAiResource() resource.Resource { - return &LlmAiResource{} -} - -func (r *LlmAiResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai" -} - -func (r *LlmAiResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("llmAi.aillm.ru"), MarkdownDescription: "Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *LlmAiModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 826: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 163, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *LlmAiResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 163, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 826: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 163, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 163, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 163, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *LlmAiResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiModel - var state LlmAiModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 163, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *LlmAiResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *LlmAiResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/163_llm_ai_secrets_resource.go b/generated/test/go/163_llm_ai_secrets_resource.go deleted file mode 100644 index 063f1fb..0000000 --- a/generated/test/go/163_llm_ai_secrets_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Subresource: secrets - -var _ resource.Resource = &LlmAiSecretsResource{} - -// LlmAiSecretsResource manages a subresource via operations. -type LlmAiSecretsResource struct { - client *core.UniversalClient -} - -type LlmAiSecretsModel struct { - ID types.String `tfsdk:"id"` - LlmAiID types.String `tfsdk:"llm_ai_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - KeyName types.String `tfsdk:"key_name"` - Models types.String `tfsdk:"models"` - RpmLimit types.Int64 `tfsdk:"rpm_limit"` - TeamName types.String `tfsdk:"team_name"` - TpmLimit types.Int64 `tfsdk:"tpm_limit"` -} - -func NewLlmAiSecretsResource() resource.Resource { - return &LlmAiSecretsResource{} -} - -func (r *LlmAiSecretsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai_secrets" -} - -func (r *LlmAiSecretsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "llm_ai_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "key_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "models": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "rpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(60), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()}}, - "team_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "tpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100000), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiSecretsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LlmAiSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.KeyName.IsNull() || plan.KeyName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "key_name is required.") - return - } - if plan.Models.IsNull() || plan.Models.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "models is required.") - return - } - if plan.TeamName.IsNull() || plan.TeamName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "team_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "keyName": resources_core.FormatString(plan.KeyName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "teamName": resources_core.FormatString(plan.TeamName), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - } - identityCodes := []string{ - "keyName", - "models", - "rpmLimit", - "teamName", - "tpmLimit", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - "keyName": resources_core.FormatString(plan.KeyName), - "teamName": resources_core.FormatString(plan.TeamName), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_secrets", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiSecretsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiSecretsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiSecretsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "keyName": resources_core.FormatString(plan.KeyName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "teamName": resources_core.FormatString(plan.TeamName), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "secrets", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiSecretsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiSecretsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_secrets" == "" { - return - } - - instanceUID := strings.TrimSpace(state.LlmAiID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "keyName": resources_core.FormatString(state.KeyName), - "models": resources_core.FormatString(state.Models), - "rpmLimit": resources_core.FormatInt64(state.RpmLimit), - "teamName": resources_core.FormatString(state.TeamName), - "tpmLimit": resources_core.FormatInt64(state.TpmLimit), - } - identityCodes := []string{ - "keyName", - "models", - "rpmLimit", - "teamName", - "tpmLimit", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("secrets", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "keyName": resources_core.FormatString(state.KeyName), - "teamName": resources_core.FormatString(state.TeamName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_secrets", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *LlmAiSecretsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/163_llm_ai_team_resource.go b/generated/test/go/163_llm_ai_team_resource.go deleted file mode 100644 index e411362..0000000 --- a/generated/test/go/163_llm_ai_team_resource.go +++ /dev/null @@ -1,365 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: llm_ai -// Subresource: team - -var _ resource.Resource = &LlmAiTeamResource{} - -// LlmAiTeamResource manages a subresource via operations. -type LlmAiTeamResource struct { - client *core.UniversalClient -} - -type LlmAiTeamModel struct { - ID types.String `tfsdk:"id"` - LlmAiID types.String `tfsdk:"llm_ai_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Models types.String `tfsdk:"models"` - RpmLimit types.Int64 `tfsdk:"rpm_limit"` - TeamName types.String `tfsdk:"team_name"` - TpmLimit types.Int64 `tfsdk:"tpm_limit"` -} - -func NewLlmAiTeamResource() resource.Resource { - return &LlmAiTeamResource{} -} - -func (r *LlmAiTeamResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_llm_ai_team" -} - -func (r *LlmAiTeamResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "llm_ai_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "models": schema.StringAttribute{Required: true}, - "rpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(60)}, - "team_name": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "tpm_limit": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100000)}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LlmAiTeamResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan LlmAiTeamModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Models.IsNull() || plan.Models.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "models is required.") - return - } - if plan.TeamName.IsNull() || plan.TeamName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "team_name is required.") - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - } - identityCodes := []string{ - "teamName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("team", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_team", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_team", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiTeamResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LlmAiTeamResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LlmAiTeamModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_team" == "" { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.LlmAiID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - "models": resources_core.FormatString(plan.Models), - "rpmLimit": resources_core.FormatInt64(plan.RpmLimit), - "tpmLimit": resources_core.FormatInt64(plan.TpmLimit), - }) - - idParams := map[string]string{ - "teamName": resources_core.FormatString(plan.TeamName), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_team", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "team", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *LlmAiTeamResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LlmAiTeamModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_team" == "" { - return - } - - instanceUID := strings.TrimSpace(state.LlmAiID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "teamName": resources_core.FormatString(state.TeamName), - } - identityCodes := []string{ - "teamName", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("team", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "teamName": resources_core.FormatString(state.TeamName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_team", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *LlmAiTeamResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/19_vc_org_resource.go b/generated/test/go/19_vc_org_resource.go deleted file mode 100644 index 90d238a..0000000 --- a/generated/test/go/19_vc_org_resource.go +++ /dev/null @@ -1,391 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org -// Service ID: 19 - -var _ resource.Resource = &VcOrgResource{} -var _ resource.ResourceWithModifyPlan = &VcOrgResource{} -var _ resource.ResourceWithImportState = &VcOrgResource{} - -type VcOrgResource struct { - client *core.UniversalClient -} - -type VcOrgModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - OrganizationType types.String `tfsdk:"organization_type"` - ResourceRealm types.String `tfsdk:"resource_realm"` - VIPConfigure types.String `tfsdk:"v_i_p_configure"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcOrgResource() resource.Resource { - return &VcOrgResource{} -} - -func (r *VcOrgResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org" -} - -func (r *VcOrgResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "organization_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("iaas"), MarkdownDescription: "Доступ к организации предоставляется только при выборе `iaas` Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Облако в котором будет развёрнут тенант Не изменяется после создания."}, - "v_i_p_configure": schema.StringAttribute{Required: true}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcOrgModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.OrganizationType.IsNull() && !plan.OrganizationType.IsUnknown() && !state.OrganizationType.IsNull() && !state.OrganizationType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.OrganizationType.ValueString(), state.OrganizationType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить organization_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VIPConfigure.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") - return - } - if config.VIPConfigure.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 418: resources_core.FormatString(config.ResourceRealm), - 556: resources_core.FormatString(config.OrganizationType), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 19, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcOrgResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VIPConfigure.IsNull() || data.VIPConfigure.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "v_i_p_configure is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 418: resources_core.FormatString(data.ResourceRealm), - 556: resources_core.FormatString(data.OrganizationType), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 19, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 19, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 19, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcOrgResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgModel - var state VcOrgModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VIPConfigure.IsNull() != state.VIPConfigure.IsNull() || plan.VIPConfigure.IsUnknown() != state.VIPConfigure.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VIPConfigure.IsNull() && !plan.VIPConfigure.IsUnknown() && !state.VIPConfigure.IsNull() && !state.VIPConfigure.IsUnknown() { - if !strings.EqualFold(plan.VIPConfigure.ValueString(), state.VIPConfigure.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 662: resources_core.FormatString(plan.VIPConfigure), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 19, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "organizationType", Field: "OrganizationType", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "vIPConfigure", Field: "VIPConfigure", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcOrgResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcOrgResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/19_vc_org_user_resource.go b/generated/test/go/19_vc_org_user_resource.go deleted file mode 100644 index b18fcdd..0000000 --- a/generated/test/go/19_vc_org_user_resource.go +++ /dev/null @@ -1,343 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org -// Subresource: user - -var _ resource.Resource = &VcOrgUserResource{} - -// VcOrgUserResource manages a subresource via operations. -type VcOrgUserResource struct { - client *core.UniversalClient -} - -type VcOrgUserModel struct { - ID types.String `tfsdk:"id"` - VcOrgID types.String `tfsdk:"vc_org_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Admins types.String `tfsdk:"admins"` -} - -func NewVcOrgUserResource() resource.Resource { - return &VcOrgUserResource{} -} - -func (r *VcOrgUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org_user" -} - -func (r *VcOrgUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "vc_org_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "admins": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcOrgUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcOrgUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.VcOrgID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "admins": resources_core.FormatString(plan.Admins), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcOrgUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.VcOrgID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "admins": resources_core.FormatString(state.Admins), - } - identityCodes := []string{ - "admins", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "admins": resources_core.FormatString(state.Admins), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *VcOrgUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/1_dummy_resource.go b/generated/test/go/1_dummy_resource.go deleted file mode 100644 index a00d9cc..0000000 --- a/generated/test/go/1_dummy_resource.go +++ /dev/null @@ -1,582 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: dummy -// Service ID: 1 - -var _ resource.Resource = &DummyResource{} -var _ resource.ResourceWithModifyPlan = &DummyResource{} -var _ resource.ResourceWithImportState = &DummyResource{} - -type DummyResource struct { - client *core.UniversalClient -} - -// DummyMapExampleModel — вложенная модель для map-fixed параметра mapExample. -type DummyMapExampleModel struct { - Subparam2 types.String `tfsdk:"subparam2" json:"subparam2"` - Subparam1 types.String `tfsdk:"subparam1" json:"subparam1"` - Secret types.String `tfsdk:"secret" json:"secret"` -} - -// DummyMapFixedModel — вложенная модель для map-fixed параметра mapFixed. -type DummyMapFixedModel struct { - Bol1 types.Bool `tfsdk:"bol1" json:"bol1"` - MinStr1 types.String `tfsdk:"min_str1" json:"minStr1"` - Param1 types.Int64 `tfsdk:"param1" json:"param1"` - Param2 types.String `tfsdk:"param2" json:"param2"` - Param3 types.Int64 `tfsdk:"param3" json:"param3"` -} - -type DummyModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Arr types.String `tfsdk:"arr"` - ArrayMapFixedExample types.String `tfsdk:"array_map_fixed_example"` - Bodymessage types.String `tfsdk:"bodymessage"` - DurationMs types.Int64 `tfsdk:"duration_ms"` - FailAtStart types.Bool `tfsdk:"fail_at_start"` - FailInProgress types.Bool `tfsdk:"fail_in_progress"` - JsonExample types.String `tfsdk:"json_example"` - MapExample *DummyMapExampleModel `tfsdk:"map_example"` - MapFixed *DummyMapFixedModel `tfsdk:"map_fixed"` - NestedRefExample types.String `tfsdk:"nested_ref_example"` - ResourceRealm types.String `tfsdk:"resource_realm"` - WhereFail types.Int64 `tfsdk:"where_fail"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewDummyResource() resource.Resource { - return &DummyResource{} -} - -func (r *DummyResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_dummy" -} - -func (r *DummyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "arr": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "array_map_fixed_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Array of map-fixed Не изменяется после создания."}, - "bodymessage": schema.StringAttribute{Optional: true, MarkdownDescription: "Дата, уходящая в Vault"}, - "duration_ms": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "перед завершением подождать (sleep) столько миллисекунд"}, - "fail_at_start": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, завершить джоб, ничего не записывая в cmdb"}, - "fail_in_progress": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb"}, - "json_example": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для тестирования интерфейса", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "map_example": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "subparam2": schema.StringAttribute{Optional: true}, - "subparam1": schema.StringAttribute{Optional: true, MarkdownDescription: "это для администратора"}, - "secret": schema.StringAttribute{Optional: true, MarkdownDescription: "это секрет"}, - }}, - "map_fixed": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "bol1": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Булевский, обязательный"}, - "min_str1": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abracadabra"), MarkdownDescription: "не меньше 10 символов"}, - "param1": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Integer, secret"}, - "param2": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("1"), MarkdownDescription: "Дескрипшен"}, - "param3": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "целое положительное"}, - }}, - "nested_ref_example": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("dummy"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "where_fail": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write"}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *DummyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *DummyModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Arr.IsNull() && !plan.Arr.IsUnknown() && !state.Arr.IsNull() && !state.Arr.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Arr.ValueString(), state.Arr.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить arr", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ArrayMapFixedExample.IsNull() && !plan.ArrayMapFixedExample.IsUnknown() && !state.ArrayMapFixedExample.IsNull() && !state.ArrayMapFixedExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ArrayMapFixedExample.ValueString(), state.ArrayMapFixedExample.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить array_map_fixed_example", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NestedRefExample.IsNull() && !plan.NestedRefExample.IsUnknown() && !state.NestedRefExample.IsNull() && !state.NestedRefExample.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NestedRefExample.ValueString(), state.NestedRefExample.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить nested_ref_example", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Arr.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - if config.Arr.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 198: resources_core.FormatInt64(config.DurationMs), - 199: resources_core.FormatBool(config.FailAtStart), - 200: resources_core.FormatBool(config.FailInProgress), - 201: resources_core.FormatInt64(config.WhereFail), - 242: resources_core.FormatString(config.ResourceRealm), - 286: resources_core.FormatString(config.Bodymessage), - 321: resources_core.BuildJSON(map[string]string{"subparam2": fmt.Sprintf("\"%s\"", config.MapExample.Subparam2.ValueString()), "subparam1": fmt.Sprintf("\"%s\"", config.MapExample.Subparam1.ValueString()), "secret": fmt.Sprintf("\"%s\"", config.MapExample.Secret.ValueString())}), - 322: resources_core.FormatString(config.JsonExample), - 396: resources_core.FormatString(config.NestedRefExample), - 647: resources_core.BuildJSON(map[string]string{"bol1": fmt.Sprintf("%v", config.MapFixed.Bol1.ValueBool()), "minStr1": fmt.Sprintf("\"%s\"", config.MapFixed.MinStr1.ValueString()), "param1": fmt.Sprintf("%d", config.MapFixed.Param1.ValueInt64()), "param2": fmt.Sprintf("\"%s\"", config.MapFixed.Param2.ValueString()), "param3": fmt.Sprintf("%d", config.MapFixed.Param3.ValueInt64())}), - 654: resources_core.FormatString(config.ArrayMapFixedExample), - 863: resources_core.FormatString(config.Arr), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 1, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *DummyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Arr.IsNull() || data.Arr.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "arr is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 198: resources_core.FormatInt64(data.DurationMs), - 199: resources_core.FormatBool(data.FailAtStart), - 200: resources_core.FormatBool(data.FailInProgress), - 201: resources_core.FormatInt64(data.WhereFail), - 242: resources_core.FormatString(data.ResourceRealm), - 286: resources_core.FormatString(data.Bodymessage), - 321: resources_core.BuildJSON(map[string]string{"subparam2": fmt.Sprintf("\"%s\"", data.MapExample.Subparam2.ValueString()), "subparam1": fmt.Sprintf("\"%s\"", data.MapExample.Subparam1.ValueString()), "secret": fmt.Sprintf("\"%s\"", data.MapExample.Secret.ValueString())}), - 322: resources_core.FormatString(data.JsonExample), - 396: resources_core.FormatString(data.NestedRefExample), - 647: resources_core.BuildJSON(map[string]string{"bol1": fmt.Sprintf("%v", data.MapFixed.Bol1.ValueBool()), "minStr1": fmt.Sprintf("\"%s\"", data.MapFixed.MinStr1.ValueString()), "param1": fmt.Sprintf("%d", data.MapFixed.Param1.ValueInt64()), "param2": fmt.Sprintf("\"%s\"", data.MapFixed.Param2.ValueString()), "param3": fmt.Sprintf("%d", data.MapFixed.Param3.ValueInt64())}), - 654: resources_core.FormatString(data.ArrayMapFixedExample), - 863: resources_core.FormatString(data.Arr), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 1, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 1, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DummyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 1, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *DummyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan DummyModel - var state DummyModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.DurationMs.IsNull() != state.DurationMs.IsNull() || plan.DurationMs.IsUnknown() != state.DurationMs.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.DurationMs.IsNull() && !plan.DurationMs.IsUnknown() && !state.DurationMs.IsNull() && !state.DurationMs.IsUnknown() { - if plan.DurationMs.ValueInt64() != state.DurationMs.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.FailAtStart.IsNull() != state.FailAtStart.IsNull() || plan.FailAtStart.IsUnknown() != state.FailAtStart.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.FailAtStart.IsNull() && !plan.FailAtStart.IsUnknown() && !state.FailAtStart.IsNull() && !state.FailAtStart.IsUnknown() { - if plan.FailAtStart.ValueBool() != state.FailAtStart.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.FailInProgress.IsNull() != state.FailInProgress.IsNull() || plan.FailInProgress.IsUnknown() != state.FailInProgress.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.FailInProgress.IsNull() && !plan.FailInProgress.IsUnknown() && !state.FailInProgress.IsNull() && !state.FailInProgress.IsUnknown() { - if plan.FailInProgress.ValueBool() != state.FailInProgress.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.WhereFail.IsNull() != state.WhereFail.IsNull() || plan.WhereFail.IsUnknown() != state.WhereFail.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.WhereFail.IsNull() && !plan.WhereFail.IsUnknown() && !state.WhereFail.IsNull() && !state.WhereFail.IsUnknown() { - if plan.WhereFail.ValueInt64() != state.WhereFail.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.Bodymessage.IsNull() != state.Bodymessage.IsNull() || plan.Bodymessage.IsUnknown() != state.Bodymessage.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.Bodymessage.IsNull() && !plan.Bodymessage.IsUnknown() && !state.Bodymessage.IsNull() && !state.Bodymessage.IsUnknown() { - if !strings.EqualFold(plan.Bodymessage.ValueString(), state.Bodymessage.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.JsonExample.IsNull() != state.JsonExample.IsNull() || plan.JsonExample.IsUnknown() != state.JsonExample.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonExample.IsNull() && !plan.JsonExample.IsUnknown() && !state.JsonExample.IsNull() && !state.JsonExample.IsUnknown() { - if !strings.EqualFold(plan.JsonExample.ValueString(), state.JsonExample.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 287: resources_core.FormatInt64(plan.DurationMs), - 288: resources_core.FormatBool(plan.FailAtStart), - 289: resources_core.FormatBool(plan.FailInProgress), - 290: resources_core.FormatInt64(plan.WhereFail), - 291: resources_core.FormatString(plan.Bodymessage), - 373: resources_core.BuildJSON(map[string]string{"secret": fmt.Sprintf("\"%s\"", plan.MapExample.Secret.ValueString())}), - 374: resources_core.FormatString(plan.JsonExample), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 1, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "arr", Field: "Arr", Type: "string"}, - {Code: "arrayMapFixedExample", Field: "ArrayMapFixedExample", Type: "array-map-fixed"}, - {Code: "bodymessage", Field: "Bodymessage", Type: "string"}, - {Code: "durationMs", Field: "DurationMs", Type: "int64"}, - {Code: "failAtStart", Field: "FailAtStart", Type: "bool"}, - {Code: "failInProgress", Field: "FailInProgress", Type: "bool"}, - {Code: "jsonExample", Field: "JsonExample", Type: "string"}, - {Code: "mapExample", Field: "MapExample", Type: "string"}, - {Code: "mapFixed", Field: "MapFixed", Type: "map-fixed"}, - {Code: "nestedRefExample", Field: "NestedRefExample", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "whereFail", Field: "WhereFail", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *DummyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state DummyModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *DummyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *DummyResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/20_vc_org_saas_resource.go b/generated/test/go/20_vc_org_saas_resource.go deleted file mode 100644 index c1277b1..0000000 --- a/generated/test/go/20_vc_org_saas_resource.go +++ /dev/null @@ -1,348 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_org_saas -// Service ID: 20 - -var _ resource.Resource = &VcOrgSaasResource{} -var _ resource.ResourceWithModifyPlan = &VcOrgSaasResource{} -var _ resource.ResourceWithImportState = &VcOrgSaasResource{} - -type VcOrgSaasResource struct { - client *core.UniversalClient -} - -type VcOrgSaasModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcOrgSaasResource() resource.Resource { - return &VcOrgSaasResource{} -} - -func (r *VcOrgSaasResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_org_saas" -} - -func (r *VcOrgSaasResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcOrgSaasResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcOrgSaasModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 185: resources_core.FormatString(config.ResourceRealm), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 20, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcOrgSaasResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 185: resources_core.FormatString(data.ResourceRealm), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 20, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 20, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgSaasResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 20, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcOrgSaasResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcOrgSaasModel - var state VcOrgSaasModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 20, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcOrgSaasResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcOrgSaasModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcOrgSaasResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcOrgSaasResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/21_vc_vdc_resource.go b/generated/test/go/21_vc_vdc_resource.go deleted file mode 100644 index 0c32692..0000000 --- a/generated/test/go/21_vc_vdc_resource.go +++ /dev/null @@ -1,573 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc -// Service ID: 21 - -var _ resource.Resource = &VcVdcResource{} -var _ resource.ResourceWithModifyPlan = &VcVdcResource{} -var _ resource.ResourceWithImportState = &VcVdcResource{} - -type VcVdcResource struct { - client *core.UniversalClient -} - -type VcVdcModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - CpuAllocated types.Int64 `tfsdk:"cpu_allocated"` - CpuGuaranteed types.Int64 `tfsdk:"cpu_guaranteed"` - MemAllocated types.Int64 `tfsdk:"mem_allocated"` - NetworkProvider types.String `tfsdk:"network_provider"` - OrganizationUid types.String `tfsdk:"organization_uid"` - ProviderVdc types.String `tfsdk:"provider_vdc"` - StorageConfig types.String `tfsdk:"storage_config"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVdcResource() resource.Resource { - return &VcVdcResource{} -} - -func (r *VcVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc" -} - -func (r *VcVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "cpu_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(80), MarkdownDescription: "шт"}, - "cpu_guaranteed": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(20), MarkdownDescription: "% резервирования Не изменяется после создания."}, - "mem_allocated": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "gb"}, - "network_provider": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "organization_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` Не изменяется после создания."}, - "provider_vdc": schema.StringAttribute{Required: true, MarkdownDescription: "Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
Не изменяется после создания."}, - "storage_config": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Первая политика в списке указывается как "По умолчанию""}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVdcModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CpuGuaranteed.IsNull() && !plan.CpuGuaranteed.IsUnknown() && !state.CpuGuaranteed.IsNull() && !state.CpuGuaranteed.IsUnknown() { - if plan.CpuGuaranteed.ValueInt64() != state.CpuGuaranteed.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить cpu_guaranteed", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.NetworkProvider.IsNull() && !plan.NetworkProvider.IsUnknown() && !state.NetworkProvider.IsNull() && !state.NetworkProvider.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NetworkProvider.ValueString(), state.NetworkProvider.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить network_provider", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.OrganizationUid.ValueString(), state.OrganizationUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить organization_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ProviderVdc.IsNull() && !plan.ProviderVdc.IsUnknown() && !state.ProviderVdc.IsNull() && !state.ProviderVdc.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ProviderVdc.ValueString(), state.ProviderVdc.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить provider_vdc", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.NetworkProvider.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") - return - } - if config.NetworkProvider.IsUnknown() { - return - } - if config.OrganizationUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") - return - } - if config.OrganizationUid.IsUnknown() { - return - } - if config.ProviderVdc.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") - return - } - if config.ProviderVdc.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.OrganizationUid.IsNull() && !config.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, config.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != config.OrganizationUid.ValueString() { - config.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - params := map[int]string{ - 30: resources_core.FormatString(config.OrganizationUid), - 335: resources_core.FormatString(config.ProviderVdc), - 361: resources_core.FormatString(config.StorageConfig), - 397: resources_core.FormatInt64(config.CpuGuaranteed), - 557: resources_core.FormatInt64(config.CpuAllocated), - 558: resources_core.FormatInt64(config.MemAllocated), - 746: resources_core.FormatString(config.NetworkProvider), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 21, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.NetworkProvider.IsNull() || data.NetworkProvider.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "network_provider is required.") - return - } - if data.OrganizationUid.IsNull() || data.OrganizationUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "organization_uid is required.") - return - } - if data.ProviderVdc.IsNull() || data.ProviderVdc.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "provider_vdc is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalOrganizationUid := data.OrganizationUid - if !data.OrganizationUid.IsNull() && !data.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, data.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedOrganizationUid != "" && resolvedOrganizationUid != data.OrganizationUid.ValueString() { - data.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 30: resources_core.FormatString(data.OrganizationUid), - 335: resources_core.FormatString(data.ProviderVdc), - 361: resources_core.FormatString(data.StorageConfig), - 397: resources_core.FormatInt64(data.CpuGuaranteed), - 557: resources_core.FormatInt64(data.CpuAllocated), - 558: resources_core.FormatInt64(data.MemAllocated), - 746: resources_core.FormatString(data.NetworkProvider), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 21, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 21, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { - state.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalOrganizationUid.IsNull() && !originalOrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - if strings.EqualFold(state.OrganizationUid.ValueString(), originalOrganizationUid.ValueString()) { - state.OrganizationUid = originalOrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 21, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, newState.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != newState.OrganizationUid.ValueString() { - newState.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() && !newState.OrganizationUid.IsNull() && !newState.OrganizationUid.IsUnknown() { - if strings.EqualFold(newState.OrganizationUid.ValueString(), state.OrganizationUid.ValueString()) { - newState.OrganizationUid = state.OrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcModel - var state VcVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.CpuAllocated.IsNull() != state.CpuAllocated.IsNull() || plan.CpuAllocated.IsUnknown() != state.CpuAllocated.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.CpuAllocated.IsNull() && !plan.CpuAllocated.IsUnknown() && !state.CpuAllocated.IsNull() && !state.CpuAllocated.IsUnknown() { - if plan.CpuAllocated.ValueInt64() != state.CpuAllocated.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.MemAllocated.IsNull() != state.MemAllocated.IsNull() || plan.MemAllocated.IsUnknown() != state.MemAllocated.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.MemAllocated.IsNull() && !plan.MemAllocated.IsUnknown() && !state.MemAllocated.IsNull() && !state.MemAllocated.IsUnknown() { - if plan.MemAllocated.ValueInt64() != state.MemAllocated.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.StorageConfig.IsNull() != state.StorageConfig.IsNull() || plan.StorageConfig.IsUnknown() != state.StorageConfig.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.StorageConfig.IsNull() && !plan.StorageConfig.IsUnknown() && !state.StorageConfig.IsNull() && !state.StorageConfig.IsUnknown() { - if !strings.EqualFold(plan.StorageConfig.ValueString(), state.StorageConfig.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 560: resources_core.FormatInt64(plan.CpuAllocated), - 561: resources_core.FormatInt64(plan.MemAllocated), - 562: resources_core.FormatString(plan.StorageConfig), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 21, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "cpuAllocated", Field: "CpuAllocated", Type: "int64"}, - {Code: "cpuGuaranteed", Field: "CpuGuaranteed", Type: "int64"}, - {Code: "memAllocated", Field: "MemAllocated", Type: "int64"}, - {Code: "networkProvider", Field: "NetworkProvider", Type: "string"}, - {Code: "organizationUid", Field: "OrganizationUid", Type: "string"}, - {Code: "providerVdc", Field: "ProviderVdc", Type: "string"}, - {Code: "storageConfig", Field: "StorageConfig", Type: "array-map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - resolvedOrganizationUid, err := r.client.ResolveRefSvcParamValue(ctx, 19, state.OrganizationUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve organization_uid", err.Error()) - } else if resolvedOrganizationUid != "" && resolvedOrganizationUid != state.OrganizationUid.ValueString() { - state.OrganizationUid = types.StringValue(resolvedOrganizationUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.OrganizationUid.IsNull() && !plan.OrganizationUid.IsUnknown() && !state.OrganizationUid.IsNull() && !state.OrganizationUid.IsUnknown() { - if strings.EqualFold(state.OrganizationUid.ValueString(), plan.OrganizationUid.ValueString()) { - state.OrganizationUid = plan.OrganizationUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVdcResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/22_vc_nsxt_resource.go b/generated/test/go/22_vc_nsxt_resource.go deleted file mode 100644 index ca0acdb..0000000 --- a/generated/test/go/22_vc_nsxt_resource.go +++ /dev/null @@ -1,614 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_nsxt -// Service ID: 22 - -var _ resource.Resource = &VcNsxtResource{} -var _ resource.ResourceWithModifyPlan = &VcNsxtResource{} -var _ resource.ResourceWithImportState = &VcNsxtResource{} - -type VcNsxtResource struct { - client *core.UniversalClient -} - -type VcNsxtModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - NeedEnableAVI types.Bool `tfsdk:"need_enable_a_v_i"` - QosProfile types.String `tfsdk:"qos_profile"` - VdcGroupUid types.String `tfsdk:"vdc_group_uid"` - VdcType types.String `tfsdk:"vdc_type"` - VdcUid types.String `tfsdk:"vdc_uid"` - VirtualServicesCount types.Int64 `tfsdk:"virtual_services_count"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcNsxtResource() resource.Resource { - return &VcNsxtResource{} -} - -func (r *VcNsxtResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_nsxt" -} - -func (r *VcNsxtResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Имя ip Space для внешнего IP"}, - "need_enable_a_v_i": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включение Load Balancer"}, - "qos_profile": schema.StringAttribute{Optional: true, MarkdownDescription: "Параметр пока не работает
Должен возвращать поле из ресурсной платформы типа vc из .vcd.hardware.gatewayQoSProfiles

Как по идее должен работать.
Нужно заполнить или CFS vdcUid, или vdcGroupUid
Идеально конечно проверять `if (params.vdcType == "vdc" && params.vdcUid != "")` или `if (params.vdcType == "vdcGroup" && params.vdcGroupUid != "")`

Далее надо пойти по стейту vdc -> org -> resPlatform, взять gatewayQoSProfiles
Или пойти по стейту vdcgroup -> vdc -> org -> resPlatform, взять gatewayQoSProfiles

Регулярку могу написать (Виталя)"}, - "vdc_group_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Группа виртуальных датацентров (groupvDC)` Не изменяется после создания."}, - "vdc_type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vdc"), MarkdownDescription: "Тип родительской услуги Не изменяется после создания."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, - "virtual_services_count": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Кол-во виртуальных сервисов, которые **резервируются** на AVI"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcNsxtResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcNsxtModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcGroupUid.ValueString(), state.VdcGroupUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_group_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcType.IsNull() && !plan.VdcType.IsUnknown() && !state.VdcType.IsNull() && !state.VdcType.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcType.ValueString(), state.VdcType.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_type", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VdcGroupUid.IsNull() && !config.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, config.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != config.VdcGroupUid.ValueString() { - config.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 8: resources_core.FormatString(config.VdcUid), - 340: resources_core.FormatBool(config.NeedEnableAVI), - 341: resources_core.FormatInt64(config.VirtualServicesCount), - 621: resources_core.FormatString(config.VdcType), - 622: resources_core.FormatString(config.VdcGroupUid), - 825: resources_core.FormatString(config.QosProfile), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 22, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcNsxtResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcGroupUid := data.VdcGroupUid - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.VdcGroupUid.IsNull() && !data.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, data.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != data.VdcGroupUid.ValueString() { - data.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 8: resources_core.FormatString(data.VdcUid), - 340: resources_core.FormatBool(data.NeedEnableAVI), - 341: resources_core.FormatInt64(data.VirtualServicesCount), - 621: resources_core.FormatString(data.VdcType), - 622: resources_core.FormatString(data.VdcGroupUid), - 825: resources_core.FormatString(data.QosProfile), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 22, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 22, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { - state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVdcGroupUid.IsNull() && !originalVdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - if strings.EqualFold(state.VdcGroupUid.ValueString(), originalVdcGroupUid.ValueString()) { - state.VdcGroupUid = originalVdcGroupUid - } - } - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNsxtResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 22, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, newState.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != newState.VdcGroupUid.ValueString() { - newState.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() && !newState.VdcGroupUid.IsNull() && !newState.VdcGroupUid.IsUnknown() { - if strings.EqualFold(newState.VdcGroupUid.ValueString(), state.VdcGroupUid.ValueString()) { - newState.VdcGroupUid = state.VdcGroupUid - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcNsxtResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcNsxtModel - var state VcNsxtModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.NeedEnableAVI.IsNull() != state.NeedEnableAVI.IsNull() || plan.NeedEnableAVI.IsUnknown() != state.NeedEnableAVI.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.NeedEnableAVI.IsNull() && !plan.NeedEnableAVI.IsUnknown() && !state.NeedEnableAVI.IsNull() && !state.NeedEnableAVI.IsUnknown() { - if plan.NeedEnableAVI.ValueBool() != state.NeedEnableAVI.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VirtualServicesCount.IsNull() != state.VirtualServicesCount.IsNull() || plan.VirtualServicesCount.IsUnknown() != state.VirtualServicesCount.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VirtualServicesCount.IsNull() && !plan.VirtualServicesCount.IsUnknown() && !state.VirtualServicesCount.IsNull() && !state.VirtualServicesCount.IsUnknown() { - if plan.VirtualServicesCount.ValueInt64() != state.VirtualServicesCount.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.QosProfile.IsNull() != state.QosProfile.IsNull() || plan.QosProfile.IsUnknown() != state.QosProfile.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.QosProfile.IsNull() && !plan.QosProfile.IsUnknown() && !state.QosProfile.IsNull() && !state.QosProfile.IsUnknown() { - if !strings.EqualFold(plan.QosProfile.ValueString(), state.QosProfile.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 368: resources_core.FormatBool(plan.NeedEnableAVI), - 369: resources_core.FormatInt64(plan.VirtualServicesCount), - 372: resources_core.FormatString(plan.IpSpaceName), - 856: resources_core.FormatString(plan.QosProfile), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 22, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "needEnableAVI", Field: "NeedEnableAVI", Type: "bool"}, - {Code: "qosProfile", Field: "QosProfile", Type: "string"}, - {Code: "vdcGroupUid", Field: "VdcGroupUid", Type: "string"}, - {Code: "vdcType", Field: "VdcType", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - {Code: "virtualServicesCount", Field: "VirtualServicesCount", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - resolvedVdcGroupUid, err := r.client.ResolveRefSvcParamValue(ctx, 29, state.VdcGroupUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_group_uid", err.Error()) - } else if resolvedVdcGroupUid != "" && resolvedVdcGroupUid != state.VdcGroupUid.ValueString() { - state.VdcGroupUid = types.StringValue(resolvedVdcGroupUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VdcGroupUid.IsNull() && !plan.VdcGroupUid.IsUnknown() && !state.VdcGroupUid.IsNull() && !state.VdcGroupUid.IsUnknown() { - if strings.EqualFold(state.VdcGroupUid.ValueString(), plan.VdcGroupUid.ValueString()) { - state.VdcGroupUid = plan.VdcGroupUid - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcNsxtResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcNsxtModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcNsxtResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcNsxtResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/23_vc_vm_resource.go b/generated/test/go/23_vc_vm_resource.go deleted file mode 100644 index 67aea2c..0000000 --- a/generated/test/go/23_vc_vm_resource.go +++ /dev/null @@ -1,581 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm -// Service ID: 23 - -var _ resource.Resource = &VcVmResource{} -var _ resource.ResourceWithModifyPlan = &VcVmResource{} -var _ resource.ResourceWithImportState = &VcVmResource{} - -type VcVmResource struct { - client *core.UniversalClient -} - -type VcVmModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessIpList types.String `tfsdk:"access_ip_list"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - TemplateName types.String `tfsdk:"template_name"` - VappUid types.String `tfsdk:"vapp_uid"` - VmCpu types.String `tfsdk:"vm_cpu"` - VmDisksize types.String `tfsdk:"vm_disksize"` - VmName types.String `tfsdk:"vm_name"` - VmRam types.String `tfsdk:"vm_ram"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmResource() resource.Resource { - return &VcVmResource{} -} - -func (r *VcVmResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm" -} - -func (r *VcVmResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("79.111.119.78/32 185.247.187.1/32"), MarkdownDescription: "Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов"}, - "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) Не изменяется после создания."}, - "template_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("RockyLinux_9-16G"), MarkdownDescription: "ОС Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "InstanceUid vApp. vApp должен быть создан Не изменяется после создания."}, - "vm_cpu": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "CPU"}, - "vm_disksize": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("10"), MarkdownDescription: "Размер диска под сервис (GB)"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("vm0001"), MarkdownDescription: "Имя ВМ. Не поддерживает спец-символы Не изменяется после создания."}, - "vm_ram": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("2"), MarkdownDescription: "RAM (GB)"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ip_space_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.TemplateName.IsNull() && !plan.TemplateName.IsUnknown() && !state.TemplateName.IsNull() && !state.TemplateName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.TemplateName.ValueString(), state.TemplateName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить template_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappUid.ValueString(), state.VappUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmName.ValueString(), state.VmName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.VappUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - if config.VappUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { - config.VappUid = types.StringValue(resolvedVappUid) - } - } - params := map[int]string{ - 33: resources_core.FormatString(config.VappUid), - 35: resources_core.FormatString(config.VmName), - 36: resources_core.FormatString(config.VmCpu), - 37: resources_core.FormatString(config.VmRam), - 38: resources_core.FormatString(config.VmDisksize), - 176: resources_core.FormatString(config.TemplateName), - 177: resources_core.FormatString(config.AccessIpList), - 406: resources_core.FormatString(config.IpSpaceName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 23, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.VappUid.IsNull() || data.VappUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVappUid := data.VappUid - if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { - data.VappUid = types.StringValue(resolvedVappUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 33: resources_core.FormatString(data.VappUid), - 35: resources_core.FormatString(data.VmName), - 36: resources_core.FormatString(data.VmCpu), - 37: resources_core.FormatString(data.VmRam), - 38: resources_core.FormatString(data.VmDisksize), - 176: resources_core.FormatString(data.TemplateName), - 177: resources_core.FormatString(data.AccessIpList), - 406: resources_core.FormatString(data.IpSpaceName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 23, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 23, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVappUid.IsNull() && !originalVappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), originalVappUid.ValueString()) { - state.VappUid = originalVappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 23, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { - newState.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() && !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - if strings.EqualFold(newState.VappUid.ValueString(), state.VappUid.ValueString()) { - newState.VappUid = state.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmModel - var state VcVmModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { - if !strings.EqualFold(plan.VmCpu.ValueString(), state.VmCpu.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { - if !strings.EqualFold(plan.VmRam.ValueString(), state.VmRam.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmDisksize.IsNull() != state.VmDisksize.IsNull() || plan.VmDisksize.IsUnknown() != state.VmDisksize.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmDisksize.IsNull() && !plan.VmDisksize.IsUnknown() && !state.VmDisksize.IsNull() && !state.VmDisksize.IsUnknown() { - if !strings.EqualFold(plan.VmDisksize.ValueString(), state.VmDisksize.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { - if !strings.EqualFold(plan.AccessIpList.ValueString(), state.AccessIpList.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 186: resources_core.FormatString(plan.VmCpu), - 187: resources_core.FormatString(plan.VmRam), - 188: resources_core.FormatString(plan.VmDisksize), - 189: resources_core.FormatString(plan.AccessIpList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 23, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "templateName", Field: "TemplateName", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "string"}, - {Code: "vmDisksize", Field: "VmDisksize", Type: "string"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), plan.VappUid.ValueString()) { - state.VappUid = plan.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/25_vcexternalip_resource.go b/generated/test/go/25_vcexternalip_resource.go deleted file mode 100644 index 686e188..0000000 --- a/generated/test/go/25_vcexternalip_resource.go +++ /dev/null @@ -1,604 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vcexternalip -// Service ID: 25 - -var _ resource.Resource = &VcexternalipResource{} -var _ resource.ResourceWithModifyPlan = &VcexternalipResource{} -var _ resource.ResourceWithImportState = &VcexternalipResource{} - -type VcexternalipResource struct { - client *core.UniversalClient -} - -type VcexternalipModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DnatCreate types.Bool `tfsdk:"dnat_create"` - FromServiceCloudEdgeName types.String `tfsdk:"from_service_cloud_edge_name"` - FromServiceCloudEdgeScope types.String `tfsdk:"from_service_cloud_edge_scope"` - FromServiceCloudOrgName types.String `tfsdk:"from_service_cloud_org_name"` - FromServiceCloudVdcName types.String `tfsdk:"from_service_cloud_vdc_name"` - FromServiceCloudVmwareUrl types.String `tfsdk:"from_service_cloud_vmware_url"` - FromServiceNamespace types.String `tfsdk:"from_service_namespace"` - FromServiceVdcGroupName types.String `tfsdk:"from_service_vdc_group_name"` - InternalAddrAccess types.String `tfsdk:"internal_addr_access"` - InternalPortAccess types.String `tfsdk:"internal_port_access"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - ResourceRealm types.String `tfsdk:"resource_realm"` - ServiceUid types.String `tfsdk:"service_uid"` - SnatCreate types.Bool `tfsdk:"snat_create"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcexternalipResource() resource.Resource { - return &VcexternalipResource{} -} - -func (r *VcexternalipResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vcexternalip" -} - -func (r *VcexternalipResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "dnat_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Необходимо создать DNAT-правило."}, - "from_service_cloud_edge_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_edge_scope": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "from_service_cloud_org_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Org составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_vdc_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_cloud_vmware_url": schema.StringAttribute{Optional: true, MarkdownDescription: "Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) Не изменяется после создания."}, - "from_service_namespace": schema.StringAttribute{Required: true, MarkdownDescription: "Namespace составной услуги (для отображения правил FW) Не изменяется после создания."}, - "from_service_vdc_group_name": schema.StringAttribute{Optional: true, MarkdownDescription: "Не изменяется после создания."}, - "internal_addr_access": schema.StringAttribute{Required: true, MarkdownDescription: "Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true"}, - "internal_port_access": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("5432"), MarkdownDescription: "Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
"}, - "ip_space_name": schema.StringAttribute{Required: true, MarkdownDescription: "Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1)"}, - "resource_realm": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("sandbox.nubes.ru"), MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "service_uid": schema.StringAttribute{Required: true}, - "snat_create": schema.BoolAttribute{Optional: true, MarkdownDescription: "Необходимо создать SNAT-правило.
"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcexternalipResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcexternalipModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.FromServiceCloudEdgeName.IsNull() && !plan.FromServiceCloudEdgeName.IsUnknown() && !state.FromServiceCloudEdgeName.IsNull() && !state.FromServiceCloudEdgeName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudEdgeName.ValueString(), state.FromServiceCloudEdgeName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudEdgeScope.IsNull() && !plan.FromServiceCloudEdgeScope.IsUnknown() && !state.FromServiceCloudEdgeScope.IsNull() && !state.FromServiceCloudEdgeScope.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudEdgeScope.ValueString(), state.FromServiceCloudEdgeScope.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_edge_scope", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudOrgName.IsNull() && !plan.FromServiceCloudOrgName.IsUnknown() && !state.FromServiceCloudOrgName.IsNull() && !state.FromServiceCloudOrgName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudOrgName.ValueString(), state.FromServiceCloudOrgName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_org_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudVdcName.IsNull() && !plan.FromServiceCloudVdcName.IsUnknown() && !state.FromServiceCloudVdcName.IsNull() && !state.FromServiceCloudVdcName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudVdcName.ValueString(), state.FromServiceCloudVdcName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vdc_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceCloudVmwareUrl.IsNull() && !plan.FromServiceCloudVmwareUrl.IsUnknown() && !state.FromServiceCloudVmwareUrl.IsNull() && !state.FromServiceCloudVmwareUrl.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceCloudVmwareUrl.ValueString(), state.FromServiceCloudVmwareUrl.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_cloud_vmware_url", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceNamespace.IsNull() && !plan.FromServiceNamespace.IsUnknown() && !state.FromServiceNamespace.IsNull() && !state.FromServiceNamespace.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceNamespace.ValueString(), state.FromServiceNamespace.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_namespace", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.FromServiceVdcGroupName.IsNull() && !plan.FromServiceVdcGroupName.IsUnknown() && !state.FromServiceVdcGroupName.IsNull() && !state.FromServiceVdcGroupName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.FromServiceVdcGroupName.ValueString(), state.FromServiceVdcGroupName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить from_service_vdc_group_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.FromServiceNamespace.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") - return - } - if config.FromServiceNamespace.IsUnknown() { - return - } - if config.InternalAddrAccess.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") - return - } - if config.InternalAddrAccess.IsUnknown() { - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.ServiceUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") - return - } - if config.ServiceUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 140: resources_core.FormatString(config.ServiceUid), - 141: resources_core.FormatBool(config.DnatCreate), - 142: resources_core.FormatString(config.InternalPortAccess), - 143: resources_core.FormatBool(config.SnatCreate), - 144: resources_core.FormatString(config.InternalAddrAccess), - 324: resources_core.FormatString(config.FromServiceNamespace), - 325: resources_core.FormatString(config.FromServiceCloudEdgeName), - 326: resources_core.FormatString(config.FromServiceCloudVdcName), - 327: resources_core.FormatString(config.FromServiceCloudOrgName), - 328: resources_core.FormatString(config.FromServiceCloudVmwareUrl), - 336: resources_core.FormatString(config.IpSpaceName), - 488: resources_core.FormatString(config.ResourceRealm), - 624: resources_core.FormatString(config.FromServiceCloudEdgeScope), - 626: resources_core.FormatString(config.FromServiceVdcGroupName), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 25, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcexternalipResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.FromServiceNamespace.IsNull() || data.FromServiceNamespace.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "from_service_namespace is required.") - return - } - if data.InternalAddrAccess.IsNull() || data.InternalAddrAccess.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "internal_addr_access is required.") - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.ServiceUid.IsNull() || data.ServiceUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "service_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 140: resources_core.FormatString(data.ServiceUid), - 141: resources_core.FormatBool(data.DnatCreate), - 142: resources_core.FormatString(data.InternalPortAccess), - 143: resources_core.FormatBool(data.SnatCreate), - 144: resources_core.FormatString(data.InternalAddrAccess), - 324: resources_core.FormatString(data.FromServiceNamespace), - 325: resources_core.FormatString(data.FromServiceCloudEdgeName), - 326: resources_core.FormatString(data.FromServiceCloudVdcName), - 327: resources_core.FormatString(data.FromServiceCloudOrgName), - 328: resources_core.FormatString(data.FromServiceCloudVmwareUrl), - 336: resources_core.FormatString(data.IpSpaceName), - 488: resources_core.FormatString(data.ResourceRealm), - 624: resources_core.FormatString(data.FromServiceCloudEdgeScope), - 626: resources_core.FormatString(data.FromServiceVdcGroupName), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 25, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 25, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcexternalipResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 25, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcexternalipResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcexternalipModel - var state VcexternalipModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ServiceUid.IsNull() != state.ServiceUid.IsNull() || plan.ServiceUid.IsUnknown() != state.ServiceUid.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ServiceUid.IsNull() && !plan.ServiceUid.IsUnknown() && !state.ServiceUid.IsNull() && !state.ServiceUid.IsUnknown() { - if !strings.EqualFold(plan.ServiceUid.ValueString(), state.ServiceUid.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.DnatCreate.IsNull() != state.DnatCreate.IsNull() || plan.DnatCreate.IsUnknown() != state.DnatCreate.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.DnatCreate.IsNull() && !plan.DnatCreate.IsUnknown() && !state.DnatCreate.IsNull() && !state.DnatCreate.IsUnknown() { - if plan.DnatCreate.ValueBool() != state.DnatCreate.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.SnatCreate.IsNull() != state.SnatCreate.IsNull() || plan.SnatCreate.IsUnknown() != state.SnatCreate.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.SnatCreate.IsNull() && !plan.SnatCreate.IsUnknown() && !state.SnatCreate.IsNull() && !state.SnatCreate.IsUnknown() { - if plan.SnatCreate.ValueBool() != state.SnatCreate.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.InternalPortAccess.IsNull() != state.InternalPortAccess.IsNull() || plan.InternalPortAccess.IsUnknown() != state.InternalPortAccess.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.InternalPortAccess.IsNull() && !plan.InternalPortAccess.IsUnknown() && !state.InternalPortAccess.IsNull() && !state.InternalPortAccess.IsUnknown() { - if !strings.EqualFold(plan.InternalPortAccess.ValueString(), state.InternalPortAccess.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.InternalAddrAccess.IsNull() != state.InternalAddrAccess.IsNull() || plan.InternalAddrAccess.IsUnknown() != state.InternalAddrAccess.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.InternalAddrAccess.IsNull() && !plan.InternalAddrAccess.IsUnknown() && !state.InternalAddrAccess.IsNull() && !state.InternalAddrAccess.IsUnknown() { - if !strings.EqualFold(plan.InternalAddrAccess.ValueString(), state.InternalAddrAccess.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 627: resources_core.FormatString(plan.ServiceUid), - 628: resources_core.FormatString(plan.IpSpaceName), - 629: resources_core.FormatBool(plan.DnatCreate), - 630: resources_core.FormatBool(plan.SnatCreate), - 631: resources_core.FormatString(plan.InternalPortAccess), - 632: resources_core.FormatString(plan.InternalAddrAccess), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 25, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "dnatCreate", Field: "DnatCreate", Type: "bool"}, - {Code: "fromServiceCloudEdgeName", Field: "FromServiceCloudEdgeName", Type: "string"}, - {Code: "fromServiceCloudEdgeScope", Field: "FromServiceCloudEdgeScope", Type: "string"}, - {Code: "fromServiceCloudOrgName", Field: "FromServiceCloudOrgName", Type: "string"}, - {Code: "fromServiceCloudVdcName", Field: "FromServiceCloudVdcName", Type: "string"}, - {Code: "fromServiceCloudVmwareUrl", Field: "FromServiceCloudVmwareUrl", Type: "string"}, - {Code: "fromServiceNamespace", Field: "FromServiceNamespace", Type: "string"}, - {Code: "fromServiceVdcGroupName", Field: "FromServiceVdcGroupName", Type: "string"}, - {Code: "internalAddrAccess", Field: "InternalAddrAccess", Type: "string"}, - {Code: "internalPortAccess", Field: "InternalPortAccess", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - {Code: "serviceUid", Field: "ServiceUid", Type: "string"}, - {Code: "snatCreate", Field: "SnatCreate", Type: "bool"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcexternalipResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcexternalipModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcexternalipResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcexternalipResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/26_vapp_resource.go b/generated/test/go/26_vapp_resource.go deleted file mode 100644 index 68d1ac6..0000000 --- a/generated/test/go/26_vapp_resource.go +++ /dev/null @@ -1,571 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vapp -// Service ID: 26 - -var _ resource.Resource = &VappResource{} -var _ resource.ResourceWithModifyPlan = &VappResource{} -var _ resource.ResourceWithImportState = &VappResource{} - -type VappResource struct { - client *core.UniversalClient -} - -type VappModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - NsxtUid types.String `tfsdk:"nsxt_uid"` - VappName types.String `tfsdk:"vapp_name"` - VdcUid types.String `tfsdk:"vdc_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVappResource() resource.Resource { - return &VappResource{} -} - -func (r *VappResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vapp" -} - -func (r *VappResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "nsxt_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Сетевой шлюз периметра (Edge)` Не изменяется после создания."}, - "vapp_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("lvifmmxj"), MarkdownDescription: "Уникальное в рамках организации Не изменяется после создания."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги `Виртуальный датацентр (vDC)` Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VappResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VappModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.NsxtUid.ValueString(), state.NsxtUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить nsxt_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappName.IsNull() && !plan.VappName.IsUnknown() && !state.VappName.IsNull() && !state.VappName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappName.ValueString(), state.VappName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.NsxtUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") - return - } - if config.NsxtUid.IsUnknown() { - return - } - if config.VdcUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - if config.VdcUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.NsxtUid.IsNull() && !config.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, config.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != config.NsxtUid.ValueString() { - config.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 190: resources_core.FormatString(config.NsxtUid), - 191: resources_core.FormatString(config.VappName), - 623: resources_core.FormatString(config.VdcUid), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 26, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VappResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.NsxtUid.IsNull() || data.NsxtUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "nsxt_uid is required.") - return - } - if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalNsxtUid := data.NsxtUid - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.NsxtUid.IsNull() && !data.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, data.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedNsxtUid != "" && resolvedNsxtUid != data.NsxtUid.ValueString() { - data.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 190: resources_core.FormatString(data.NsxtUid), - 191: resources_core.FormatString(data.VappName), - 623: resources_core.FormatString(data.VdcUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 26, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 26, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { - state.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalNsxtUid.IsNull() && !originalNsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - if strings.EqualFold(state.NsxtUid.ValueString(), originalNsxtUid.ValueString()) { - state.NsxtUid = originalNsxtUid - } - } - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VappResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 26, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, newState.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != newState.NsxtUid.ValueString() { - newState.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() && !newState.NsxtUid.IsNull() && !newState.NsxtUid.IsUnknown() { - if strings.EqualFold(newState.NsxtUid.ValueString(), state.NsxtUid.ValueString()) { - newState.NsxtUid = state.NsxtUid - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VappResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VappModel - var state VappModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 26, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "nsxtUid", Field: "NsxtUid", Type: "string"}, - {Code: "vappName", Field: "VappName", Type: "string"}, - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - resolvedNsxtUid, err := r.client.ResolveRefSvcParamValue(ctx, 22, state.NsxtUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve nsxt_uid", err.Error()) - } else if resolvedNsxtUid != "" && resolvedNsxtUid != state.NsxtUid.ValueString() { - state.NsxtUid = types.StringValue(resolvedNsxtUid) - } - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.NsxtUid.IsNull() && !plan.NsxtUid.IsUnknown() && !state.NsxtUid.IsNull() && !state.NsxtUid.IsUnknown() { - if strings.EqualFold(state.NsxtUid.ValueString(), plan.NsxtUid.ValueString()) { - state.NsxtUid = plan.NsxtUid - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VappResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VappModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VappResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VappResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/27_vc_vm_v2_resource.go b/generated/test/go/27_vc_vm_v2_resource.go deleted file mode 100644 index 0fe1861..0000000 --- a/generated/test/go/27_vc_vm_v2_resource.go +++ /dev/null @@ -1,350 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v2 -// Service ID: 27 - -var _ resource.Resource = &VcVmV2Resource{} -var _ resource.ResourceWithModifyPlan = &VcVmV2Resource{} -var _ resource.ResourceWithImportState = &VcVmV2Resource{} - -type VcVmV2Resource struct { - client *core.UniversalClient -} - -type VcVmV2Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - ServerListJson types.String `tfsdk:"server_list_json"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmV2Resource() resource.Resource { - return &VcVmV2Resource{} -} - -func (r *VcVmV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v2" -} - -func (r *VcVmV2Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "server_list_json": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[{}]"), MarkdownDescription: "Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2)", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmV2Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmV2Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 301: resources_core.FormatString(config.ServerListJson), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 27, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 301: resources_core.FormatString(data.ServerListJson), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 27, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 27, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV2Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 27, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmV2Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV2Model - var state VcVmV2Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ServerListJson.IsNull() != state.ServerListJson.IsNull() || plan.ServerListJson.IsUnknown() != state.ServerListJson.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ServerListJson.IsNull() && !plan.ServerListJson.IsUnknown() && !state.ServerListJson.IsNull() && !state.ServerListJson.IsUnknown() { - if !strings.EqualFold(plan.ServerListJson.ValueString(), state.ServerListJson.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 302: resources_core.FormatString(plan.ServerListJson), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 27, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "serverListJson", Field: "ServerListJson", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmV2Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmV2Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmV2Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/28_vc_vm_v3_resource.go b/generated/test/go/28_vc_vm_v3_resource.go deleted file mode 100644 index 58d967f..0000000 --- a/generated/test/go/28_vc_vm_v3_resource.go +++ /dev/null @@ -1,697 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vm_v3 -// Service ID: 28 - -var _ resource.Resource = &VcVmV3Resource{} -var _ resource.ResourceWithModifyPlan = &VcVmV3Resource{} -var _ resource.ResourceWithImportState = &VcVmV3Resource{} - -type VcVmV3Resource struct { - client *core.UniversalClient -} - -type VcVmV3Model struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessIpList types.String `tfsdk:"access_ip_list"` - AccessPortList types.String `tfsdk:"access_port_list"` - CloudInit types.String `tfsdk:"cloud_init"` - ImageVm types.String `tfsdk:"image_vm"` - IpSpaceName types.String `tfsdk:"ip_space_name"` - UserLogin types.String `tfsdk:"user_login"` - UserPublicKey types.String `tfsdk:"user_public_key"` - VappUid types.String `tfsdk:"vapp_uid"` - VmCpu types.Int64 `tfsdk:"vm_cpu"` - VmDisk types.Int64 `tfsdk:"vm_disk"` - VmName types.String `tfsdk:"vm_name"` - VmRam types.Int64 `tfsdk:"vm_ram"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVmV3Resource() resource.Resource { - return &VcVmV3Resource{} -} - -func (r *VcVmV3Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vm_v3" -} - -func (r *VcVmV3Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_ip_list": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"]", PlanModifiers: []planmodifier.String{resources_core.JsonNormalize()}}, - "access_port_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[\"22\"]"), MarkdownDescription: "По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX"}, - "cloud_init": schema.StringAttribute{Optional: true, MarkdownDescription: "Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. Не изменяется после создания."}, - "image_vm": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания."}, - "ip_space_name": schema.StringAttribute{Required: true}, - "user_login": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("myuser"), MarkdownDescription: "Учётка SSH Не изменяется после создания."}, - "user_public_key": schema.StringAttribute{Required: true, MarkdownDescription: "Публичная часть SSH-Ключа для пользователя Не изменяется после создания."}, - "vapp_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=26 (mapped to UUID in core) Не изменяется после создания."}, - "vm_cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "шт"}, - "vm_disk": schema.Int64Attribute{Optional: true, MarkdownDescription: "Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму"}, - "vm_name": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("web01"), MarkdownDescription: "Не изменяется после создания."}, - "vm_ram": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Gb"}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVmV3Resource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVmV3Model - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.CloudInit.IsNull() && !plan.CloudInit.IsUnknown() && !state.CloudInit.IsNull() && !state.CloudInit.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.CloudInit.ValueString(), state.CloudInit.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить cloud_init", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ImageVm.IsNull() && !plan.ImageVm.IsUnknown() && !state.ImageVm.IsNull() && !state.ImageVm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ImageVm.ValueString(), state.ImageVm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить image_vm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserLogin.IsNull() && !plan.UserLogin.IsUnknown() && !state.UserLogin.IsNull() && !state.UserLogin.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserLogin.ValueString(), state.UserLogin.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.UserPublicKey.IsNull() && !plan.UserPublicKey.IsUnknown() && !state.UserPublicKey.IsNull() && !state.UserPublicKey.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.UserPublicKey.ValueString(), state.UserPublicKey.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить user_public_key", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VappUid.ValueString(), state.VappUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vapp_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.VmName.IsNull() && !plan.VmName.IsUnknown() && !state.VmName.IsNull() && !state.VmName.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VmName.ValueString(), state.VmName.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vm_name", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ImageVm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") - return - } - if config.ImageVm.IsUnknown() { - return - } - if config.IpSpaceName.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if config.IpSpaceName.IsUnknown() { - return - } - if config.UserPublicKey.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") - return - } - if config.UserPublicKey.IsUnknown() { - return - } - if config.VappUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - if config.VappUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VappUid.IsNull() && !config.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, config.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != config.VappUid.ValueString() { - config.VappUid = types.StringValue(resolvedVappUid) - } - } - params := map[int]string{ - 407: resources_core.FormatString(config.VappUid), - 408: resources_core.FormatString(config.VmName), - 409: resources_core.FormatInt64(config.VmCpu), - 410: resources_core.FormatInt64(config.VmRam), - 411: resources_core.FormatInt64(config.VmDisk), - 412: resources_core.FormatString(config.IpSpaceName), - 413: resources_core.FormatString(config.AccessIpList), - 414: resources_core.FormatString(config.ImageVm), - 415: resources_core.FormatString(config.CloudInit), - 416: resources_core.FormatString(config.UserLogin), - 417: resources_core.FormatString(config.UserPublicKey), - 448: resources_core.FormatString(config.AccessPortList), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 28, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVmV3Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ImageVm.IsNull() || data.ImageVm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "image_vm is required.") - return - } - if data.IpSpaceName.IsNull() || data.IpSpaceName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "ip_space_name is required.") - return - } - if data.UserPublicKey.IsNull() || data.UserPublicKey.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "user_public_key is required.") - return - } - if data.VappUid.IsNull() || data.VappUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vapp_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVappUid := data.VappUid - if !data.VappUid.IsNull() && !data.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, data.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVappUid != "" && resolvedVappUid != data.VappUid.ValueString() { - data.VappUid = types.StringValue(resolvedVappUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 407: resources_core.FormatString(data.VappUid), - 408: resources_core.FormatString(data.VmName), - 409: resources_core.FormatInt64(data.VmCpu), - 410: resources_core.FormatInt64(data.VmRam), - 411: resources_core.FormatInt64(data.VmDisk), - 412: resources_core.FormatString(data.IpSpaceName), - 413: resources_core.FormatString(data.AccessIpList), - 414: resources_core.FormatString(data.ImageVm), - 415: resources_core.FormatString(data.CloudInit), - 416: resources_core.FormatString(data.UserLogin), - 417: resources_core.FormatString(data.UserPublicKey), - 448: resources_core.FormatString(data.AccessPortList), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 28, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 28, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVappUid.IsNull() && !originalVappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), originalVappUid.ValueString()) { - state.VappUid = originalVappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV3Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 28, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, newState.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != newState.VappUid.ValueString() { - newState.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() && !newState.VappUid.IsNull() && !newState.VappUid.IsUnknown() { - if strings.EqualFold(newState.VappUid.ValueString(), state.VappUid.ValueString()) { - newState.VappUid = state.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVmV3Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVmV3Model - var state VcVmV3Model - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.VmCpu.IsNull() != state.VmCpu.IsNull() || plan.VmCpu.IsUnknown() != state.VmCpu.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmCpu.IsNull() && !plan.VmCpu.IsUnknown() && !state.VmCpu.IsNull() && !state.VmCpu.IsUnknown() { - if plan.VmCpu.ValueInt64() != state.VmCpu.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmRam.IsNull() != state.VmRam.IsNull() || plan.VmRam.IsUnknown() != state.VmRam.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmRam.IsNull() && !plan.VmRam.IsUnknown() && !state.VmRam.IsNull() && !state.VmRam.IsUnknown() { - if plan.VmRam.ValueInt64() != state.VmRam.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.VmDisk.IsNull() != state.VmDisk.IsNull() || plan.VmDisk.IsUnknown() != state.VmDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.VmDisk.IsNull() && !plan.VmDisk.IsUnknown() && !state.VmDisk.IsNull() && !state.VmDisk.IsUnknown() { - if plan.VmDisk.ValueInt64() != state.VmDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceName.IsNull() != state.IpSpaceName.IsNull() || plan.IpSpaceName.IsUnknown() != state.IpSpaceName.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceName.IsNull() && !plan.IpSpaceName.IsUnknown() && !state.IpSpaceName.IsNull() && !state.IpSpaceName.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceName.ValueString(), state.IpSpaceName.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessIpList.IsNull() != state.AccessIpList.IsNull() || plan.AccessIpList.IsUnknown() != state.AccessIpList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessIpList.IsNull() && !plan.AccessIpList.IsUnknown() && !state.AccessIpList.IsNull() && !state.AccessIpList.IsUnknown() { - if !strings.EqualFold(plan.AccessIpList.ValueString(), state.AccessIpList.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.AccessPortList.IsNull() != state.AccessPortList.IsNull() || plan.AccessPortList.IsUnknown() != state.AccessPortList.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.AccessPortList.IsNull() && !plan.AccessPortList.IsUnknown() && !state.AccessPortList.IsNull() && !state.AccessPortList.IsUnknown() { - if !strings.EqualFold(plan.AccessPortList.ValueString(), state.AccessPortList.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 493: resources_core.FormatInt64(plan.VmCpu), - 494: resources_core.FormatInt64(plan.VmRam), - 495: resources_core.FormatInt64(plan.VmDisk), - 496: resources_core.FormatString(plan.IpSpaceName), - 497: resources_core.FormatString(plan.AccessIpList), - 498: resources_core.FormatString(plan.AccessPortList), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 28, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessIpList", Field: "AccessIpList", Type: "string"}, - {Code: "accessPortList", Field: "AccessPortList", Type: "string"}, - {Code: "cloudInit", Field: "CloudInit", Type: "string"}, - {Code: "imageVm", Field: "ImageVm", Type: "string"}, - {Code: "ipSpaceName", Field: "IpSpaceName", Type: "string"}, - {Code: "userLogin", Field: "UserLogin", Type: "string"}, - {Code: "userPublicKey", Field: "UserPublicKey", Type: "string"}, - {Code: "vappUid", Field: "VappUid", Type: "string"}, - {Code: "vmCpu", Field: "VmCpu", Type: "int64"}, - {Code: "vmDisk", Field: "VmDisk", Type: "int64"}, - {Code: "vmName", Field: "VmName", Type: "string"}, - {Code: "vmRam", Field: "VmRam", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - resolvedVappUid, err := r.client.ResolveRefSvcParamValue(ctx, 26, state.VappUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vapp_uid", err.Error()) - } else if resolvedVappUid != "" && resolvedVappUid != state.VappUid.ValueString() { - state.VappUid = types.StringValue(resolvedVappUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VappUid.IsNull() && !plan.VappUid.IsUnknown() && !state.VappUid.IsNull() && !state.VappUid.IsUnknown() { - if strings.EqualFold(state.VappUid.ValueString(), plan.VappUid.ValueString()) { - state.VappUid = plan.VappUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVmV3Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVmV3Model - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVmV3Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVmV3Resource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/29_vc_vdc_group_resource.go b/generated/test/go/29_vc_vdc_group_resource.go deleted file mode 100644 index 4166b73..0000000 --- a/generated/test/go/29_vc_vdc_group_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc_group -// Service ID: 29 - -var _ resource.Resource = &VcVdcGroupResource{} -var _ resource.ResourceWithModifyPlan = &VcVdcGroupResource{} -var _ resource.ResourceWithImportState = &VcVdcGroupResource{} - -type VcVdcGroupResource struct { - client *core.UniversalClient -} - -type VcVdcGroupModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - VdcUid types.String `tfsdk:"vdc_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVcVdcGroupResource() resource.Resource { - return &VcVdcGroupResource{} -} - -func (r *VcVdcGroupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc_group" -} - -func (r *VcVdcGroupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "vdc_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UI display_name for service_id=21 (mapped to UUID in core) Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcGroupResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VcVdcGroupModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.VdcUid.ValueString(), state.VdcUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить vdc_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.VdcUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - if config.VdcUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.VdcUid.IsNull() && !config.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, config.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != config.VdcUid.ValueString() { - config.VdcUid = types.StringValue(resolvedVdcUid) - } - } - params := map[int]string{ - 618: resources_core.FormatString(config.VdcUid), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 29, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VcVdcGroupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.VdcUid.IsNull() || data.VdcUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "vdc_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalVdcUid := data.VdcUid - if !data.VdcUid.IsNull() && !data.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, data.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedVdcUid != "" && resolvedVdcUid != data.VdcUid.ValueString() { - data.VdcUid = types.StringValue(resolvedVdcUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 618: resources_core.FormatString(data.VdcUid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 29, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 29, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalVdcUid.IsNull() && !originalVdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), originalVdcUid.ValueString()) { - state.VdcUid = originalVdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 29, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, newState.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != newState.VdcUid.ValueString() { - newState.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() && !newState.VdcUid.IsNull() && !newState.VdcUid.IsUnknown() { - if strings.EqualFold(newState.VdcUid.ValueString(), state.VdcUid.ValueString()) { - newState.VdcUid = state.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VcVdcGroupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcGroupModel - var state VcVdcGroupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 29, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "vdcUid", Field: "VdcUid", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - resolvedVdcUid, err := r.client.ResolveRefSvcParamValue(ctx, 21, state.VdcUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve vdc_uid", err.Error()) - } else if resolvedVdcUid != "" && resolvedVdcUid != state.VdcUid.ValueString() { - state.VdcUid = types.StringValue(resolvedVdcUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.VdcUid.IsNull() && !plan.VdcUid.IsUnknown() && !state.VdcUid.IsNull() && !state.VdcUid.IsUnknown() { - if strings.EqualFold(state.VdcUid.ValueString(), plan.VdcUid.ValueString()) { - state.VdcUid = plan.VdcUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcGroupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VcVdcGroupResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VcVdcGroupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/29_vc_vdc_group_vdc_resource.go b/generated/test/go/29_vc_vdc_group_vdc_resource.go deleted file mode 100644 index 3e3e8ef..0000000 --- a/generated/test/go/29_vc_vdc_group_vdc_resource.go +++ /dev/null @@ -1,326 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vc_vdc_group -// Subresource: vdc - -var _ resource.Resource = &VcVdcGroupVdcResource{} - -// VcVdcGroupVdcResource manages a subresource via operations. -type VcVdcGroupVdcResource struct { - client *core.UniversalClient -} - -type VcVdcGroupVdcModel struct { - ID types.String `tfsdk:"id"` - VcVdcGroupID types.String `tfsdk:"vc_vdc_group_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewVcVdcGroupVdcResource() resource.Resource { - return &VcVdcGroupVdcResource{} -} - -func (r *VcVdcGroupVdcResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vc_vdc_group_vdc" -} - -func (r *VcVdcGroupVdcResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "vc_vdc_group_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VcVdcGroupVdcResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan VcVdcGroupVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("vdc", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcVdcGroupVdcResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VcVdcGroupVdcResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VcVdcGroupVdcModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.VcVdcGroupID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "vdc", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *VcVdcGroupVdcResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VcVdcGroupVdcModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.VcVdcGroupID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("vdc", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *VcVdcGroupVdcResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/2_template_resource.go b/generated/test/go/2_template_resource.go deleted file mode 100644 index b45b9e6..0000000 --- a/generated/test/go/2_template_resource.go +++ /dev/null @@ -1,397 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: template -// Service ID: 2 - -var _ resource.Resource = &TemplateResource{} -var _ resource.ResourceWithModifyPlan = &TemplateResource{} -var _ resource.ResourceWithImportState = &TemplateResource{} - -type TemplateResource struct { - client *core.UniversalClient -} - -// TemplateAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type TemplateAccessConfigurationModel struct { - Type types.String `tfsdk:"type" json:"type"` - Domain types.String `tfsdk:"domain" json:"domain"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` -} - -// TemplateClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type TemplateClusterConfigurationModel struct { - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// TemplateStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type TemplateStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type TemplateModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *TemplateAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *TemplateClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *TemplateStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewTemplateResource() resource.Resource { - return &TemplateResource{} -} - -func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_template" -} - -func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "type": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("ingress")}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("abracadabra-pin1v6zw")}, - "master_ip_space": schema.StringAttribute{Required: true}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]")}, - "slave_ip_space": schema.StringAttribute{Required: true}, - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(5)}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(128)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *TemplateResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *TemplateModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 813: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64())}), - 814: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 815: resources_core.BuildJSON(map[string]string{"type": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 2, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *TemplateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 813: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64())}), - 814: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 815: resources_core.BuildJSON(map[string]string{"type": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 2, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 2, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 2, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan TemplateModel - var state TemplateModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 816: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 817: resources_core.BuildJSON(map[string]string{"type": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.Type.ValueString()), "domain": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.Domain.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 2, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *TemplateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state TemplateModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *TemplateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *TemplateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/32_vmpostgre_resource.go b/generated/test/go/32_vmpostgre_resource.go deleted file mode 100644 index 14a037e..0000000 --- a/generated/test/go/32_vmpostgre_resource.go +++ /dev/null @@ -1,322 +0,0 @@ -package resources_gen - -import ( - "context" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: vmpostgre -// Service ID: 32 - -var _ resource.Resource = &VmpostgreResource{} -var _ resource.ResourceWithModifyPlan = &VmpostgreResource{} -var _ resource.ResourceWithImportState = &VmpostgreResource{} - -type VmpostgreResource struct { - client *core.UniversalClient -} - -type VmpostgreModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewVmpostgreResource() resource.Resource { - return &VmpostgreResource{} -} - -func (r *VmpostgreResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_vmpostgre" -} - -func (r *VmpostgreResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *VmpostgreResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *VmpostgreModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{} - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 32, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *VmpostgreResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 32, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{} - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 32, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 32, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VmpostgreResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 32, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *VmpostgreResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan VmpostgreModel - var state VmpostgreModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 32, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{}) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *VmpostgreResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state VmpostgreModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *VmpostgreResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *VmpostgreResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/50_nextcloud_backup_resource.go b/generated/test/go/50_nextcloud_backup_resource.go deleted file mode 100644 index feb1f0e..0000000 --- a/generated/test/go/50_nextcloud_backup_resource.go +++ /dev/null @@ -1,344 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Subresource: backup - -var _ resource.Resource = &NextcloudBackupResource{} - -// NextcloudBackupResource manages a subresource via operations. -type NextcloudBackupResource struct { - client *core.UniversalClient -} - -type NextcloudBackupModel struct { - ID types.String `tfsdk:"id"` - NextcloudID types.String `tfsdk:"nextcloud_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - StartupConfiguration types.String `tfsdk:"startup_configuration"` -} - -func NewNextcloudBackupResource() resource.Resource { - return &NextcloudBackupResource{} -} - -func (r *NextcloudBackupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud_backup" -} - -func (r *NextcloudBackupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "nextcloud_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "startup_configuration": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudBackupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan NextcloudBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.StartupConfiguration.IsNull() || plan.StartupConfiguration.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "startup_configuration is required.") - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - } - identityCodes := []string{ - "startupConfiguration", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudBackupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudBackupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.NextcloudID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(plan.StartupConfiguration), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *NextcloudBackupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NextcloudBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.NextcloudID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "startupConfiguration": resources_core.FormatString(state.StartupConfiguration), - } - identityCodes := []string{ - "startupConfiguration", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *NextcloudBackupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/50_nextcloud_resource.go b/generated/test/go/50_nextcloud_resource.go deleted file mode 100644 index 43d2c4e..0000000 --- a/generated/test/go/50_nextcloud_resource.go +++ /dev/null @@ -1,414 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nextcloud -// Service ID: 50 - -var _ resource.Resource = &NextcloudResource{} -var _ resource.ResourceWithModifyPlan = &NextcloudResource{} -var _ resource.ResourceWithImportState = &NextcloudResource{} - -type NextcloudResource struct { - client *core.UniversalClient -} - -// NextcloudAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type NextcloudAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// NextcloudAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type NextcloudAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - DocumentServer types.String `tfsdk:"document_server" json:"documentServer"` - LogLevel types.String `tfsdk:"log_level" json:"logLevel"` -} - -// NextcloudClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type NextcloudClusterConfigurationModel struct { - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// NextcloudStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type NextcloudStartupConfigurationModel struct { - PsqlEnabled types.Bool `tfsdk:"psql_enabled" json:"psqlEnabled"` - RedisEnabled types.Bool `tfsdk:"redis_enabled" json:"redisEnabled"` - PsqlUid types.String `tfsdk:"psql_uid" json:"psqlUid"` - RedisUid types.String `tfsdk:"redis_uid" json:"redisUid"` - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type NextcloudModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *NextcloudAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *NextcloudAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *NextcloudClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *NextcloudStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNextcloudResource() resource.Resource { - return &NextcloudResource{} -} - -func (r *NextcloudResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nextcloud" -} - -func (r *NextcloudResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nextcloud-c2xizgvp"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("33.0.4")}, - "document_server": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("collabora"), MarkdownDescription: "Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ"}, - "log_level": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("INFO"), MarkdownDescription: "Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "psql_enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud"}, - "redis_enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud"}, - "psql_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета`"}, - "redis_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета`"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NextcloudResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NextcloudModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1055: resources_core.BuildJSON(map[string]string{"psqlEnabled": fmt.Sprintf("%v", config.StartupConfiguration.PsqlEnabled.ValueBool()), "redisEnabled": fmt.Sprintf("%v", config.StartupConfiguration.RedisEnabled.ValueBool()), "psqlUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.PsqlUid.ValueString()), "redisUid": fmt.Sprintf("\"%s\"", config.StartupConfiguration.RedisUid.ValueString()), "resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1056: resources_core.BuildJSON(map[string]string{"disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1057: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1058: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", config.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", config.AppConfiguration.LogLevel.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 50, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NextcloudResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1055: resources_core.BuildJSON(map[string]string{"psqlEnabled": fmt.Sprintf("%v", data.StartupConfiguration.PsqlEnabled.ValueBool()), "redisEnabled": fmt.Sprintf("%v", data.StartupConfiguration.RedisEnabled.ValueBool()), "psqlUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.PsqlUid.ValueString()), "redisUid": fmt.Sprintf("\"%s\"", data.StartupConfiguration.RedisUid.ValueString()), "resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1056: resources_core.BuildJSON(map[string]string{"disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1057: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1058: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", data.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", data.AppConfiguration.LogLevel.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 50, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 50, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 50, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NextcloudResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NextcloudModel - var state NextcloudModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1059: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64())}), - 1060: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "documentServer": fmt.Sprintf("\"%s\"", plan.AppConfiguration.DocumentServer.ValueString()), "logLevel": fmt.Sprintf("\"%s\"", plan.AppConfiguration.LogLevel.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 50, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NextcloudResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NextcloudModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NextcloudResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NextcloudResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/81_superset_resource.go b/generated/test/go/81_superset_resource.go deleted file mode 100644 index c66e1d8..0000000 --- a/generated/test/go/81_superset_resource.go +++ /dev/null @@ -1,447 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: superset -// Service ID: 81 - -var _ resource.Resource = &SupersetResource{} -var _ resource.ResourceWithModifyPlan = &SupersetResource{} -var _ resource.ResourceWithImportState = &SupersetResource{} - -type SupersetResource struct { - client *core.UniversalClient -} - -type SupersetModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - Emails types.String `tfsdk:"emails"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewSupersetResource() resource.Resource { - return &SupersetResource{} -} - -func (r *SupersetResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_superset" -} - -func (r *SupersetResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("superset-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "emails": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("testuser1"), MarkdownDescription: "Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *SupersetResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *SupersetModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Emails.IsNull() && !plan.Emails.IsUnknown() && !state.Emails.IsNull() && !state.Emails.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Emails.ValueString(), state.Emails.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить emails", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 14: resources_core.FormatString(config.Domain), - 20: resources_core.FormatString(config.Emails), - 96: resources_core.FormatInt64(config.ResourceCPU), - 97: resources_core.FormatInt64(config.ResourceMemory), - 98: resources_core.FormatInt64(config.ResourceDisk), - 109: resources_core.FormatString(config.ResourceRealm), - 227: resources_core.FormatInt64(config.ResourceInstances), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 81, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *SupersetResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 14: resources_core.FormatString(data.Domain), - 20: resources_core.FormatString(data.Emails), - 96: resources_core.FormatInt64(data.ResourceCPU), - 97: resources_core.FormatInt64(data.ResourceMemory), - 98: resources_core.FormatInt64(data.ResourceDisk), - 109: resources_core.FormatString(data.ResourceRealm), - 227: resources_core.FormatInt64(data.ResourceInstances), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 81, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 81, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *SupersetResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 81, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *SupersetResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan SupersetModel - var state SupersetModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 81, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "emails", Field: "Emails", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *SupersetResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state SupersetModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *SupersetResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *SupersetResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/82_harbor_resource.go b/generated/test/go/82_harbor_resource.go deleted file mode 100644 index ab83b24..0000000 --- a/generated/test/go/82_harbor_resource.go +++ /dev/null @@ -1,482 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: harbor -// Service ID: 82 - -var _ resource.Resource = &HarborResource{} -var _ resource.ResourceWithModifyPlan = &HarborResource{} -var _ resource.ResourceWithImportState = &HarborResource{} - -type HarborResource struct { - client *core.UniversalClient -} - -type HarborModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - S3Uid types.String `tfsdk:"s3_uid"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewHarborResource() resource.Resource { - return &HarborResource{} -} - -func (r *HarborResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_harbor" -} - -func (r *HarborResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("harbor-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "s3_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID экземпляра услуги S3 Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *HarborResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *HarborModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.S3Uid.IsNull() && !plan.S3Uid.IsUnknown() && !state.S3Uid.IsNull() && !state.S3Uid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.S3Uid.ValueString(), state.S3Uid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить s3_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - if config.S3Uid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") - return - } - if config.S3Uid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.S3Uid.IsNull() && !config.S3Uid.IsUnknown() { - resolvedS3Uid, err := r.client.ResolveRefSvcParamValue(ctx, 12, config.S3Uid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve s3_uid", err.Error()) - } else if resolvedS3Uid != "" && resolvedS3Uid != config.S3Uid.ValueString() { - config.S3Uid = types.StringValue(resolvedS3Uid) - } - } - params := map[int]string{ - 16: resources_core.FormatString(config.Domain), - 110: resources_core.FormatString(config.ResourceRealm), - 228: resources_core.FormatInt64(config.ResourceCPU), - 229: resources_core.FormatInt64(config.ResourceMemory), - 230: resources_core.FormatInt64(config.ResourceInstances), - 231: resources_core.FormatString(config.S3Uid), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 82, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *HarborResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if data.S3Uid.IsNull() || data.S3Uid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "s3_uid is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 16: resources_core.FormatString(data.Domain), - 110: resources_core.FormatString(data.ResourceRealm), - 228: resources_core.FormatInt64(data.ResourceCPU), - 229: resources_core.FormatInt64(data.ResourceMemory), - 230: resources_core.FormatInt64(data.ResourceInstances), - 231: resources_core.FormatString(data.S3Uid), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 82, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 82, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HarborResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 82, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *HarborResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HarborModel - var state HarborModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 822: resources_core.FormatInt64(plan.ResourceInstances), - 823: resources_core.FormatInt64(plan.ResourceCPU), - 824: resources_core.FormatInt64(plan.ResourceMemory), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 82, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HarborResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state HarborModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *HarborResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *HarborResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/86_k8s_velero_resource.go b/generated/test/go/86_k8s_velero_resource.go deleted file mode 100644 index 4ad3d14..0000000 --- a/generated/test/go/86_k8s_velero_resource.go +++ /dev/null @@ -1,401 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_velero -// Service ID: 86 - -var _ resource.Resource = &K8sVeleroResource{} -var _ resource.ResourceWithModifyPlan = &K8sVeleroResource{} -var _ resource.ResourceWithImportState = &K8sVeleroResource{} - -type K8sVeleroResource struct { - client *core.UniversalClient -} - -// K8sVeleroBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type K8sVeleroBackupConfigurationModel struct { - S3BucketUid types.String `tfsdk:"s3_bucket_uid" json:"s3BucketUid"` -} - -// K8sVeleroClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type K8sVeleroClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// K8sVeleroNodeAgentConfigurationModel — вложенная модель для map-fixed параметра nodeAgentConfiguration. -type K8sVeleroNodeAgentConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` -} - -// K8sVeleroStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type K8sVeleroStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type K8sVeleroModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - BackupConfiguration *K8sVeleroBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *K8sVeleroClusterConfigurationModel `tfsdk:"cluster_configuration"` - NodeAgentConfiguration *K8sVeleroNodeAgentConfigurationModel `tfsdk:"node_agent_configuration"` - StartupConfiguration *K8sVeleroStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sVeleroResource() resource.Resource { - return &K8sVeleroResource{} -} - -func (r *K8sVeleroResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_velero" -} - -func (r *K8sVeleroResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "s3_bucket_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3-Бакета обязателен
Учётная запись будет создана автоматически, если её нету"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "node_agent_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2000), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(2048), MarkdownDescription: "Указывается в Megabytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sVeleroResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sVeleroModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 962: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 963: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 964: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.NodeAgentConfiguration.Memory.ValueInt64())}), - 1076: resources_core.BuildJSON(map[string]string{"s3BucketUid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3BucketUid.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 86, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sVeleroResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 86, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 962: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 963: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 964: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.NodeAgentConfiguration.Memory.ValueInt64())}), - 1076: resources_core.BuildJSON(map[string]string{"s3BucketUid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3BucketUid.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 86, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 86, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sVeleroResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 86, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sVeleroResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sVeleroModel - var state K8sVeleroModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 965: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 966: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.NodeAgentConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.NodeAgentConfiguration.Memory.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 86, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "nodeAgentConfiguration", Field: "NodeAgentConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sVeleroResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sVeleroModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sVeleroResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sVeleroResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/88_k8s_ziti_controller_resource.go b/generated/test/go/88_k8s_ziti_controller_resource.go deleted file mode 100644 index 4c54fce..0000000 --- a/generated/test/go/88_k8s_ziti_controller_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: k8s_ziti_controller -// Service ID: 88 - -var _ resource.Resource = &K8sZitiControllerResource{} -var _ resource.ResourceWithModifyPlan = &K8sZitiControllerResource{} -var _ resource.ResourceWithImportState = &K8sZitiControllerResource{} - -type K8sZitiControllerResource struct { - client *core.UniversalClient -} - -type K8sZitiControllerModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewK8sZitiControllerResource() resource.Resource { - return &K8sZitiControllerResource{} -} - -func (r *K8sZitiControllerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_k8s_ziti_controller" -} - -func (r *K8sZitiControllerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("k8sZitiController-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(300), MarkdownDescription: "Квота ядра пода (milicores) Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *K8sZitiControllerResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *K8sZitiControllerModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 294: resources_core.FormatInt64(config.ResourceCPU), - 295: resources_core.FormatInt64(config.ResourceMemory), - 296: resources_core.FormatInt64(config.ResourceDisk), - 297: resources_core.FormatInt64(config.ResourceInstances), - 298: resources_core.FormatString(config.ResourceRealm), - 299: resources_core.FormatString(config.Domain), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 88, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *K8sZitiControllerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 294: resources_core.FormatInt64(data.ResourceCPU), - 295: resources_core.FormatInt64(data.ResourceMemory), - 296: resources_core.FormatInt64(data.ResourceDisk), - 297: resources_core.FormatInt64(data.ResourceInstances), - 298: resources_core.FormatString(data.ResourceRealm), - 299: resources_core.FormatString(data.Domain), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 88, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 88, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sZitiControllerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 88, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *K8sZitiControllerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan K8sZitiControllerModel - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 88, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *K8sZitiControllerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state K8sZitiControllerModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *K8sZitiControllerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *K8sZitiControllerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/89_flask_resource.go b/generated/test/go/89_flask_resource.go deleted file mode 100644 index 1fe8642..0000000 --- a/generated/test/go/89_flask_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: flask -// Service ID: 89 - -var _ resource.Resource = &FlaskResource{} -var _ resource.ResourceWithModifyPlan = &FlaskResource{} -var _ resource.ResourceWithImportState = &FlaskResource{} - -type FlaskResource struct { - client *core.UniversalClient -} - -// FlaskAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type FlaskAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// FlaskAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type FlaskAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// FlaskClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type FlaskClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// FlaskStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type FlaskStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type FlaskModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *FlaskAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *FlaskAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *FlaskClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *FlaskStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewFlaskResource() resource.Resource { - return &FlaskResource{} -} - -func (r *FlaskResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_flask" -} - -func (r *FlaskResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("flask-mdpw2bcy"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("3.12")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/Foxyhhd/Baldurs-Gate-test.git"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}")}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *FlaskResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *FlaskModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1037: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1038: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1039: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1040: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1041: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 89, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *FlaskResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1037: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1038: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1039: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1040: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1041: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 89, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 89, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *FlaskResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 89, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *FlaskResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan FlaskModel - var state FlaskModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1042: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1043: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1044: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 89, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *FlaskResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state FlaskModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *FlaskResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *FlaskResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/90_postgres_backup_resource.go b/generated/test/go/90_postgres_backup_resource.go deleted file mode 100644 index 957eb3e..0000000 --- a/generated/test/go/90_postgres_backup_resource.go +++ /dev/null @@ -1,326 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: backup - -var _ resource.Resource = &PostgresBackupResource{} - -// PostgresBackupResource manages a subresource via operations. -type PostgresBackupResource struct { - client *core.UniversalClient -} - -type PostgresBackupModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` -} - -func NewPostgresBackupResource() resource.Resource { - return &PostgresBackupResource{} -} - -func (r *PostgresBackupResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_backup" -} - -func (r *PostgresBackupResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresBackupResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_backup", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresBackupResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresBackupResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresBackupModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{} - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "backup", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresBackupResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresBackupModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{} - identityCodes := []string{} - listKey, idKey := resources_core.ResolveSubresourceStateKeys("backup", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{}) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresBackupResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/90_postgres_database_resource.go b/generated/test/go/90_postgres_database_resource.go deleted file mode 100644 index 7310720..0000000 --- a/generated/test/go/90_postgres_database_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: database - -var _ resource.Resource = &PostgresDatabaseResource{} - -// PostgresDatabaseResource manages a subresource via operations. -type PostgresDatabaseResource struct { - client *core.UniversalClient -} - -type PostgresDatabaseModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` - DbOwner types.String `tfsdk:"db_owner"` -} - -func NewPostgresDatabaseResource() resource.Resource { - return &PostgresDatabaseResource{} -} - -func (r *PostgresDatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_database" -} - -func (r *PostgresDatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "db_owner": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.DbName.IsNull() || plan.DbName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") - return - } - if plan.DbOwner.IsNull() || plan.DbOwner.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_owner is required.") - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - } - identityCodes := []string{ - "dbName", - "dbOwner", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_database", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresDatabaseModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "dbName": resources_core.FormatString(plan.DbName), - "dbOwner": resources_core.FormatString(plan.DbOwner), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "database", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresDatabaseModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_database" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - "dbOwner": resources_core.FormatString(state.DbOwner), - } - identityCodes := []string{ - "dbName", - "dbOwner", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("database", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "dbName": resources_core.FormatString(state.DbName), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_database", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresDatabaseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/90_postgres_resource.go b/generated/test/go/90_postgres_resource.go deleted file mode 100644 index 4b3c2ca..0000000 --- a/generated/test/go/90_postgres_resource.go +++ /dev/null @@ -1,475 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Service ID: 90 - -var _ resource.Resource = &PostgresResource{} -var _ resource.ResourceWithModifyPlan = &PostgresResource{} -var _ resource.ResourceWithImportState = &PostgresResource{} - -type PostgresResource struct { - client *core.UniversalClient -} - -// PostgresAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type PostgresAccessConfigurationModel struct { - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` -} - -// PostgresAutoscaleConfigurationModel — вложенная модель для map-fixed параметра autoscaleConfiguration. -type PostgresAutoscaleConfigurationModel struct { - Enabled types.Bool `tfsdk:"enabled" json:"enabled"` - Schedule types.Int64 `tfsdk:"schedule" json:"schedule"` - Quota types.Int64 `tfsdk:"quota" json:"quota"` - Percent types.Int64 `tfsdk:"percent" json:"percent"` -} - -// PostgresBackupConfigurationModel — вложенная модель для map-fixed параметра backupConfiguration. -type PostgresBackupConfigurationModel struct { - Schedule types.String `tfsdk:"schedule" json:"schedule"` - S3Uid types.String `tfsdk:"s3_uid" json:"s3Uid"` - Retain types.Int64 `tfsdk:"retain" json:"retain"` -} - -// PostgresClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type PostgresClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// PostgresPostgresConfigurationModel — вложенная модель для map-fixed параметра postgresConfiguration. -type PostgresPostgresConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - SslRequired types.Bool `tfsdk:"ssl_required" json:"sslRequired"` - PoolerMaster types.Bool `tfsdk:"pooler_master" json:"poolerMaster"` - PoolerSlave types.Bool `tfsdk:"pooler_slave" json:"poolerSlave"` -} - -// PostgresStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type PostgresStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type PostgresModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *PostgresAccessConfigurationModel `tfsdk:"access_configuration"` - AutoscaleConfiguration *PostgresAutoscaleConfigurationModel `tfsdk:"autoscale_configuration"` - BackupConfiguration *PostgresBackupConfigurationModel `tfsdk:"backup_configuration"` - ClusterConfiguration *PostgresClusterConfigurationModel `tfsdk:"cluster_configuration"` - PostgresConf types.String `tfsdk:"postgres_conf"` - PostgresConfiguration *PostgresPostgresConfigurationModel `tfsdk:"postgres_configuration"` - StartupConfiguration *PostgresStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewPostgresResource() resource.Resource { - return &PostgresResource{} -} - -func (r *PostgresResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres" -} - -func (r *PostgresResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "slave_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - }}, - "autoscale_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги"}, - "schedule": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(0), MarkdownDescription: "Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23)"}, - "quota": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "До какого размера возможно увеличивать диск, когда включен Autoscale"}, - "percent": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb**"}, - }}, - "backup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "schedule": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("0 0 * * *")}, - "s3_uid": schema.StringAttribute{Required: true, MarkdownDescription: "Экземпляр S3 обязателен"}, - "retain": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(7)}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "postgres_conf": schema.StringAttribute{Optional: true}, - "postgres_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("17")}, - "ssl_required": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true), MarkdownDescription: "По умолчанию подключение обязательное (sslmode=require)"}, - "pooler_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi**"}, - "pooler_slave": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi**"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *PostgresModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 788: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 789: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 790: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString())}), - 791: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", config.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", config.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", config.PostgresConfiguration.PoolerSlave.ValueBool())}), - 792: resources_core.FormatString(config.PostgresConf), - 793: resources_core.BuildJSON(map[string]string{"schedule": fmt.Sprintf("\"%s\"", config.BackupConfiguration.Schedule.ValueString()), "s3Uid": fmt.Sprintf("\"%s\"", config.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", config.BackupConfiguration.Retain.ValueInt64())}), - 794: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", config.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", config.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", config.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", config.AutoscaleConfiguration.Percent.ValueInt64())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 90, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *PostgresResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 788: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 789: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 790: resources_core.BuildJSON(map[string]string{"masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString())}), - 791: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", data.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", data.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", data.PostgresConfiguration.PoolerSlave.ValueBool())}), - 792: resources_core.FormatString(data.PostgresConf), - 793: resources_core.BuildJSON(map[string]string{"schedule": fmt.Sprintf("\"%s\"", data.BackupConfiguration.Schedule.ValueString()), "s3Uid": fmt.Sprintf("\"%s\"", data.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", data.BackupConfiguration.Retain.ValueInt64())}), - 794: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", data.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", data.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", data.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", data.AutoscaleConfiguration.Percent.ValueInt64())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 90, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 90, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 90, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *PostgresResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresModel - var state PostgresModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.PostgresConf.IsNull() != state.PostgresConf.IsNull() || plan.PostgresConf.IsUnknown() != state.PostgresConf.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.PostgresConf.IsNull() && !plan.PostgresConf.IsUnknown() && !state.PostgresConf.IsNull() && !state.PostgresConf.IsUnknown() { - if !strings.EqualFold(plan.PostgresConf.ValueString(), state.PostgresConf.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 795: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 796: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString()), "slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString())}), - 797: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.PostgresConfiguration.Version.ValueString()), "sslRequired": fmt.Sprintf("%v", plan.PostgresConfiguration.SslRequired.ValueBool()), "poolerMaster": fmt.Sprintf("%v", plan.PostgresConfiguration.PoolerMaster.ValueBool()), "poolerSlave": fmt.Sprintf("%v", plan.PostgresConfiguration.PoolerSlave.ValueBool())}), - 798: resources_core.FormatString(plan.PostgresConf), - 799: resources_core.BuildJSON(map[string]string{"s3Uid": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.S3Uid.ValueString()), "retain": fmt.Sprintf("%d", plan.BackupConfiguration.Retain.ValueInt64()), "schedule": fmt.Sprintf("\"%s\"", plan.BackupConfiguration.Schedule.ValueString())}), - 800: resources_core.BuildJSON(map[string]string{"enabled": fmt.Sprintf("%v", plan.AutoscaleConfiguration.Enabled.ValueBool()), "schedule": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Schedule.ValueInt64()), "quota": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Quota.ValueInt64()), "percent": fmt.Sprintf("%d", plan.AutoscaleConfiguration.Percent.ValueInt64())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 90, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "autoscaleConfiguration", Field: "AutoscaleConfiguration", Type: "map-fixed"}, - {Code: "backupConfiguration", Field: "BackupConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "postgresConf", Field: "PostgresConf", Type: "array-map-fixed"}, - {Code: "postgresConfiguration", Field: "PostgresConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *PostgresResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *PostgresResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/90_postgres_user_resource.go b/generated/test/go/90_postgres_user_resource.go deleted file mode 100644 index 5101eca..0000000 --- a/generated/test/go/90_postgres_user_resource.go +++ /dev/null @@ -1,358 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: postgres -// Subresource: user - -var _ resource.Resource = &PostgresUserResource{} - -// PostgresUserResource manages a subresource via operations. -type PostgresUserResource struct { - client *core.UniversalClient -} - -type PostgresUserModel struct { - ID types.String `tfsdk:"id"` - PostgresID types.String `tfsdk:"postgres_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Role types.String `tfsdk:"role"` - Username types.String `tfsdk:"username"` -} - -func NewPostgresUserResource() resource.Resource { - return &PostgresUserResource{} -} - -func (r *PostgresUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_postgres_user" -} - -func (r *PostgresUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "postgres_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "role": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать роль из списка Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Не изменяется после создания. Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PostgresUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan PostgresUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.Role.IsNull() || plan.Role.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "role is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "role": resources_core.FormatString(plan.Role), - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "role", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "role": resources_core.FormatString(plan.Role), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PostgresUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PostgresUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "" == "" { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.PostgresID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{}) - - idParams := map[string]string{ - "role": resources_core.FormatString(plan.Role), - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *PostgresUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PostgresUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.PostgresID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "role": resources_core.FormatString(state.Role), - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "role", - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *PostgresUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/91_redis_resource.go b/generated/test/go/91_redis_resource.go deleted file mode 100644 index 864cd27..0000000 --- a/generated/test/go/91_redis_resource.go +++ /dev/null @@ -1,393 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: redis -// Service ID: 91 - -var _ resource.Resource = &RedisResource{} -var _ resource.ResourceWithModifyPlan = &RedisResource{} -var _ resource.ResourceWithImportState = &RedisResource{} - -type RedisResource struct { - client *core.UniversalClient -} - -// RedisAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type RedisAccessConfigurationModel struct { - SlaveAccessList types.String `tfsdk:"slave_access_list" json:"slaveAccessList"` - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` - SlaveIpSpace types.String `tfsdk:"slave_ip_space" json:"slaveIpSpace"` -} - -// RedisClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type RedisClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` -} - -// RedisStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type RedisStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type RedisModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *RedisAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *RedisClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *RedisStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewRedisResource() resource.Resource { - return &RedisResource{} -} - -func (r *RedisResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_redis" -} - -func (r *RedisResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "slave_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - "slave_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(10), MarkdownDescription: "Указывается в GIGAbytes"}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *RedisResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *RedisModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1061: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1062: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64())}), - 1063: resources_core.BuildJSON(map[string]string{"slaveAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 91, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *RedisResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1061: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1062: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64())}), - 1063: resources_core.BuildJSON(map[string]string{"slaveAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 91, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 91, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RedisResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 91, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *RedisResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RedisModel - var state RedisModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1064: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64())}), - 1065: resources_core.BuildJSON(map[string]string{"slaveAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveAccessList.ValueString()), "masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString()), "slaveIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.SlaveIpSpace.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 91, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RedisResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state RedisModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *RedisResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *RedisResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/92_mongodb_resource.go b/generated/test/go/92_mongodb_resource.go deleted file mode 100644 index d4f5b8a..0000000 --- a/generated/test/go/92_mongodb_resource.go +++ /dev/null @@ -1,389 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mongodb -// Service ID: 92 - -var _ resource.Resource = &MongodbResource{} -var _ resource.ResourceWithModifyPlan = &MongodbResource{} -var _ resource.ResourceWithImportState = &MongodbResource{} - -type MongodbResource struct { - client *core.UniversalClient -} - -// MongodbAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type MongodbAccessConfigurationModel struct { - MasterIpSpace types.String `tfsdk:"master_ip_space" json:"masterIpSpace"` - MasterAccessList types.String `tfsdk:"master_access_list" json:"masterAccessList"` -} - -// MongodbClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type MongodbClusterConfigurationModel struct { - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` - Disk types.Int64 `tfsdk:"disk" json:"disk"` - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` -} - -// MongodbStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type MongodbStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type MongodbModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - AccessConfiguration *MongodbAccessConfigurationModel `tfsdk:"access_configuration"` - ClusterConfiguration *MongodbClusterConfigurationModel `tfsdk:"cluster_configuration"` - StartupConfiguration *MongodbStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewMongodbResource() resource.Resource { - return &MongodbResource{} -} - -func (r *MongodbResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mongodb" -} - -func (r *MongodbResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "master_ip_space": schema.StringAttribute{Required: true, MarkdownDescription: "Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа"}, - "master_access_list": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("[]"), MarkdownDescription: "Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1024)}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - "disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(5), MarkdownDescription: "Указывается в Gb"}, - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000)}, - }}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MongodbResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *MongodbModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1089: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1090: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", config.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64())}), - 1091: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", config.AccessConfiguration.MasterAccessList.ValueString())}), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 92, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *MongodbResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1089: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1090: resources_core.BuildJSON(map[string]string{"memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64()), "disk": fmt.Sprintf("%d", data.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64())}), - 1091: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", data.AccessConfiguration.MasterAccessList.ValueString())}), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 92, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 92, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 92, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *MongodbResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MongodbModel - var state MongodbModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 1092: resources_core.BuildJSON(map[string]string{"disk": fmt.Sprintf("%d", plan.ClusterConfiguration.Disk.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1093: resources_core.BuildJSON(map[string]string{"masterIpSpace": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterIpSpace.ValueString()), "masterAccessList": fmt.Sprintf("\"%s\"", plan.AccessConfiguration.MasterAccessList.ValueString())}), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 92, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MongodbModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *MongodbResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *MongodbResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/92_mongodb_user_resource.go b/generated/test/go/92_mongodb_user_resource.go deleted file mode 100644 index 6f0e40d..0000000 --- a/generated/test/go/92_mongodb_user_resource.go +++ /dev/null @@ -1,364 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: mongodb -// Subresource: user - -var _ resource.Resource = &MongodbUserResource{} - -// MongodbUserResource manages a subresource via operations. -type MongodbUserResource struct { - client *core.UniversalClient -} - -type MongodbUserModel struct { - ID types.String `tfsdk:"id"` - MongodbID types.String `tfsdk:"mongodb_id"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - SkipMissingOnDelete types.Bool `tfsdk:"skip_missing_on_delete"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - DbName types.String `tfsdk:"db_name"` - Role types.String `tfsdk:"role"` - Username types.String `tfsdk:"username"` -} - -func NewMongodbUserResource() resource.Resource { - return &MongodbUserResource{} -} - -func (r *MongodbUserResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_mongodb_user" -} - -func (r *MongodbUserResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "mongodb_id": schema.StringAttribute{ - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - }, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "skip_missing_on_delete": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "db_name": schema.StringAttribute{Required: true}, - "role": schema.StringAttribute{Required: true}, - "username": schema.StringAttribute{Required: true, MarkdownDescription: "Изменение требует пересоздания.", PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}}, - } - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *MongodbUserResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan MongodbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if plan.DbName.IsNull() || plan.DbName.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "db_name is required.") - return - } - if plan.Role.IsNull() || plan.Role.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "role is required.") - return - } - if plan.Username.IsNull() || plan.Username.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "username is required.") - return - } - - instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - adoptExistingOnCreate := !plan.AdoptExistingOnCreate.IsNull() && !plan.AdoptExistingOnCreate.IsUnknown() && plan.AdoptExistingOnCreate.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - if adoptExistingOnCreate { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Объект уже есть, выполняется усыновление: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - resp.Diagnostics.AddError("Подресурс уже существует", "Найден объект с таким именем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "role": resources_core.FormatString(plan.Role), - }) - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - createErr := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - if createErr != nil && resources_core.IsSubresourceTransientDependencyError(createErr) { - createErr = resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "create_user", params, operationTimeout) - } - if createErr != nil { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс подтверждён в state_out", "Операция create вернула ошибку, но объект найден в state_out и принят в state: "+targetValue) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - if adoptExistingOnCreate && resources_core.IsSubresourceAlreadyExistsError(createErr) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if found || !known { - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.AddWarning("Подресурс уже существует", "Операция вернула duplicate/exist, объект принят в state") - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - } - resp.Diagnostics.AddError("Нарушена консистентность", "Операция вернула duplicate/exist, но объект не найден в state_out") - return - } - resp.Diagnostics.AddError("Ошибка клиента", createErr.Error()) - return - } - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция create завершилась успешно, но объект не найден в state_out: "+targetValue) - return - } - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MongodbUserResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *MongodbUserResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan MongodbUserModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - - if "modify_user" == "" { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if plan.ID.IsNull() || plan.ID.IsUnknown() { - plan.ID = state.ID - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - instanceUID := strings.TrimSpace(plan.MongodbID.ValueString()) - if instanceUID == "" { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра") - return - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(plan.Username), - "dbName": resources_core.FormatString(plan.DbName), - "role": resources_core.FormatString(plan.Role), - }) - - idParams := map[string]string{ - "username": resources_core.FormatString(plan.Username), - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "modify_user", params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = types.StringValue(resources_core.BuildSubresourceID(instanceUID, "user", idParams)) - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) -} - -func (r *MongodbUserResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state MongodbUserModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - if "delete_user" == "" { - return - } - - instanceUID := strings.TrimSpace(state.MongodbID.ValueString()) - if instanceUID == "" { - return - } - skipMissingOnDelete := !state.SkipMissingOnDelete.IsNull() && !state.SkipMissingOnDelete.IsUnknown() && state.SkipMissingOnDelete.ValueBool() - - idParams := map[string]string{ - "username": resources_core.FormatString(state.Username), - } - identityCodes := []string{ - "username", - } - listKey, idKey := resources_core.ResolveSubresourceStateKeys("user", identityCodes) - targetValue := strings.TrimSpace(idParams[idKey]) - if targetValue == "" { - for _, code := range identityCodes { - candidate := strings.TrimSpace(idParams[code]) - if candidate == "" { - continue - } - targetValue = candidate - idKey = code - break - } - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && !found { - resp.Diagnostics.AddWarning("Подресурс не найден", "Объект уже удалён, пропускаем: "+targetValue) - return - } - } - - params := resources_core.CompactParams(map[string]string{ - "username": resources_core.FormatString(state.Username), - }) - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.RunOperationByCodeWithTimeout(ctx, r.client, instanceUID, "delete_user", params, operationTimeout); err != nil { - if resources_core.IsSubresourceMissingError(err) { - if targetValue != "" { - found, known, checkErr := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if checkErr != nil { - resp.Diagnostics.AddError("Ошибка клиента", checkErr.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete вернула not found, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } - if skipMissingOnDelete { - resp.Diagnostics.AddWarning("Подресурс не найден", "Операция delete вернула not found, объект отсутствует в state_out, удаление пропущено") - return - } - } - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - if targetValue != "" { - found, known, err := resources_core.FindSubresourceInStateOut(ctx, r.client, instanceUID, listKey, idKey, targetValue) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if known && found { - resp.Diagnostics.AddError("Нарушена консистентность", "Операция delete завершилась успешно, но объект всё ещё присутствует в state_out: "+targetValue) - return - } - } -} - -func (r *MongodbUserResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/93_rabbitmq_resource.go b/generated/test/go/93_rabbitmq_resource.go deleted file mode 100644 index a6dbba5..0000000 --- a/generated/test/go/93_rabbitmq_resource.go +++ /dev/null @@ -1,462 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: rabbitmq -// Service ID: 93 - -var _ resource.Resource = &RabbitmqResource{} -var _ resource.ResourceWithModifyPlan = &RabbitmqResource{} -var _ resource.ResourceWithImportState = &RabbitmqResource{} - -type RabbitmqResource struct { - client *core.UniversalClient -} - -type RabbitmqModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - IpSpaceNameMaster types.String `tfsdk:"ip_space_name_master"` - NeedExternalAddressMaster types.Bool `tfsdk:"need_external_address_master"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewRabbitmqResource() resource.Resource { - return &RabbitmqResource{} -} - -func (r *RabbitmqResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_rabbitmq" -} - -func (r *RabbitmqResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "ip_space_name_master": schema.StringAttribute{Optional: true, MarkdownDescription: "Пример: `internet-no-antiddos-v1`"}, - "need_external_address_master": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false), MarkdownDescription: "Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`"}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1000), MarkdownDescription: "Пример: `2000`"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(30), MarkdownDescription: "Пример: `20`"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Пример: `1`"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1124), MarkdownDescription: "Пример: `2048`"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Выбрать платформу из списка Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *RabbitmqResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *RabbitmqModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 116: resources_core.FormatInt64(config.ResourceCPU), - 117: resources_core.FormatInt64(config.ResourceMemory), - 118: resources_core.FormatInt64(config.ResourceDisk), - 119: resources_core.FormatString(config.ResourceRealm), - 224: resources_core.FormatInt64(config.ResourceInstances), - 225: resources_core.FormatBool(config.NeedExternalAddressMaster), - 543: resources_core.FormatString(config.IpSpaceNameMaster), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 93, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *RabbitmqResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 116: resources_core.FormatInt64(data.ResourceCPU), - 117: resources_core.FormatInt64(data.ResourceMemory), - 118: resources_core.FormatInt64(data.ResourceDisk), - 119: resources_core.FormatString(data.ResourceRealm), - 224: resources_core.FormatInt64(data.ResourceInstances), - 225: resources_core.FormatBool(data.NeedExternalAddressMaster), - 543: resources_core.FormatString(data.IpSpaceNameMaster), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 93, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 93, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RabbitmqResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 93, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *RabbitmqResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan RabbitmqModel - var state RabbitmqModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.NeedExternalAddressMaster.IsNull() != state.NeedExternalAddressMaster.IsNull() || plan.NeedExternalAddressMaster.IsUnknown() != state.NeedExternalAddressMaster.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.NeedExternalAddressMaster.IsNull() && !plan.NeedExternalAddressMaster.IsUnknown() && !state.NeedExternalAddressMaster.IsNull() && !state.NeedExternalAddressMaster.IsUnknown() { - if plan.NeedExternalAddressMaster.ValueBool() != state.NeedExternalAddressMaster.ValueBool() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IpSpaceNameMaster.IsNull() != state.IpSpaceNameMaster.IsNull() || plan.IpSpaceNameMaster.IsUnknown() != state.IpSpaceNameMaster.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IpSpaceNameMaster.IsNull() && !plan.IpSpaceNameMaster.IsUnknown() && !state.IpSpaceNameMaster.IsNull() && !state.IpSpaceNameMaster.IsUnknown() { - if !strings.EqualFold(plan.IpSpaceNameMaster.ValueString(), state.IpSpaceNameMaster.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 304: resources_core.FormatInt64(plan.ResourceCPU), - 305: resources_core.FormatInt64(plan.ResourceMemory), - 306: resources_core.FormatInt64(plan.ResourceDisk), - 307: resources_core.FormatInt64(plan.ResourceInstances), - 308: resources_core.FormatBool(plan.NeedExternalAddressMaster), - 545: resources_core.FormatString(plan.IpSpaceNameMaster), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 93, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "ipSpaceNameMaster", Field: "IpSpaceNameMaster", Type: "string"}, - {Code: "needExternalAddressMaster", Field: "NeedExternalAddressMaster", Type: "bool"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *RabbitmqResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state RabbitmqModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *RabbitmqResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *RabbitmqResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/94_lucee_resource.go b/generated/test/go/94_lucee_resource.go deleted file mode 100644 index 4f1a93b..0000000 --- a/generated/test/go/94_lucee_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: lucee -// Service ID: 94 - -var _ resource.Resource = &LuceeResource{} -var _ resource.ResourceWithModifyPlan = &LuceeResource{} -var _ resource.ResourceWithImportState = &LuceeResource{} - -type LuceeResource struct { - client *core.UniversalClient -} - -// LuceeAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type LuceeAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// LuceeAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type LuceeAppConfigurationModel struct { - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` - Version types.String `tfsdk:"version" json:"version"` -} - -// LuceeClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type LuceeClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// LuceeStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type LuceeStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type LuceeModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *LuceeAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *LuceeAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *LuceeClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *LuceeStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewLuceeResource() resource.Resource { - return &LuceeResource{} -} - -func (r *LuceeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_lucee" -} - -func (r *LuceeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("lucee-k7rjv4db"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testlucee"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("6.0")}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *LuceeResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *LuceeModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1019: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1020: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1021: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1022: resources_core.BuildJSON(map[string]string{"gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString()), "version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString())}), - 1023: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 94, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *LuceeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1019: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1020: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1021: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1022: resources_core.BuildJSON(map[string]string{"gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString()), "version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString())}), - 1023: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 94, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 94, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LuceeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 94, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *LuceeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan LuceeModel - var state LuceeModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1024: resources_core.BuildJSON(map[string]string{"replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64()), "cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64())}), - 1025: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1026: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 94, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *LuceeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state LuceeModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *LuceeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *LuceeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/95_nodejs_resource.go b/generated/test/go/95_nodejs_resource.go deleted file mode 100644 index 8822a79..0000000 --- a/generated/test/go/95_nodejs_resource.go +++ /dev/null @@ -1,449 +0,0 @@ -package resources_gen - -import ( - "context" - "fmt" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodejs -// Service ID: 95 - -var _ resource.Resource = &NodejsResource{} -var _ resource.ResourceWithModifyPlan = &NodejsResource{} -var _ resource.ResourceWithImportState = &NodejsResource{} - -type NodejsResource struct { - client *core.UniversalClient -} - -// NodejsAccessConfigurationModel — вложенная модель для map-fixed параметра accessConfiguration. -type NodejsAccessConfigurationModel struct { - Domain types.String `tfsdk:"domain" json:"domain"` -} - -// NodejsAppConfigurationModel — вложенная модель для map-fixed параметра appConfiguration. -type NodejsAppConfigurationModel struct { - Version types.String `tfsdk:"version" json:"version"` - GitPath types.String `tfsdk:"git_path" json:"gitPath"` - HealthPath types.String `tfsdk:"health_path" json:"healthPath"` -} - -// NodejsClusterConfigurationModel — вложенная модель для map-fixed параметра clusterConfiguration. -type NodejsClusterConfigurationModel struct { - Cpu types.Int64 `tfsdk:"cpu" json:"cpu"` - Memory types.Int64 `tfsdk:"memory" json:"memory"` - Replicas types.Int64 `tfsdk:"replicas" json:"replicas"` -} - -// NodejsStartupConfigurationModel — вложенная модель для map-fixed параметра startupConfiguration. -type NodejsStartupConfigurationModel struct { - ResourceRealm types.String `tfsdk:"resource_realm" json:"resourceRealm"` -} - -type NodejsModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - AccessConfiguration *NodejsAccessConfigurationModel `tfsdk:"access_configuration"` - AppConfiguration *NodejsAppConfigurationModel `tfsdk:"app_configuration"` - ClusterConfiguration *NodejsClusterConfigurationModel `tfsdk:"cluster_configuration"` - JsonEnv types.String `tfsdk:"json_env"` - StartupConfiguration *NodejsStartupConfigurationModel `tfsdk:"startup_configuration"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNodejsResource() resource.Resource { - return &NodejsResource{} -} - -func (r *NodejsResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodejs" -} - -func (r *NodejsResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "access_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("node-evwflsbl"), MarkdownDescription: "Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне"}, - }}, - "app_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "version": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("23")}, - "git_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("https://github.com/xahys/testnode.git"), MarkdownDescription: "Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников"}, - "health_path": schema.StringAttribute{Optional: true, MarkdownDescription: "Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту"}, - }}, - "cluster_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "cpu": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Указывается в Milicores
`1000` Mili == `1` Ядро"}, - "memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(256), MarkdownDescription: "Указывается в Megabytes"}, - "replicas": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1)}, - }}, - "json_env": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ"}, - "startup_configuration": schema.SingleNestedAttribute{Required: true, - Attributes: map[string]schema.Attribute{ - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Кластер Kubernetes, на котором будет развернут экземпляр"}, - }}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NodejsResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NodejsModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 1028: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", config.StartupConfiguration.ResourceRealm.ValueString())}), - 1029: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", config.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", config.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", config.ClusterConfiguration.Replicas.ValueInt64())}), - 1030: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", config.AccessConfiguration.Domain.ValueString())}), - 1031: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", config.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", config.AppConfiguration.HealthPath.ValueString())}), - 1032: resources_core.FormatString(config.JsonEnv), - } - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 95, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NodejsResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 1028: resources_core.BuildJSON(map[string]string{"resourceRealm": fmt.Sprintf("\"%s\"", data.StartupConfiguration.ResourceRealm.ValueString())}), - 1029: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", data.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", data.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", data.ClusterConfiguration.Replicas.ValueInt64())}), - 1030: resources_core.BuildJSON(map[string]string{"domain": fmt.Sprintf("\"%s\"", data.AccessConfiguration.Domain.ValueString())}), - 1031: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", data.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", data.AppConfiguration.HealthPath.ValueString())}), - 1032: resources_core.FormatString(data.JsonEnv), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 95, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 95, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NodejsResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 95, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NodejsResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NodejsModel - var state NodejsModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.JsonEnv.IsNull() != state.JsonEnv.IsNull() || plan.JsonEnv.IsUnknown() != state.JsonEnv.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 1033: resources_core.BuildJSON(map[string]string{"cpu": fmt.Sprintf("%d", plan.ClusterConfiguration.Cpu.ValueInt64()), "memory": fmt.Sprintf("%d", plan.ClusterConfiguration.Memory.ValueInt64()), "replicas": fmt.Sprintf("%d", plan.ClusterConfiguration.Replicas.ValueInt64())}), - 1034: resources_core.BuildJSON(map[string]string{"version": fmt.Sprintf("\"%s\"", plan.AppConfiguration.Version.ValueString()), "gitPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.GitPath.ValueString()), "healthPath": fmt.Sprintf("\"%s\"", plan.AppConfiguration.HealthPath.ValueString())}), - 1035: resources_core.FormatString(plan.JsonEnv), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 95, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "accessConfiguration", Field: "AccessConfiguration", Type: "map-fixed"}, - {Code: "appConfiguration", Field: "AppConfiguration", Type: "map-fixed"}, - {Code: "clusterConfiguration", Field: "ClusterConfiguration", Type: "map-fixed"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "startupConfiguration", Field: "StartupConfiguration", Type: "map-fixed"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NodejsResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NodejsModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NodejsResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NodejsResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/96_pgadmin_resource.go b/generated/test/go/96_pgadmin_resource.go deleted file mode 100644 index 661b70b..0000000 --- a/generated/test/go/96_pgadmin_resource.go +++ /dev/null @@ -1,515 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: pgadmin -// Service ID: 96 - -var _ resource.Resource = &PgadminResource{} -var _ resource.ResourceWithModifyPlan = &PgadminResource{} -var _ resource.ResourceWithImportState = &PgadminResource{} - -type PgadminResource struct { - client *core.UniversalClient -} - -type PgadminModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Domain types.String `tfsdk:"domain"` - Login types.String `tfsdk:"login"` - Password types.String `tfsdk:"password"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewPgadminResource() resource.Resource { - return &PgadminResource{} -} - -func (r *PgadminResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_pgadmin" -} - -func (r *PgadminResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("pgadmin-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "login": schema.StringAttribute{Required: true, MarkdownDescription: "Логин для подключения к pgAdmin, в формате email (login@email.net) Не изменяется после создания."}, - "password": schema.StringAttribute{Required: true, MarkdownDescription: "Пароль для подключения к pgAdmin Не изменяется после создания.", Sensitive: true}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (milicores). 1000Mili = 1 Ядро"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (MBytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *PgadminResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *PgadminModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Login.IsNull() && !plan.Login.IsUnknown() && !state.Login.IsNull() && !state.Login.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Login.ValueString(), state.Login.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить login", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.Password.IsNull() && !plan.Password.IsUnknown() && !state.Password.IsNull() && !state.Password.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Password.ValueString(), state.Password.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить password", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.Login.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "login is required.") - return - } - if config.Login.IsUnknown() { - return - } - if config.Password.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "password is required.") - return - } - if config.Password.IsUnknown() { - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 164: resources_core.FormatString(config.Domain), - 165: resources_core.FormatInt64(config.ResourceCPU), - 166: resources_core.FormatInt64(config.ResourceMemory), - 167: resources_core.FormatInt64(config.ResourceDisk), - 169: resources_core.FormatString(config.ResourceRealm), - 170: resources_core.FormatString(config.Login), - 171: resources_core.FormatString(config.Password), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 96, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *PgadminResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.Login.IsNull() || data.Login.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "login is required.") - return - } - if data.Password.IsNull() || data.Password.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "password is required.") - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 164: resources_core.FormatString(data.Domain), - 165: resources_core.FormatInt64(data.ResourceCPU), - 166: resources_core.FormatInt64(data.ResourceMemory), - 167: resources_core.FormatInt64(data.ResourceDisk), - 169: resources_core.FormatString(data.ResourceRealm), - 170: resources_core.FormatString(data.Login), - 171: resources_core.FormatString(data.Password), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 96, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 96, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PgadminResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 96, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *PgadminResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan PgadminModel - var state PgadminModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 172: resources_core.FormatInt64(plan.ResourceCPU), - 173: resources_core.FormatInt64(plan.ResourceMemory), - 174: resources_core.FormatInt64(plan.ResourceDisk), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 96, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "login", Field: "Login", Type: "string"}, - {Code: "password", Field: "Password", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *PgadminResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state PgadminModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *PgadminResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *PgadminResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/97_nodered_resource.go b/generated/test/go/97_nodered_resource.go deleted file mode 100644 index f8b17d5..0000000 --- a/generated/test/go/97_nodered_resource.go +++ /dev/null @@ -1,431 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: nodered -// Service ID: 97 - -var _ resource.Resource = &NoderedResource{} -var _ resource.ResourceWithModifyPlan = &NoderedResource{} -var _ resource.ResourceWithImportState = &NoderedResource{} - -type NoderedResource struct { - client *core.UniversalClient -} - -type NoderedModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewNoderedResource() resource.Resource { - return &NoderedResource{} -} - -func (r *NoderedResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_nodered" -} - -func (r *NoderedResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nodered-3"), MarkdownDescription: "Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро Не изменяется после создания."}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes) Не изменяется после создания."}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов Не изменяется после создания."}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes) Не изменяется после создания."}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *NoderedResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *NoderedModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_c_p_u", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_disk", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_instances", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - resp.Diagnostics.AddError("Нельзя изменить resource_memory", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 193: resources_core.FormatInt64(config.ResourceCPU), - 194: resources_core.FormatInt64(config.ResourceMemory), - 195: resources_core.FormatInt64(config.ResourceDisk), - 196: resources_core.FormatString(config.ResourceRealm), - 197: resources_core.FormatString(config.Domain), - 323: resources_core.FormatInt64(config.ResourceInstances), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 97, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *NoderedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 193: resources_core.FormatInt64(data.ResourceCPU), - 194: resources_core.FormatInt64(data.ResourceMemory), - 195: resources_core.FormatInt64(data.ResourceDisk), - 196: resources_core.FormatString(data.ResourceRealm), - 197: resources_core.FormatString(data.Domain), - 323: resources_core.FormatInt64(data.ResourceInstances), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 97, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 97, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NoderedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 97, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *NoderedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan NoderedModel - var state NoderedModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{} - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 97, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *NoderedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state NoderedModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "delete" - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *NoderedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *NoderedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/98_http_resource.go b/generated/test/go/98_http_resource.go deleted file mode 100644 index 5fe21d9..0000000 --- a/generated/test/go/98_http_resource.go +++ /dev/null @@ -1,509 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: http -// Service ID: 98 - -var _ resource.Resource = &HttpResource{} -var _ resource.ResourceWithModifyPlan = &HttpResource{} -var _ resource.ResourceWithImportState = &HttpResource{} - -type HttpResource struct { - client *core.UniversalClient -} - -type HttpModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - // --- Redeploy support (ARCHITECTURE.md) --- - // git_revision: при изменении вызывает redeploy вместо modify. - // Опциональное поле — если не задано, modify работает как обычно. - GitRevision types.String `tfsdk:"git_revision"` - Domain types.String `tfsdk:"domain"` - IngressAnnotations types.String `tfsdk:"ingress_annotations"` - JsonEnv types.String `tfsdk:"json_env"` - RegistryPath types.String `tfsdk:"registry_path"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - ResourceRealm types.String `tfsdk:"resource_realm"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewHttpResource() resource.Resource { - return &HttpResource{} -} - -func (r *HttpResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_http" -} - -func (r *HttpResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("http-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_annotations": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("{}"), MarkdownDescription: "Формат ключ/значение
Поддерживает пока простые аннотации Не изменяется после создания."}, - "json_env": schema.StringAttribute{Optional: true, MarkdownDescription: "Массив переменных, будут переданы в env приложения Не изменяется после создания."}, - "registry_path": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("nexus-sa.tst.nubes.ru/docker-nubes/jolt"), MarkdownDescription: "Путь до image, который будет установлен как deployment Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(100), MarkdownDescription: "Квота ядра пода (Указывается в mili)"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(200), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "resource_realm": schema.StringAttribute{Required: true, MarkdownDescription: "Платформа для развертывания Не изменяется после создания."}, - // --- Redeploy support (ARCHITECTURE.md) --- - // При изменении git_revision вызывается redeploy вместо modify. - // Если не задано — modify работает без изменений. - "git_revision": schema.StringAttribute{Optional: true, MarkdownDescription: "Git revision (commit hash/tag). Changing this triggers redeploy instead of modify."}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *HttpResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *HttpModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.IngressAnnotations.IsNull() && !plan.IngressAnnotations.IsUnknown() && !state.IngressAnnotations.IsNull() && !state.IngressAnnotations.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.IngressAnnotations.ValueString(), state.IngressAnnotations.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить ingress_annotations", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.JsonEnv.IsNull() && !plan.JsonEnv.IsUnknown() && !state.JsonEnv.IsNull() && !state.JsonEnv.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.JsonEnv.ValueString(), state.JsonEnv.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить json_env", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.RegistryPath.IsNull() && !plan.RegistryPath.IsUnknown() && !state.RegistryPath.IsNull() && !state.RegistryPath.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.RegistryPath.ValueString(), state.RegistryPath.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить registry_path", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.ResourceRealm.IsNull() && !plan.ResourceRealm.IsUnknown() && !state.ResourceRealm.IsNull() && !state.ResourceRealm.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.ResourceRealm.ValueString(), state.ResourceRealm.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить resource_realm", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.ResourceRealm.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - if config.ResourceRealm.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - params := map[int]string{ - 212: resources_core.FormatString(config.Domain), - 213: resources_core.FormatInt64(config.ResourceCPU), - 214: resources_core.FormatInt64(config.ResourceMemory), - 215: resources_core.FormatInt64(config.ResourceInstances), - 216: resources_core.FormatString(config.ResourceRealm), - 217: resources_core.FormatString(config.RegistryPath), - 241: resources_core.FormatString(config.JsonEnv), - 782: resources_core.FormatString(config.IngressAnnotations), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 98, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *HttpResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.ResourceRealm.IsNull() || data.ResourceRealm.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "resource_realm is required.") - return - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 212: resources_core.FormatString(data.Domain), - 213: resources_core.FormatInt64(data.ResourceCPU), - 214: resources_core.FormatInt64(data.ResourceMemory), - 215: resources_core.FormatInt64(data.ResourceInstances), - 216: resources_core.FormatString(data.ResourceRealm), - 217: resources_core.FormatString(data.RegistryPath), - 241: resources_core.FormatString(data.JsonEnv), - 782: resources_core.FormatString(data.IngressAnnotations), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 98, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 98, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HttpResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 98, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *HttpResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan HttpModel - var state HttpModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - // --- Redeploy support (ARCHITECTURE.md) --- - // Проверяем изменился ли git_revision. - // Если да — вызываем redeploy вместо modify. - // git_revision = "" (не задано) → обычный modify. - redeployRequested := false - if !plan.GitRevision.IsNull() && !plan.GitRevision.IsUnknown() { - if state.GitRevision.IsNull() || state.GitRevision.IsUnknown() || plan.GitRevision.ValueString() != state.GitRevision.ValueString() { - redeployRequested = true - } - } - - if !hasServiceParamChanges && !redeployRequested { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - // --- Redeploy support (ARCHITECTURE.md) --- - if redeployRequested { - // Вызываем redeploy — пустой params (операция не требует CFS-параметров) - if err := r.client.RunRedeployOperation(ctx, instanceID.ValueString(), operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } else { - params := map[int]string{ - 218: resources_core.FormatInt64(plan.ResourceCPU), - 219: resources_core.FormatInt64(plan.ResourceMemory), - 220: resources_core.FormatInt64(plan.ResourceInstances), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 98, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressAnnotations", Field: "IngressAnnotations", Type: "string"}, - {Code: "jsonEnv", Field: "JsonEnv", Type: "string"}, - {Code: "registryPath", Field: "RegistryPath", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - {Code: "resourceRealm", Field: "ResourceRealm", Type: "string"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *HttpResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state HttpModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *HttpResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *HttpResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/99_gitea_resource.go b/generated/test/go/99_gitea_resource.go deleted file mode 100644 index 0e8a084..0000000 --- a/generated/test/go/99_gitea_resource.go +++ /dev/null @@ -1,579 +0,0 @@ -package resources_gen - -import ( - "context" - "strings" - - "terraform-provider-nubes/internal/core" - "terraform-provider-nubes/internal/resources_core" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -// Code generated by tools/gen_v2. DO NOT EDIT. -// Service: gitea -// Service ID: 99 - -var _ resource.Resource = &GiteaResource{} -var _ resource.ResourceWithModifyPlan = &GiteaResource{} -var _ resource.ResourceWithImportState = &GiteaResource{} - -type GiteaResource struct { - client *core.UniversalClient -} - -type GiteaModel struct { - ID types.String `tfsdk:"id"` - ResourceName types.String `tfsdk:"resource_name"` - OperationTimeout types.String `tfsdk:"operation_timeout"` - LogLevel types.String `tfsdk:"log_level"` - Domain types.String `tfsdk:"domain"` - IngressExtraAnnotations types.String `tfsdk:"ingress_extra_annotations"` - IniServerParams types.String `tfsdk:"ini_server_params"` - PsqlUid types.String `tfsdk:"psql_uid"` - ResourceCPU types.Int64 `tfsdk:"resource_c_p_u"` - ResourceDisk types.Int64 `tfsdk:"resource_disk"` - ResourceInstances types.Int64 `tfsdk:"resource_instances"` - ResourceMemory types.Int64 `tfsdk:"resource_memory"` - SuspendOnDestroy types.Bool `tfsdk:"suspend_on_destroy"` - AdoptExistingOnCreate types.Bool `tfsdk:"adopt_existing_on_create"` - StateParams types.Map `tfsdk:"state_params"` - StateOut types.Map `tfsdk:"state_out"` - StateParamsFlat types.Map `tfsdk:"state_params_flat"` - StateOutFlat types.Map `tfsdk:"state_out_flat"` - VaultSecrets types.Map `tfsdk:"vault_secrets"` - VaultUrl types.String `tfsdk:"vault_url"` - VaultUserPath types.String `tfsdk:"vault_user_path"` - VaultFields types.List `tfsdk:"vault_fields"` -} - -func NewGiteaResource() resource.Resource { - return &GiteaResource{} -} - -func (r *GiteaResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_gitea" -} - -func (r *GiteaResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - attrs := map[string]schema.Attribute{ - "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, - "resource_name": schema.StringAttribute{Required: true}, - "operation_timeout": schema.StringAttribute{Optional: true}, - "log_level": schema.StringAttribute{Optional: true, MarkdownDescription: "Operation stages log level: none (default), info, debug. Overrides provider-level log_level."}, - "domain": schema.StringAttribute{Optional: true, Computed: true, Default: stringdefault.StaticString("gitea-1"), MarkdownDescription: "Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm Не изменяется после создания."}, - "ingress_extra_annotations": schema.StringAttribute{Optional: true}, - "ini_server_params": schema.StringAttribute{Optional: true}, - "psql_uid": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически Не изменяется после создания."}, - "resource_c_p_u": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(500), MarkdownDescription: "Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро"}, - "resource_disk": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Квота диска (Указывается в GIGAbytes)"}, - "resource_instances": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(1), MarkdownDescription: "Количество узлов"}, - "resource_memory": schema.Int64Attribute{Optional: true, Computed: true, Default: int64default.StaticInt64(512), MarkdownDescription: "Квота памяти пода (Указывается в Megabytes)"}, - "suspend_on_destroy": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(true)}, - "adopt_existing_on_create": schema.BoolAttribute{Optional: true, Computed: true, Default: booldefault.StaticBool(false)}, - "state_params": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_params_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "state_out_flat": schema.MapAttribute{Computed: true, ElementType: types.StringType}, - "vault_secrets": schema.MapAttribute{Computed: true, ElementType: types.StringType, Sensitive: true}, - "vault_url": schema.StringAttribute{Computed: true}, - "vault_user_path": schema.StringAttribute{Computed: true}, - "vault_fields": schema.ListAttribute{Computed: true, ElementType: types.StringType}, - } - - resp.Schema = schema.Schema{Attributes: attrs} -} - -func (r *GiteaResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - if r.client == nil { - return - } - - var config *GiteaModel - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { - return - } - if config == nil { - return - } - - var state *GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if req.State.Raw.IsNull() && req.Plan.Raw.IsNull() { - return - } - - if state != nil && !state.ID.IsNull() && !state.ID.IsUnknown() { - var plan GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { - return - } - // FIX(uuid-case): resolve ref_svc params НЕ делаем в ModifyPlan. - // Terraform правило: plan ОБЯЗАН равняться config для user-provided атрибутов. - // Resolve (displayName → UUID или uppercase → lowercase) нужен только для - // API-вызова в Create/Update. Делать его здесь = менять plan = ошибка - // "Provider produced invalid plan: planned value does not match config value". - if !plan.ResourceName.IsNull() && !plan.ResourceName.IsUnknown() && !state.ResourceName.IsNull() && !state.ResourceName.IsUnknown() { - if plan.ResourceName.ValueString() != state.ResourceName.ValueString() { - resp.Diagnostics.AddError("Нельзя изменить resource_name", "Параметр resource_name задается при создании и не может быть изменен. Создайте новый ресурс с другим именем.") - return - } - } - if !plan.Domain.IsNull() && !plan.Domain.IsUnknown() && !state.Domain.IsNull() && !state.Domain.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.Domain.ValueString(), state.Domain.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить domain", "Параметр задается при создании и не может быть изменен.") - return - } - } - if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - // FIX(uuid-case): сравниваем без учёта регистра — API возвращает UUID - // в lowercase, пользователь мог написать upper/mixed. Это одно и то же - // значение, менять его нельзя только если оно реально другое. - if !strings.EqualFold(plan.PsqlUid.ValueString(), state.PsqlUid.ValueString()) { - resp.Diagnostics.AddError("Нельзя изменить psql_uid", "Параметр задается при создании и не может быть изменен.") - return - } - } - return - } - if config.PsqlUid.IsNull() { - resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") - return - } - if config.PsqlUid.IsUnknown() { - return - } - - if config.ResourceName.IsNull() || config.ResourceName.IsUnknown() { - return - } - adoptExistingOnCreate := false - if !config.AdoptExistingOnCreate.IsNull() && !config.AdoptExistingOnCreate.IsUnknown() { - adoptExistingOnCreate = config.AdoptExistingOnCreate.ValueBool() - } - if !config.PsqlUid.IsNull() && !config.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, config.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != config.PsqlUid.ValueString() { - config.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - params := map[int]string{ - 232: resources_core.FormatInt64(config.ResourceCPU), - 233: resources_core.FormatInt64(config.ResourceMemory), - 234: resources_core.FormatInt64(config.ResourceDisk), - 235: resources_core.FormatInt64(config.ResourceInstances), - 237: resources_core.FormatString(config.Domain), - 238: resources_core.FormatString(config.PsqlUid), - 818: resources_core.FormatString(config.IngressExtraAnnotations), - 821: resources_core.FormatString(config.IniServerParams), - } - desiredDomain := "" - if !config.Domain.IsNull() && !config.Domain.IsUnknown() { - desiredDomain = config.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - - resp.Diagnostics.Append(resources_core.PlanExistingResourceDiagnosticsWithParamsAndDomainAndServices(ctx, r.client, 99, config.ResourceName.ValueString(), adoptExistingOnCreate, params, desiredDomain, domainServiceIDs)...) -} - -func (r *GiteaResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - if data.PsqlUid.IsNull() || data.PsqlUid.IsUnknown() { - resp.Diagnostics.AddError("Missing required attribute", "psql_uid is required.") - return - } - // ══════════════════════════════════════════════════════════════════════════ - // ПРАВИЛО TERRAFORM (официальная документация): - // «If an attribute value is configured, it is NEVER valid to change that - // value in the plan.» — то есть plan ОБЯЗАН равняться config (= тому что - // написал пользователь). Менять plan запрещено на уровне фреймворка. - // - // ПРОБЛЕМА: API нашего облака возвращает UUID в нижнем регистре. - // Пользователь пишет: vapp_uid = "6214BA32-..." (верхний или смешанный). - // После apply API вернул: "6214ba32-..." → state != plan → Terraform кричит: - // «Provider produced inconsistent result after apply». - // - // РЕШЕНИЕ: корректировать STATE под PLAN, а не наоборот. - // Шаг 1 (здесь): сохраняем оригинальное значение из plan ДО того как - // ResolveRefSvcParamValue переведёт UUID в нижний регистр (нужен для API). - // Шаг 2 (ниже, после RefreshResourceState): восстанавливаем оригинальный - // регистр в state через strings.EqualFold (сравниваем без учёта регистра). - // ══════════════════════════════════════════════════════════════════════════ - originalPsqlUid := data.PsqlUid - if !data.PsqlUid.IsNull() && !data.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, data.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if resolvedPsqlUid != "" && resolvedPsqlUid != data.PsqlUid.ValueString() { - data.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - - resourceName := data.ResourceName.ValueString() - desiredDomain := "" - if !data.Domain.IsNull() && !data.Domain.IsUnknown() { - desiredDomain = data.Domain.ValueString() - } - domainServiceIDs := []int{81, 82, 88, 96, 97, 98, 99, 119, 149, 151} - resp.Diagnostics.Append(resources_core.CreateExistingResourceDiagnosticsWithDomainAndServices(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), desiredDomain, domainServiceIDs)...) - // ⛔ Проверяем HasError ДО create — при hard-error (running без adopt, suspend без adopt, - // not created, конфликт) сайд-эффект create не должен выполняться. - if resp.Diagnostics.HasError() { - return - } - - params := map[int]string{ - 232: resources_core.FormatInt64(data.ResourceCPU), - 233: resources_core.FormatInt64(data.ResourceMemory), - 234: resources_core.FormatInt64(data.ResourceDisk), - 235: resources_core.FormatInt64(data.ResourceInstances), - 237: resources_core.FormatString(data.Domain), - 238: resources_core.FormatString(data.PsqlUid), - 818: resources_core.FormatString(data.IngressExtraAnnotations), - 821: resources_core.FormatString(data.IniServerParams), - } - - operationTimeout := "" - if !data.OperationTimeout.IsNull() && !data.OperationTimeout.IsUnknown() { - operationTimeout = data.OperationTimeout.ValueString() - } - if !data.LogLevel.IsNull() && !data.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, data.LogLevel.ValueString()) - } - id, err := resources_core.CreateResourceWithTimeout(ctx, r.client, 99, resourceName, data.AdoptExistingOnCreate.ValueBool(), params, operationTimeout) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - data.ID = types.StringValue(id) - - state, diags := resources_core.RefreshResourceState(ctx, r.client, id, 99, data, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { - state.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Create). - // EqualFold = «совпадают ли значения без учёта регистра?» - // Если да — значит API вернул «туже» строку, только в другом регистре. - // Заменяем state на original (то что было в plan/config пользователя). - // Итог: plan=="6214BA32-..." и state=="6214BA32-..." → нет diff → нет taint. - if !originalPsqlUid.IsNull() && !originalPsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - if strings.EqualFold(state.PsqlUid.ValueString(), originalPsqlUid.ValueString()) { - state.PsqlUid = originalPsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - if r.client != nil { - remove, err := resources_core.ShouldRemoveFromState(ctx, r.client, state.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - if remove { - resp.State.RemoveResource(ctx) - return - } - } - - newState, diags := resources_core.RefreshResourceState(ctx, r.client, state.ID.ValueString(), 99, state, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, newState.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != newState.PsqlUid.ValueString() { - newState.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Read). - // При чтении у нас нет plan — но предыдущий state уже хранит значение - // в регистре пользователя (после первого Create оно было восстановлено). - // Берём prior state (переменная state) как эталон регистра. - // Если API вернул то же UUID только строчными буквами — восстанавливаем. - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() && !newState.PsqlUid.IsNull() && !newState.PsqlUid.IsUnknown() { - if strings.EqualFold(newState.PsqlUid.ValueString(), state.PsqlUid.ValueString()) { - newState.PsqlUid = state.PsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) -} - -func (r *GiteaResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan GiteaModel - var state GiteaModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - - instanceID := state.ID - if instanceID.IsNull() || instanceID.IsUnknown() { - instanceID = plan.ID - } - if instanceID.IsNull() || instanceID.IsUnknown() { - resp.Diagnostics.AddError("Ошибка клиента", "отсутствует идентификатор экземпляра для modify") - return - } - - hasServiceParamChanges := false - if !hasServiceParamChanges { - if plan.ResourceCPU.IsNull() != state.ResourceCPU.IsNull() || plan.ResourceCPU.IsUnknown() != state.ResourceCPU.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceCPU.IsNull() && !plan.ResourceCPU.IsUnknown() && !state.ResourceCPU.IsNull() && !state.ResourceCPU.IsUnknown() { - if plan.ResourceCPU.ValueInt64() != state.ResourceCPU.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceMemory.IsNull() != state.ResourceMemory.IsNull() || plan.ResourceMemory.IsUnknown() != state.ResourceMemory.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceMemory.IsNull() && !plan.ResourceMemory.IsUnknown() && !state.ResourceMemory.IsNull() && !state.ResourceMemory.IsUnknown() { - if plan.ResourceMemory.ValueInt64() != state.ResourceMemory.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceDisk.IsNull() != state.ResourceDisk.IsNull() || plan.ResourceDisk.IsUnknown() != state.ResourceDisk.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceDisk.IsNull() && !plan.ResourceDisk.IsUnknown() && !state.ResourceDisk.IsNull() && !state.ResourceDisk.IsUnknown() { - if plan.ResourceDisk.ValueInt64() != state.ResourceDisk.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.ResourceInstances.IsNull() != state.ResourceInstances.IsNull() || plan.ResourceInstances.IsUnknown() != state.ResourceInstances.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.ResourceInstances.IsNull() && !plan.ResourceInstances.IsUnknown() && !state.ResourceInstances.IsNull() && !state.ResourceInstances.IsUnknown() { - if plan.ResourceInstances.ValueInt64() != state.ResourceInstances.ValueInt64() { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IngressExtraAnnotations.IsNull() != state.IngressExtraAnnotations.IsNull() || plan.IngressExtraAnnotations.IsUnknown() != state.IngressExtraAnnotations.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IngressExtraAnnotations.IsNull() && !plan.IngressExtraAnnotations.IsUnknown() && !state.IngressExtraAnnotations.IsNull() && !state.IngressExtraAnnotations.IsUnknown() { - if !strings.EqualFold(plan.IngressExtraAnnotations.ValueString(), state.IngressExtraAnnotations.ValueString()) { - hasServiceParamChanges = true - } - } - } - if !hasServiceParamChanges { - if plan.IniServerParams.IsNull() != state.IniServerParams.IsNull() || plan.IniServerParams.IsUnknown() != state.IniServerParams.IsUnknown() { - hasServiceParamChanges = true - } else if !plan.IniServerParams.IsNull() && !plan.IniServerParams.IsUnknown() && !state.IniServerParams.IsNull() && !state.IniServerParams.IsUnknown() { - if !strings.EqualFold(plan.IniServerParams.ValueString(), state.IniServerParams.ValueString()) { - hasServiceParamChanges = true - } - } - } - - if !hasServiceParamChanges { - plan.ID = instanceID - plan.StateParams = state.StateParams - plan.StateOut = state.StateOut - plan.StateParamsFlat = state.StateParamsFlat - plan.StateOutFlat = state.StateOutFlat - plan.VaultSecrets = state.VaultSecrets - plan.VaultUrl = state.VaultUrl - plan.VaultUserPath = state.VaultUserPath - plan.VaultFields = state.VaultFields - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) - return - } - - operationTimeout := "" - if !plan.OperationTimeout.IsNull() && !plan.OperationTimeout.IsUnknown() { - operationTimeout = plan.OperationTimeout.ValueString() - } - if !plan.LogLevel.IsNull() && !plan.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, plan.LogLevel.ValueString()) - } - params := map[int]string{ - 259: resources_core.FormatInt64(plan.ResourceCPU), - 260: resources_core.FormatInt64(plan.ResourceMemory), - 261: resources_core.FormatInt64(plan.ResourceDisk), - 262: resources_core.FormatInt64(plan.ResourceInstances), - 819: resources_core.FormatString(plan.IngressExtraAnnotations), - 820: resources_core.FormatString(plan.IniServerParams), - } - if err := resources_core.UpdateResourceWithTimeout(ctx, r.client, instanceID.ValueString(), params, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } - - plan.ID = instanceID - state, diags := resources_core.RefreshResourceState(ctx, r.client, instanceID.ValueString(), 99, plan, []resources_core.StateField{ - {Code: "state_params"}, - {Code: "state_out"}, - {Code: "state_params_flat"}, - {Code: "state_out_flat"}, - {Code: "vault_secrets"}, - {Code: "vault_url"}, - {Code: "vault_user_path"}, - {Code: "vault_fields"}, - }, []resources_core.InputField{ - {Code: "domain", Field: "Domain", Type: "string"}, - {Code: "ingressExtraAnnotations", Field: "IngressExtraAnnotations", Type: "string"}, - {Code: "iniServerParams", Field: "IniServerParams", Type: "string"}, - {Code: "psqlUid", Field: "PsqlUid", Type: "string"}, - {Code: "resourceCPU", Field: "ResourceCPU", Type: "int64"}, - {Code: "resourceDisk", Field: "ResourceDisk", Type: "int64"}, - {Code: "resourceInstances", Field: "ResourceInstances", Type: "int64"}, - {Code: "resourceMemory", Field: "ResourceMemory", Type: "int64"}, - }) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - if !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - resolvedPsqlUid, err := r.client.ResolveRefSvcParamValue(ctx, 90, state.PsqlUid.ValueString()) - if err != nil { - resp.Diagnostics.AddWarning("Failed to resolve psql_uid", err.Error()) - } else if resolvedPsqlUid != "" && resolvedPsqlUid != state.PsqlUid.ValueString() { - state.PsqlUid = types.StringValue(resolvedPsqlUid) - } - } - // Restore user-provided casing в state (Update). - // После API-вызова modify state содержит значения в lower-case от API. - // Plan == config == то что написал пользователь (регистр неизменён). - // EqualFold: если UUID совпадает без учёта регистра — берём из plan. - if !plan.PsqlUid.IsNull() && !plan.PsqlUid.IsUnknown() && !state.PsqlUid.IsNull() && !state.PsqlUid.IsUnknown() { - if strings.EqualFold(state.PsqlUid.ValueString(), plan.PsqlUid.ValueString()) { - state.PsqlUid = plan.PsqlUid - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) -} - -func (r *GiteaResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state GiteaModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { - return - } - if state.ID.IsNull() || state.ID.IsUnknown() { - return - } - deleteMode := "state_only" - if !state.SuspendOnDestroy.IsNull() && !state.SuspendOnDestroy.IsUnknown() && state.SuspendOnDestroy.ValueBool() { - deleteMode = "suspend" - } - - operationTimeout := "" - if !state.OperationTimeout.IsNull() && !state.OperationTimeout.IsUnknown() { - operationTimeout = state.OperationTimeout.ValueString() - } - if !state.LogLevel.IsNull() && !state.LogLevel.IsUnknown() { - ctx = core.CtxWithLogLevel(ctx, state.LogLevel.ValueString()) - } - if err := resources_core.DeleteResourceWithTimeout(ctx, r.client, state.ID.ValueString(), deleteMode, operationTimeout); err != nil { - resp.Diagnostics.AddError("Ошибка клиента", err.Error()) - return - } -} - -func (r *GiteaResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} - -func (r *GiteaResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - client, ok := req.ProviderData.(*core.UniversalClient) - if !ok { - resp.Diagnostics.AddError("Error", "Invalid client type") - return - } - r.client = client -} diff --git a/generated/test/go/registry.go b/generated/test/go/registry.go deleted file mode 100644 index e0f02fc..0000000 --- a/generated/test/go/registry.go +++ /dev/null @@ -1,78 +0,0 @@ -package resources_gen - -import "github.com/hashicorp/terraform-plugin-framework/resource" - -// Code generated by tools/gen_v2. DO NOT EDIT. -func AllResources() []func() resource.Resource { - return []func() resource.Resource{ - NewAkhqResource, - NewClickhouseResource, - NewDnsrecordResource, - NewDnszoneResource, - NewDummyResource, - NewFlaskResource, - NewGiteaResource, - NewGiteaComplexResource, - NewHarborResource, - NewHttpResource, - NewK8sOpenbaoResource, - NewK8sSthutrvalClusterResource, - NewK8sVeleroResource, - NewK8sZitiControllerResource, - NewKafkaResource, - NewLlmAiResource, - NewLuceeResource, - NewMariadbResource, - NewMongodbResource, - NewNextcloudResource, - NewNifiResource, - NewNodejsResource, - NewNoderedResource, - NewOpenwhiskResource, - NewPgadminResource, - NewPostgresResource, - NewRabbitmqResource, - NewRedisResource, - NewS3Resource, - NewS3bucketResource, - NewSupersetResource, - NewTemplateResource, - NewTenantResource, - NewValoTenantResource, - NewVappResource, - NewVcComplexResource, - NewVcMgmtSthutrvalClusterResource, - NewVcNsxtResource, - NewVcOrgResource, - NewVcOrgSaasResource, - NewVcVdcResource, - NewVcVdcGroupResource, - NewVcVmResource, - NewVcVmV2Resource, - NewVcVmV3Resource, - NewVcexternalipResource, - NewVmpostgreResource, - NewZonesV2Resource, - NewClickhouseDatabaseResource, - NewClickhouseUserResource, - NewK8sOpenbaoKvResource, - NewK8sOpenbaoPolicyResource, - NewK8sOpenbaoSecretsResource, - NewK8sSthutrvalClusterUserResource, - NewKafkaTopicResource, - NewKafkaUserResource, - NewLlmAiSecretsResource, - NewLlmAiTeamResource, - NewMariadbDatabaseResource, - NewMariadbUserResource, - NewMongodbUserResource, - NewNextcloudBackupResource, - NewPostgresBackupResource, - NewPostgresDatabaseResource, - NewPostgresUserResource, - NewS3SubUserResource, - NewVcOrgUserResource, - NewVcVdcGroupVdcResource, - NewZonesV2RecordResource, - } -} diff --git a/generated/test/resources_yaml/100_openwhisk.yaml b/generated/test/resources_yaml/100_openwhisk.yaml deleted file mode 100644 index fdc2fdc..0000000 --- a/generated/test/resources_yaml/100_openwhisk.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: openwhisk -service_id: 100 -service_display_name: Serverless Openwhisk (openwhisk_tenant) -service_short_name: openwhisk -service_man: Добавление и удаления пользователя для работы с Openwhisk -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: [] diff --git a/generated/test/resources_yaml/109_zones_v2.yaml b/generated/test/resources_yaml/109_zones_v2.yaml deleted file mode 100644 index 7e89d7e..0000000 --- a/generated/test/resources_yaml/109_zones_v2.yaml +++ /dev/null @@ -1,263 +0,0 @@ -name: zones_v2 -service_id: 109 -service_display_name: Управление DNS -service_short_name: zonesV2 -service_man: '# Управление DNS

## Что делает сервис

Управляет DNS-зоной вашего домена в инфраструктуре `ngcloud.ru`.
Сервис помогает:

- создать DNS-зону;
- поддерживать записи в зоне (добавлять, удалять, массово менять);
- удалять зону;
- синхронизировать данные зоны с фактическим состоянием на DNS-серверах.

Сервис работает с авторитетными DNS-серверами зоны:

- `ns3.ngcloud.ru`
- `185.247.187.83`
- `2a10:aa80:0:5::3`
- `ns4.ngcloud.ru`
- `81.22.46.43`
- `2a10:aa80:0:6::3`

## Очень важно: что сделать у регистратора домена

Чтобы зона действительно начала обслуживаться, **нужно делегировать домен** у регистратора на авторитетные DNS-серверы:

- `ns3.ngcloud.ru`
- `ns4.ngcloud.ru`

В панели регистратора нужно заменить NS-серверы домена на значения выше.
Без этого записи в зоне могут быть созданы, но интернет не будет использовать их для вашего домена.

## Проверка после настройки

Проверить ответы от серверов зоны:

```bash
dig <ваш_домен> @ns3.ngcloud.ru
dig <ваш_домен> @ns4.ngcloud.ru
```

Проверить, что делегирование у регистратора применилось:

```bash
dig <ваш_домен> NS
```

В ответе должны быть `ns3.ngcloud.ru` и `ns4.ngcloud.ru`.
' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 254 - kind: instance - action: create - man: '# Операция `create` (создание DNS-зоны)

## Что делает операция

`create` создает новую DNS-зону на авторитетных серверах сервиса.
После создания зона закрепляется за пользователем, который запустил создание инстанса.

## Когда использовать

- подключаете домен к сервису впервые;
- нужна новая отдельная зона.

## Важное условие после создания

У регистратора домена нужно делегировать зону на:

- `ns3.ngcloud.ru`
- `185.247.187.83`
- `2a10:aa80:0:5::3`
- `ns4.ngcloud.ru`
- `81.22.46.43`
- `2a10:aa80:0:6::3`

Если этого не сделать, зона будет создана технически, но в интернете домен не начнет работать через эти DNS.

## Объяснение параметра `Типа зоны`

### `NATIVE`

Это основная (ведущая) зона в нашем сервисе.
Вы управляете записями здесь, и именно эта зона считается источником правды.

### `SLAVE`

Это копия зоны, которая подтягивает данные с внешнего DNS-сервера.
Такой режим нужен, когда истинный источник записей находится не в нашем сервисе, а на другом DNS.

## Что проверяет операция

- что такая зона еще не существует на целевых DNS;
- корректность входных параметров зоны;
- ограничения на служебные/запрещенные имена зон.' - params: - - id: 761 - code: zoneName - data_type: string - required: true - regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ - maxlength: 64 - minlength: 1 - descr: Имя зоны - sort: 20 - - id: 762 - code: zoneKind - data_type: string - required: true - default: NATIVE - value_list: - - NATIVE - - SLAVE - descr: Тип зоны - sort: 30 - - id: 763 - code: zoneMastersForSlave - data_type: string - required: false - descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов - man: Пример:
**8.8.8.8 8.8.4.4** - sort: 40 - - id: 764 - code: zoneRefresh - data_type: integer > 0 - required: true - default: "86400" - minvalue: 600 - descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов - sort: 50 - - id: 765 - code: zoneRetry - data_type: integer > 0 - required: true - default: "7200" - minvalue: 600 - descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов - sort: 60 - - id: 766 - code: zoneExpire - data_type: integer > 0 - required: true - default: "3600000" - minvalue: 3600 - descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным - sort: 70 - - id: 767 - code: zoneTTL - data_type: integer > 0 - required: true - default: "86400" - minvalue: 600 - descr: Время, в течение которого информация будет кешироваться другими DNS-серверами - sort: 80 - - id: 768 - code: resourceRealm - data_type: string - required: true - default: powerdns - value_list: - - powerdns - func: getAvailableResourceRealms - sort: 10 - - name: create_record - id: 260 - kind: subresource - action: create - subresource: record - man: '# Операция `create_record` (добавление одной записи)

## Что делает операция

`create_record` точечно добавляет одну DNS-запись в существующую зону.
Если аналогичная запись уже есть, итоговый набор дополняется в рамках логики типа/имени.

## Когда использовать

- нужно добавить одну новую запись без массовой переработки зоны;
- нужно быстро внести точечное изменение.
' - params: - - id: 777 - code: recordName - data_type: string - required: true - regex: ^([a-zA-Z0-9\-_]+\.)*[a-zA-Z0-9\-_]+$|^(\d{1,3}\.){3}\d{1,3}$|^.+$ - unique_scope: parent - maxlength: 64 - minlength: 1 - man: Имя зоны в конце проставляется автоматически - sort: 10 - - id: 778 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - maxvalue: 84000 - minvalue: 30 - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 20 - - id: 779 - code: recordType - data_type: string - required: true - default: A - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - sort: 30 - - id: 780 - code: recordInput - data_type: string - required: true - man: Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - sort: 40 - - name: delete - id: 255 - kind: instance - action: delete - man: '# Операция `delete` (удаление зоны)

## Что делает операция

`delete` удаляет DNS-зону целиком вместе со всеми записями.

## Когда использовать

- зона больше не нужна;
- домен выводится из эксплуатации в рамках сервиса.

## Важная логика

- удаляется вся зона, а не отдельные записи;
- перед удалением стоит проверить, что домен действительно не используется.
' - params: [] - - name: delete_record - id: 261 - kind: subresource - action: delete - subresource: record - man: '# Операция `delete_record` (удаление записей по имени)

## Что делает операция

`delete_record` удаляет все записи с указанным именем (`recordName`), независимо от типа (`A`, `TXT`, `CNAME` и т.д.).

## Когда использовать

- нужно полностью убрать конкретное имя из зоны;
- требуется быстро очистить все варианты записи для одного имени.

## Важная логика

- удаляются все типы записей для выбранного имени;
- если нужно удалить только один тип записи, лучше использовать `modify_record` с нужным целевым состоянием.
' - params: - - id: 781 - code: recordName - data_type: string - required: true - - name: modify - id: 256 - kind: instance - action: modify - man: '# Операция `modify` (массовое изменение записей зоны)

## Что делает операция

`modify` обновляет записи зоны целиком на основе переданного массива `records`.
Это операция "приведения к целевому состоянию": система берет входной набор и применяет его на DNS-серверах.

## Когда использовать

- нужно массово обновить зону;
- нужно заменить сразу несколько записей одним запуском;
- нужно синхронно привести зону к заранее подготовленному списку записей.

## Важная логика

- запись группируется по `(имя, тип, TTL)`;
- операция ориентирована на полное обновление набора, а не на "точечную мелкую правку".

Для точечных изменений одной записи обычно удобнее `create_record` или `modify_record`.
' - params: - - id: 769 - code: records - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 87 - code: recordInput - data_type: string - required: true - default: "" - man: Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - is_modifiable: false - - id: 86 - code: recordName - data_type: string - required: true - default: "" - regex: ^([a-zA-Z0-9\-_]+\.)*[a-zA-Z0-9\-_]+$|^(\d{1,3}\.){3}\d{1,3}$|^.+$ - man: Имя зоны в конце проставляется автоматически - is_modifiable: false - - id: 88 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - is_modifiable: false - - id: 85 - code: recordType - data_type: string - required: true - default: A - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - is_modifiable: false - - name: modify_record - id: 262 - kind: subresource - action: modify - subresource: record - man: '# Операция `modify_record` (изменение конкретной записи)

## Что делает операция

`modify_record` заменяет записи с указанным именем одной новой записью заданного типа и значения.

## Когда использовать

- нужно исправить одну конкретную запись;
- нужно заменить старые значения записи на новое целевое значение.

## Важная логика

- существующие записи выбранного имени очищаются;
- затем добавляется одна новая запись с переданными параметрами.
' - params: - - id: 783 - code: recordName - data_type: string - required: true - sort: 10 - - id: 784 - code: recordTTL - data_type: integer > 0 - required: true - maxvalue: 84000 - minvalue: 30 - man: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 20 - - id: 785 - code: recordType - data_type: string - required: true - value_list: - - A - - AAAA - - CNAME - - MX - - SRV - - TXT - - LUA - - NS - - SSHFP - sort: 30 - - id: 786 - code: recordInput - data_type: string - required: true - man: Валидация производится только после запуска операции. Если при указанном типе будет указано некорректно значение, это будет описано в логе ошибки операции - sort: 40 - - name: reconcile - id: 259 - kind: action - action: reconcile - man: '# Операция `reconcile` (актуализация состояния зоны)

## Что делает операция

`reconcile` подтягивает актуальное фактическое состояние DNS-зоны и синхронизирует его с данными инстанса.
Операция техническая: она нужна для выравнивания состояния, а не для бизнес-изменений записей.

## Когда использовать

- записи менялись вручную на DNS-серверах;
- нужно обновить схему/состояние инстанса в личном кабинете;
- нужно импортировать текущие записи в управляемое состояние.

## Важная логика

- актуализируется список записей в данных инстанса;
- стандартно ничего "нового" для клиента не добавляет и не удаляет само по себе, а приводит данные к реальности.
' - params: [] diff --git a/generated/test/resources_yaml/110_dnszone.yaml b/generated/test/resources_yaml/110_dnszone.yaml deleted file mode 100644 index de8d32c..0000000 --- a/generated/test/resources_yaml/110_dnszone.yaml +++ /dev/null @@ -1,97 +0,0 @@ -name: dnszone -service_id: 110 -service_display_name: DNS зона -service_short_name: dnszone -service_man: '## ![image](https://img.icons8.com/?size=50&id=41400&format=png) Необходимо делегировать зону у регистратора на Авторитетные DNS (Сервера зон)

ns3.ngcloud.ru
``` bash
185.247.187.83
2a10:aa80:0:5::3
```

ns4.ngcloud.ru
``` bash
81.22.46.43
2a10:aa80:0:6::3
```' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 43 - kind: instance - action: create - params: - - id: 66 - code: zoneEmail - data_type: string - required: true - descr: Контактный e-mail - - id: 67 - code: zoneName - data_type: string - required: true - regex: ^([a-z0-9-]+\.)+[a-z0-9-]+$ - descr: Имя зоны - man: Имя зоны - - id: 68 - code: zoneKind - data_type: string - required: true - default: NATIVE - value_list: - - NATIVE - - SLAVE - descr: Тип зоны - - id: 69 - code: zoneMastersForSlave - data_type: string - required: false - descr: Использовать если выбран тип SLAVE. IP адреса в формате через пробел для настройки Master-серверов - - id: 70 - code: zoneTimeToRefresh - data_type: string - required: true - default: "86400" - descr: Время ожидания ответа вторичного DNS перед запросом SOA-записи с первичных серверов - - id: 71 - code: zoneTimeToRetry - data_type: string - required: true - default: "7200" - descr: Время в течение которого сервер повторно попробует синхронизировать информацию с первичных DNS-серверов - - id: 72 - code: zoneTimeToExpire - data_type: string - required: true - default: "3600000" - descr: Время в течение которого вторичный DNS будет пытаться завершить синхронизацию зоны с первичным - - id: 73 - code: zoneTimeToLive - data_type: string - required: true - default: "86400" - descr: Время, в течение которого информация будет кешироваться другими DNS-серверами - - id: 284 - code: resourceRealm - data_type: string - required: true - default: powerdns - value_list: - - powerdns - func: getAvailableResourceRealms - descr: Платформа для развертывания - - name: delete - id: 44 - kind: instance - action: delete - params: [] diff --git a/generated/test/resources_yaml/111_dnsrecord.yaml b/generated/test/resources_yaml/111_dnsrecord.yaml deleted file mode 100644 index d87a3c1..0000000 --- a/generated/test/resources_yaml/111_dnsrecord.yaml +++ /dev/null @@ -1,105 +0,0 @@ -name: dnsrecord -service_id: 111 -service_display_name: DNS запись -service_short_name: dnsrecord -service_man: Необходима DNS зона, в которой будет создана DNS запись.
Выберите
* Зону в которой будет создана запись
* Тип записи
* Имя записи
* Значение записи -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 45 - kind: instance - action: create - params: - - id: 75 - code: zoneUid - data_type: uuid - required: true - ref_svc_id: 109 - descr: UUID экземпляра услуги DNS-зоны - man: UUID экземпляра услуги DNS-зоны - sort: 10 - - id: 76 - code: recordType - data_type: string - required: true - value_list: - - A - - AAAA - - CNAME - - MX - - PTR - - SOA - - SRV - - TXT - - LUA - - NS - - SSHFP - descr: Тип DNS-записи - man: Тип DNS-записи - sort: 20 - - id: 77 - code: recordName - data_type: string - required: true - unique_scope: parent - descr: Имя записи. Имя зоны в конце проставляется автоматически - man: Имя записи. Имя зоны в конце проставляется автоматически - sort: 30 - - id: 78 - code: recordInput - data_type: string - required: true - descr: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - man: Значение. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - sort: 40 - is_modifiable: true - - id: 450 - code: recordTTL - data_type: integer > 0 - required: true - default: "3600" - maxvalue: 604800 - minvalue: 30 - descr: Time-to-Leave записи. Раз в какое время запись будет обновлена - sort: 50 - - name: delete - id: 46 - kind: instance - action: delete - params: [] - - name: modify - id: 90 - kind: instance - action: modify - params: - - id: 256 - code: recordInput - data_type: string - required: true - descr: Запись. Необходима валидация, в зависимости от типа записи. Должно быть заменено на UUID-внешнийIP - - name: reconcile - id: 408 - kind: action - action: reconcile - params: [] diff --git a/generated/test/resources_yaml/112_tenant.yaml b/generated/test/resources_yaml/112_tenant.yaml deleted file mode 100644 index 89bc4a6..0000000 --- a/generated/test/resources_yaml/112_tenant.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: tenant -service_id: 112 -service_display_name: Тенант в Grafana -service_short_name: tenant -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 84 - kind: instance - action: create - params: - - id: 239 - code: grafanaOrgName - data_type: string - required: true - regex: ^WZ[\d]+$ - descr: WZ-Код клиента - sort: 10 - - id: 240 - code: resourceRealm - data_type: string - required: true - default: grafana.ngcloud.ru - value_list: - - grafana.ngcloud.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 20 - - name: delete - id: 86 - kind: instance - action: delete - params: [] - - name: modify - id: 87 - kind: instance - action: modify - man: Просто удаляет все текущие датасоурсы и ставит их заново
Полезно, когда нужно "ресетнуть" / "добавить" - params: [] diff --git a/generated/test/resources_yaml/113_vc_complex.yaml b/generated/test/resources_yaml/113_vc_complex.yaml deleted file mode 100644 index 0929440..0000000 --- a/generated/test/resources_yaml/113_vc_complex.yaml +++ /dev/null @@ -1,158 +0,0 @@ -name: vc_complex -service_id: 113 -service_display_name: Быстрый старт -service_short_name: vcComplex -service_man: '# Создает окружение для ВМ.

## Создает сервисы:
**Организация**
**Виртуальный датацентр**
**Каталог**
**Сетевой шлюз периметра**

' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 107 - kind: instance - action: create - params: - - id: 332 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы - sort: 1 - - id: 334 - code: vappName - data_type: string - required: true - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: Имя vApp - sort: 10 - - id: 358 - code: vdcProviderGateway - data_type: string - required: true - default: sandbox-v1cl1-pvdc - descr: Взять из свойств ресурсной платформы.
Путь .vcd.providerVdc - взять KEY

providerGateway Cloud Director. Влияет ресурсы будут предоставлены. Является полем (List<String>) в свойствах рес. платформы ЛК, но сам параметр передаётся только как один String - sort: 200 - - id: 359 - code: virtualServicesCount - required: true - default: "1" - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые выделяются на AVI. Во избежании коллапса пока выделяется до 4 - sort: 110 - - id: 360 - code: ipSpaceName - data_type: string - required: false - default: internet-ipv4-v1 - descr: Имя ip Space для внешнего IP. Целесообразнее брать самый дешёвый, так как это vip для исходящего трафика. Необходимо задавать если включен needExternalAddressSNAT - sort: 130 - - id: 399 - code: storageConfig - data_type: json - required: true - default: '[{"name": "", "size": 1024}]' - descr: Описывается как массив объектов, где описывается имя политики и её размер - sort: 210 - - id: 400 - code: vdcNetworkPool - data_type: string - required: true - default: nsxt-sandbox-geneve-np - sort: 120 - - id: 401 - code: cpuGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - sort: 20 - - id: 402 - code: memGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - sort: 30 - - id: 403 - code: segroupName - data_type: string - required: false - default: SEGROUP-SANDBOX-CL1-SHARED-01 - descr: Наименование segroup, в котором будут выделяться пулы на AVI - sort: 100 - - id: 404 - code: needExternalAddressSNAT - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Выделяет внешний IP на Edge. Используется для SNAT всего трафика - sort: 40 - - id: 405 - code: needEnableAVI - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Включает AVI в Cloud Director - sort: 50 - - id: 566 - code: cpuAllocated - data_type: string - required: true - default: "1" - minvalue: 1 - descr: шт - sort: 21 - - id: 567 - code: memAllocated - data_type: integer > 0 - required: true - default: "10" - minvalue: 1 - descr: gb - sort: 31 - - id: 568 - code: vIPCount - data_type: integer > 0 - required: true - default: "0" - maxvalue: 20 - minvalue: 0 - descr: ш - sort: 131 diff --git a/generated/test/resources_yaml/114_gitea_complex.yaml b/generated/test/resources_yaml/114_gitea_complex.yaml deleted file mode 100644 index b18075e..0000000 --- a/generated/test/resources_yaml/114_gitea_complex.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: gitea_complex -service_id: 114 -service_display_name: Комплексная услуга по созданию gitea -service_short_name: GiteaComplex -service_man: Комплексная услуга из ["gittea", "postgres"] -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: [] diff --git a/generated/test/resources_yaml/115_mariadb.yaml b/generated/test/resources_yaml/115_mariadb.yaml deleted file mode 100644 index ef90350..0000000 --- a/generated/test/resources_yaml/115_mariadb.yaml +++ /dev/null @@ -1,460 +0,0 @@ -name: mariadb -service_id: 115 -service_display_name: Mariadb -service_short_name: mariadb -service_man: 'Mariadb кластер. Ресурсная платформа: k8s. Автоматизация: k8s operator.
Автоматическое резервное копирование, ручное восстановление.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 138 - kind: instance - action: create - params: - - id: 877 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 242 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 878 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 243 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 244 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 245 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - man: Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать - is_modifiable: false - - id: 246 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 879 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 248 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 247 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 880 - code: mariadbConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 249 - code: version - data_type: string - required: true - default: 9.4.0 - value_list: - - 8.4.0 - - 9.4.0 - is_modifiable: false - - id: 881 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - is_modifiable: true - sub_params: - - id: 250 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 251 - code: retain - data_type: integer > 0 - required: true - default: "720" - is_modifiable: false - - id: 252 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 882 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 255 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 256 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 253 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 254 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - name: create_database - id: 242 - kind: subresource - action: create - subresource: database - params: - - id: 734 - code: dbName - data_type: string - required: true - default: db-bdwhrjlz - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: 'Пример: `db01`' - - name: create_user - id: 243 - kind: subresource - action: create - subresource: user - params: - - id: 735 - code: username - data_type: string - required: true - default: user2ktdtvd8 - regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ - maxlength: 62 - minlength: 2 - descr: 'Пример: `myusername`' - sort: 10 - - id: 736 - code: password - data_type: string - required: true - default: dz8vtf8o - maxlength: 62 - minlength: 8 - descr: Секрет будет записан в хранилище секретов и не будет отображаться публично - sort: 20 - is_sensitive: true - - id: 737 - code: dbName - data_type: array-map-fixed - required: true - default: '[{}]' - descr: Необходимо указать массив существующих баз - man: Если базы не существует - при выполнении операции вернётся ошибка - sort: 30 - sub_params: - - id: 54 - code: dbname - data_type: string - required: true - default: "" - is_modifiable: false - - id: 738 - code: accessHosts - data_type: string - required: true - default: '%' - descr: 'Пример: `%`' - sort: 40 - - name: delete - id: 139 - kind: instance - action: delete - params: - - id: 456 - code: deleteS3Bucket - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Также удалить технический S3 Bucket с бэкапами? - sort: 10 - - name: delete_database - id: 248 - kind: subresource - action: delete - subresource: database - params: - - id: 745 - code: dbName - data_type: string - required: true - descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ - - name: delete_user - id: 247 - kind: subresource - action: delete - subresource: user - params: - - id: 744 - code: username - data_type: string - required: true - descr: Имя пользователя, созданное через операцию `create_user` - - name: modify - id: 142 - kind: instance - action: modify - params: - - id: 883 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 259 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - man: Пока `Galera` выключена
Как будет запрос на включение и тестирование, реплики можно будет увеличивать - is_modifiable: false - - id: 260 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 257 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 258 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 884 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 261 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 262 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 885 - code: mariadbConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 263 - code: version - data_type: string - required: true - default: "" - value_list: - - 8.4.0 - - 9.4.0 - is_modifiable: false - - id: 886 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 264 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 265 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 266 - code: schedule - data_type: string - required: true - default: "" - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 887 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 270 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 267 - code: enabled - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 268 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 269 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - name: reconcile - id: 287 - kind: action - action: reconcile - params: [] - - name: resume - id: 141 - kind: instance - action: resume - params: [] - - name: suspend - id: 140 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/116_kafka.yaml b/generated/test/resources_yaml/116_kafka.yaml deleted file mode 100644 index 6d2b92c..0000000 --- a/generated/test/resources_yaml/116_kafka.yaml +++ /dev/null @@ -1,337 +0,0 @@ -name: kafka -service_id: 116 -service_display_name: ApacheKafka -service_short_name: kafka -service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Идентификатор организации
Уникальный идентификатор созданной организации. Используется для всех дальнейших API-операций.

### Статус организации
Отражает текущее состояние: active, suspended, modifying, deleting.

### Параметры сети
Выходные данные при наличии ipSpaces: список выделенных внешних IP, имя ip_space, количество IP.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)**;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к этой организации.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.

' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 148 - kind: instance - action: create - params: - - id: 1084 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 579 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1085 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 580 - code: cpu - data_type: integer > 0 - required: true - default: "2000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 581 - code: memory - data_type: integer > 0 - required: true - default: "2048" - is_modifiable: false - - id: 582 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 583 - code: disk - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 1086 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 585 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 584 - code: allocationExternal - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - is_modifiable: false - - name: create_topic - id: 165 - kind: subresource - action: create - subresource: topic - man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' - params: - - id: 523 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`' - sort: 10 - - id: 524 - code: partitions - data_type: integer > 0 - required: true - descr: Число партиций можно увеличить, но уменьшить их нельзя. - - id: 525 - code: replicas - data_type: integer > 0 - required: true - descr: Количество реплик не может привышать количество реплик брокера - sort: 20 - - name: create_user - id: 168 - kind: subresource - action: create - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 530 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - id: 531 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' - - id: 532 - code: operations - data_type: array - required: true - value_list: - - Create - - Describe - - Read - - Write - descr: 'Пример: `Create, Describe, Read, Write`' - - id: 533 - code: group - data_type: string - required: true - default: '*' - descr: 'Пример: `order-consumers` или `*`' - - id: 534 - code: accessHosts - data_type: string - required: true - default: '*' - descr: 'Пример: `192.168.1.0/24` или `*`' - - name: delete - id: 149 - kind: instance - action: delete - params: [] - - name: delete_topic - id: 167 - kind: subresource - action: delete - subresource: topic - params: - - id: 529 - code: nameTopic - data_type: string - required: true - descr: Необходимо выбрать топик, который необходимо удалить - - name: delete_user - id: 170 - kind: subresource - action: delete - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 540 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - name: modify - id: 162 - kind: instance - action: modify - params: - - id: 1087 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 588 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 589 - code: disk - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 586 - code: cpu - data_type: integer > 0 - required: true - default: "2000" - is_modifiable: false - - id: 587 - code: memory - data_type: integer > 0 - required: true - default: "2048" - is_modifiable: false - - id: 1088 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 590 - code: allocationExternal - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - is_modifiable: false - - id: 591 - code: masterAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - name: modify_topic - id: 166 - kind: subresource - action: modify - subresource: topic - man: '# Инструкция по управлению топиками Apache Kafka через платформу

## 1. Общая информация

---

Топик — это основная сущность в Apache Kafka, предназначенная для приёма сообщений от продюсеров и передачи их консюмерам.
Платформа позволяет создавать, удалять и изменять параметры топиков без прямого доступа к брокерам, обеспечивая удобное управление через интерфейс или API.

---

## Доступные операции

- **create_topic** — Создание топика
- **modify_topic** — Модификация топика
- **delete_topic** — Удаление топика

---

## 2. Параметры создания топика

Ниже перечислены параметры, которые необходимо указать при создании нового топика:

* **Имя топика**
Название топика.
Должно быть уникальным в пределах кластера.
Пример: `orders-events`.

* **Партиции**
Количество партиций топика.
Рекомендуется выбирать с учётом уровня параллелизма потребителей и предполагаемой нагрузки.
Пример: `6`. Здесь важно рассказать о том как происходит расчет оптимального количества
Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.

* **Реплики**
Количество реплик для каждой партиции.
Обычно равно количеству доступных брокеров или меньше.
Пример: `3`.


---

## 4. Параметры модификации топика

Для уже существующих топиков можно изменять параметры. Все параметры являются опциональными:

* **Имя топика**
Название топика.
Пример: `orders-events`.

* **Партиции**
Новое количество партиций.
Пример: `12`.

* **Реплики**
Новое количество реплик.
Пример: `2`.

---

## 5. Рекомендации

* Планируйте количество партиций с запасом — уменьшение их числа невозможно.
* Количество реплик должно соответствовать требуемому уровню отказоустойчивости, но не превышать количество брокеров.
* Изменение параметров топика в рабочем окружении может временно влиять на производительность. Рекомендуется проводить крупные изменения в периоды минимальной нагрузки.

---' - params: - - id: 526 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`' - sort: 10 - - id: 527 - code: partitions - required: false - descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` - sort: 30 - - id: 528 - code: replicas - required: false - descr: Количество партиций ≤ (число брокеров). Пример `2` - sort: 20 - - name: modify_user - id: 169 - kind: subresource - action: modify - subresource: user - man: '# Инструкция по управлению пользователями Apache Kafka.

---

## 1. Общая информация

Пользователи в Apache Kafka используются для аутентификации и авторизации при подключении к кластеру.
Платформа позволяет создавать, удалять и изменять пользователей без прямого доступа к брокерам, а также настраивать права доступа к топикам и группам.
Это обеспечивает централизованное и безопасное управление доступом к ресурсам Kafka.

---

## Доступные операции

- **create_user** — Создание пользователя
- **modify_user** — Модификация пользователя
- **delete_user** — Удаление пользователя

---

## 2. Параметры создания пользователя

Ниже перечислены параметры, которые необходимо указать при создании нового пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 3. Параметры модификации пользователя

Ниже перечислены параметры, которые необходимо указать при модификации пользователя:

* **Имя пользователя**
Имя создаваемого пользователя. Используется для подключения к Kafka и настройки прав.
Пример: `username-admin`

* **Топик**
Название топика, к которому будут назначены права для данного пользователя.
Пример: `orders-events`.
Использовать маску нельзя (`orders*`), только полное название или `*`.

* **Операции**
Список разрешённых операций на указанном топике.
Пример: `Describe, Read, Write`

* **Группа**
Имя группы, которой разрешён доступ. Используется при настройке консюмер-групп.
Пример: `order-consumers`.

* **Разрешённые хосты**
Список хостов или подсетей, с которых разрешено подключение пользователя.
Пример: `192.168.1.0/24` или `*`.

---

## 4. Параметры удалении пользователя

Ниже перечислены параметры, которые необходимо указать при удалении пользователя:

* **Имя пользователя**
Имя удаляемого пользователя.
Пример: `username-admin`

## 5. Выходные параметры

После успешного создания пользователя платформа возвращает параметры, необходимые для подключения:

* **ca.crt**
Корневой сертификат (Certificate Authority), используемый для проверки подлинности сервера при TLS-подключении.
Файл содержит публичный ключ удостоверяющего центра, которому доверяют брокеры и клиенты. Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **ca.p12**
PKCS#12-хранилище с корневым сертификатом (Certificate Authority), используемое для проверки подлинности сервера при TLS-подключении.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.p12
```

* **ca.password**
Пароль для доступа к хранилищу `ca.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

* **user.crt**
Клиентский сертификат, подтверждающий личность пользователя при подключении к Kafka.
Используется совместно с приватным ключом `user.key` для аутентификации.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.crt
```

* **user.key**
Приватный ключ пользователя, применяемый для установления защищённого TLS-соединения и подтверждения подлинности.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo ''<содержание поля>'' | base64 -d > <название>.key
```

* **user.p12**
PKCS#12-хранилище с клиентским сертификатом и приватным ключом, предназначенное для аутентификации пользователя в Kafka.
Выдаеться в формате base64 для получения файла сертификата использовать команду
```bash
echo "<содержание поля>" | base64 -d > <название>.p12
```

* **user.password**
Пароль для доступа к хранилищу `user.p12`. Выдаеться в чистом виде. Раскодировать не нужно.

Эти параметры используются для настройки TLS-подключения.

---

## 6. Пример конфигурации клиента внутри кластера

Пример пода:

```yaml
apiVersion: v1
kind: Pod
metadata:
name: kafka-4
spec:
volumes:
- name: work
emptyDir: {}
containers:
- name: kcat
image: quay.io/strimzi/kafka:latest-kafka-4.0.0
command: ["sleep", "5600"]
volumeMounts:
- name: work
mountPath: /work
restartPolicy: Never
```

Создаём под и сохраняем сертификаты:

```bash
kubectl cp user.p12 kafka-4:/work/
kubectl cp ca.p12 kafka-4:/work/
```

Создаём файлы конфигурации:

```bash
cat > /work/user-read.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

```bash
cat > /work/user-write.properties <<''EOF''
security.protocol=SSL
ssl.keystore.type=PKCS12
ssl.keystore.location=/work/user-write.p12
ssl.keystore.password=<user.password>
ssl.truststore.type=PKCS12
ssl.truststore.location=/work/ca.p12
ssl.truststore.password=<ca.password>
EOF
```

Листинг доступных топиков:

```bash
/opt/kafka/bin/kafka-topics.sh \
--bootstrap-server kafkak8s-bootstrap-external:9093 \
--command-config /work/user-read.properties \
--list
```

Запись сообщений:

```bash
/opt/kafka/bin/kafka-console-producer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--producer.config /work/user-write.properties
```

Чтение сообщений:

```bash
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server <адрес сервис брокера>:9093 \
--topic <topic> \
--from-beginning \
--group <group> \
--consumer.config /work/user-read.properties \
--max-messages 50
```

---

## 7. Пример внешнего подключения с использованием **kcat**

**Листинг топиков**

```bash
kcat -b <externalConnect.bootstrap.fqdn>:9093 \
-L \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

**Запись сообщения**

```bash
echo "hello from kcat" | kcat -P \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-write.crt \
-X ssl.key.location=./user-write.key \
-X ssl.endpoint.identification.algorithm=none
```

**Чтение сообщений**

```bash
kcat -C \
-b <externalConnect.bootstrap.fqdn>:9093 \
-t write-test \
-o beginning \
-q \
-X security.protocol=SSL \
-X ssl.ca.location=./ca.crt \
-X ssl.certificate.location=./user-read.crt \
-X ssl.key.location=./user-read.key \
-X ssl.endpoint.identification.algorithm=none
```

---


## 9. Рекомендации

* Минимизируйте набор разрешённых операций для повышения безопасности.
* Используйте отдельные учётные записи для продюсеров, консюмеров и системных сервисов.
* Ограничивайте доступ по IP или подсетям с помощью параметра **Разрешённые хосты**.
* После изменения прав или групп пользователя рекомендуется проверить подключение и доступ к топикам.

---' - params: - - id: 535 - code: nameTopic - data_type: string - required: false - default: '*' - descr: 'Пример: `orders-events`. Использовать маску нельзя(`orders*`), только полное название или `*`' - - id: 536 - code: operations - data_type: array - required: false - value_list: - - Create - - Describe - - Read - - Write - descr: 'Пример: `Create, Describe, Read, Write`' - - id: 537 - code: group - data_type: string - required: false - default: '*' - descr: 'Пример: `order-consumers` или `*`' - - id: 538 - code: accessHosts - data_type: string - required: false - default: '*' - descr: 'Пример: `192.168.1.0/24` или `*`' - - id: 539 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - sort: 10 - - name: resume - id: 161 - kind: instance - action: resume - params: [] - - name: suspend - id: 160 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/117_nifi.yaml b/generated/test/resources_yaml/117_nifi.yaml deleted file mode 100644 index 39c49bd..0000000 --- a/generated/test/resources_yaml/117_nifi.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: nifi -service_id: 117 -service_display_name: Nifi -service_short_name: nifi -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 150 - kind: instance - action: create - params: - - id: 471 - code: kafkaUid - data_type: uuid - required: true - ref_svc_id: 116 - descr: Выбрать кластер из списка - sort: 10 - - id: 472 - code: partitions - data_type: integer > 0 - required: true - descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` - man: 'Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.' - sort: 30 - is_modifiable: true - - id: 473 - code: replicas - data_type: integer > 0 - required: true - descr: Количество партиций ≤ (число брокеров). Пример `2` - sort: 40 - is_modifiable: true - - id: 474 - code: nameTopic - data_type: string - required: true - descr: 'Пример: `orders-events`' - sort: 20 - - name: delete - id: 151 - kind: instance - action: delete - params: [] - - name: modify - id: 154 - kind: instance - action: modify - params: - - id: 477 - code: partitions - data_type: integer > 0 - required: false - descr: Число партиций на кластер ≤ 100 × (число брокеров) × (фактор репликации). Пример `50` - man: 'Обратите внимание: уменьшить количество партиций после создания невозможно, поэтому лучше закладывать небольшой запас на будущий рост нагрузки.' - - id: 478 - code: replicas - data_type: integer > 0 - required: false - descr: Количество партиций ≤ (число брокеров). Пример `2` diff --git a/generated/test/resources_yaml/119_akhq.yaml b/generated/test/resources_yaml/119_akhq.yaml deleted file mode 100644 index a12f8a1..0000000 --- a/generated/test/resources_yaml/119_akhq.yaml +++ /dev/null @@ -1,109 +0,0 @@ -name: akhq -service_id: 119 -service_display_name: Akhq -service_short_name: Akhq -service_man: '# Инструкция по развертыванию Apache Kafka через платформу

---
## 1. Общая информация
AKHQ — это веб-интерфейс для работы с кластерами Apache Kafka.
Позволяет просматривать топики, consumer group''ы, сообщения, ACL, схему безопасности, состояние брокеров и метаданные.
Поддерживает работу с несколькими кластерами Kafka одновременно, включая внутренние и внешние (TLS) подключения.

## Доступные операции
- **create** — Создание ресурса AKHQ
- **suspend** — Остановка ресурса
- **resume** — Возобновление работы ресурса
- **modify** — Модификация настроек
- **delete** — Удаление ресурса

---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса Kafka:

* **Кластер Kafka к которому нужно настроить подключение**
Для использования AKHQ необходимо предварительно развернуть сервис Kafka. Пример: `kafka-external`.

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество узлов для работы Akhq. Пример `2`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `512`

* **Квота (millicore ) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Имя домена:**
Dns Имя по которому будет происходить обращение к сервису. Указать необходимо уникальное имя до . ( то есть если необходимо получить dns ui) Пример: `kafka-external`

---
## 4. Выходные параметры
После успешного развертывания ресурса Kafka платформа возвращает ряд параметров.
Ключевым из них является:

**connectionUrl**
Это полное доменное имя (FQDN), по которому будет доступен интерфейс AKHQ. После развёртывания подождите несколько минут — UI станет доступен по HTTPS, как только все сервисы полностью запустятся.

**Учетные данные**
Здесь представлены поля для ввода логина и пароля, с помощью которых вы сможете авторизоваться в сервисе.

---
## 5. Дополнительная информация
Учётные записи, созданные для работы с AKHQ, предоставляют доступ только для просмотра данных. Для управления Kafka, пожалуйста, используйте возможности личного кабинета.
---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 175 - kind: instance - action: create - params: - - id: 550 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Выбрать платформу из списка - sort: 10 - - id: 551 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - minvalue: 512 - descr: 'Пример: `1024`' - sort: 20 - - id: 552 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - minvalue: 500 - descr: 'Пример: `1000`' - sort: 30 - - id: 553 - code: domain - data_type: string - required: true - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 40 - - id: 554 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - minvalue: 1 - sort: 11 - - id: 555 - code: kafkaUid - data_type: uuid - required: true - ref_svc_id: 116 - descr: Выбрать кластер из списка - sort: 5 - is_modifiable: true - - name: delete - id: 176 - kind: instance - action: delete - params: [] - - name: modify - id: 180 - kind: instance - action: modify - params: - - id: 565 - code: kafkaUid - data_type: uuid - required: true - ref_svc_id: 116 - descr: Выбрать кластер из списка - - name: resume - id: 178 - kind: instance - action: resume - params: [] - - name: suspend - id: 177 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/120_clickhouse.yaml b/generated/test/resources_yaml/120_clickhouse.yaml deleted file mode 100644 index 79c30e8..0000000 --- a/generated/test/resources_yaml/120_clickhouse.yaml +++ /dev/null @@ -1,572 +0,0 @@ -name: clickhouse -service_id: 120 -service_display_name: ClickHouse -service_short_name: clickhouse -service_man: '# Инструкция по управлению кластерами и пользователями ClickHouse

---

## Общая информация

ClickHouse — это колоночная система управления базами данных, предназначенная для обработки аналитических запросов в реальном времени. Платформа позволяет создавать, удалять, приостанавливать и возобновлять кластеры ClickHouse, а также управлять базами данных и пользователями.

Кластер создаётся через оператор Altinity (`https://github.com/Altinity/clickhouse-operator`) в ресурсной платформе типа `k8s`

Кластеры могут создаваться шардированными с произвольным количеством реплик. Для обеспечения отказоустойчивости и координации обязательно включение параметра `Создать ClickHouse-Keeper`.

Кластера создаются с обязательным бекапом. Для этого необходимо предварительно создать услугу `S3 Object Storage`.
---

## Доступные операции

* **create** — Создание кластера
* **delete** — Удаление кластера
* **suspend** — Приостановка работы кластера
* **resume** — Возобновление работы кластера
* **create_database** — Создание базы данных
* **delete_database** — Удаление базы данных
* **create_user** — Создание пользователя и настройка прав доступа к базе данных
* **delete_user** — Удаление пользователя

## Процесс создания и первого подключения

1. Выполнить операцию `create` - будет создан кластер
2. Выполнить операцию `create_database` - будет создана база данных
3. Выполнить операцию `create_user` - будет создан пользователь (выбрать доступ подключения к БД, созданной в п.2)

## Примеры подключения

### Подключение через HTTP-интерфейс

```bash
# Проверка доступности кластера
curl "http://<username>:<password>@<host>:8123/" -d "SELECT 1"

# Выполнение запроса
curl "http://<username>:<password>@<host>:8123/" -d "SHOW DATABASES"
```

## Простые кейсы
*Создать БД и обычной таблицы*
```clickhouse
CREATE TABLE database.table
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

INSERT INTO database.table SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM database.table;
```
Базы и таблицы не реплицируются без специального ключа (`ON CLUSTER ''clickhousek8s''`)
В случае создания в стиле, описанным выше, необходимо будет выполнять действия на всех шардах/репликах


*Создание БД и шардированной таблицы*
```clickhouse
CREATE TABLE database.localUsers
(
id UInt64,
value String
)
ENGINE = MergeTree()
ORDER BY id;

CREATE TABLE database.shardedTable
AS localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', id);

INSERT INTO shardedTable SELECT number, toString(number) FROM numbers(10000000);

SELECT count() FROM shardedTable;
```
Важный момент - в данном примере таблицы придётся также выполнять на всех нодах ClickHouse

*Создание реплицируемой и шардированной таблицы*
```clickhouse
CREATE TABLE IF NOT EXISTS database.localUsers ON cluster ''clickhousek8s''
(
user_id UInt64,
name String,
email String
)
ENGINE = ReplicatedMergeTree(
''/clickhouse/tables/{shard}/localUsers'',
''{replica}''
)
ORDER BY user_id;

CREATE TABLE IF NOT EXISTS database.users ON cluster ''clickhousek8s''
AS database.localUsers
ENGINE = Distributed(''clickhousek8s'', ''database'', ''localUsers'', rand());

INSERT INTO database.users VALUES (1, ''Alice'', ''alice@example.com'');
INSERT INTO database.users VALUES (2, ''Bob'', ''bob@example.com'');
INSERT INTO database.users VALUES (3, ''Charlie'', ''charlie@example.com'');
INSERT INTO database.users VALUES (4, ''David'', ''david@example.com'');
INSERT INTO database.users VALUES (5, ''Gosha'', ''gosha@example.com'');
INSERT INTO database.users VALUES (6, ''Eve'', ''eve@example.com'');
INSERT INTO database.users VALUES (7, ''Frank'', ''frank@example.com'');
INSERT INTO database.users VALUES (8, ''Grace'', ''grace@example.com'');
INSERT INTO database.users VALUES (9, ''Hannah'', ''hannah@example.com'');
INSERT INTO database.users VALUES (10, ''Ivan'', ''ivan@example.com'');

SELECT * FROM database.localUsers;
SELECT * FROM database.users;

SELECT COUNT(*) AS total_users FROM database.users;
SELECT COUNT(*) AS total_users_local FROM database.localUsers;
```' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 181 - kind: instance - action: create - man: Операция создания кластера ClickHouse

Для подключения к кластеру также необходимо создать базу данных через операцию `create_database` и создание пользователя через операцию `create_user` - params: - - id: 837 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 194 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 838 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 195 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 196 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 197 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 199 - code: shards - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - - "3" - man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' - is_modifiable: false - - id: 198 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 839 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 200 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 201 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 840 - code: clickhouseKeeperConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 202 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 203 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 204 - code: replicas - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "1" - - "3" - man: Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут - is_modifiable: false - - id: 841 - code: clickhouseConfiguration - data_type: map-fixed - required: true - sort: 50 - is_modifiable: true - sub_params: - - id: 205 - code: version - data_type: string - required: true - default: "25.10" - value_list: - - "25.10" - is_modifiable: false - - id: 842 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 207 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 208 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 206 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 843 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 80 - is_modifiable: true - sub_params: - - id: 209 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 210 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 211 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 212 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 851 - code: clickhouseFilesConfiguration - data_type: array-map-fixed - required: false - man: 'Формат:
- path: somethings3.xml
xmlConfig: ''<clickhouse>......</clickhouse>''' - sort: 60 - is_modifiable: true - sub_params: - - id: 234 - code: path - data_type: string - required: true - default: something.xml - regex: ^[a-zA-Z0-9]+\.xml$ - is_modifiable: false - - id: 235 - code: xmlConfig - data_type: string - required: true - default: '<clickhouse></clickhouse>' - man: Предварительно xml необходимо отформатировать через
```
$ tr -d '\n \t' < input.xml > output.xml
```

Позже будет исправлено - is_modifiable: false - is_sensitive: true - - name: create_database - id: 186 - kind: subresource - action: create - subresource: database - man: База данных создаётся на всех репликах кластера ClickHouse - params: - - id: 600 - code: dbName - data_type: string - required: true - default: db01 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: 'Пример: `db01`' - sort: 10 - - name: create_user - id: 185 - kind: subresource - action: create - subresource: user - params: - - id: 596 - code: username - data_type: string - required: true - default: myusername - regex: ^(?![0-9_])[a-zA-Z_][a-zA-Z0-9_]{0,62}$ - maxlength: 62 - minlength: 2 - descr: 'Пример: `myusername`' - sort: 10 - - id: 598 - code: dbName - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 30 - sub_params: - - id: 233 - code: dbName - data_type: string - required: true - default: "" - is_modifiable: false - - id: 599 - code: accessHosts - data_type: string - required: true - default: ::/0 - descr: 'Пример: `::/0`' - sort: 40 - - name: delete - id: 184 - kind: instance - action: delete - man: Удаление возможно только после остановки сервиса через операцию `suspend`
После выполнения операции необходимо выждать определенное кол-во времени - params: [] - - name: delete_database - id: 188 - kind: subresource - action: delete - subresource: database - man: Необратимое действие!!!
Трижды подумай, заблокируй телефон тимлида и не выполняй это действие - params: - - id: 602 - code: dbName - data_type: string - required: true - descr: ВСЕ ДАННЫЕ ВНУТРИ БД БУДУТ УДАЛЕНЫ - - name: delete_user - id: 187 - kind: subresource - action: delete - subresource: user - params: - - id: 601 - code: username - required: true - descr: Имя пользователя, созданное через операцию `create_user` - - name: modify - id: 189 - kind: instance - action: modify - params: - - id: 844 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 216 - code: shards - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - - "3" - man: '**Шард** - это часть данных, разделенная на разные узлы (серверы) для горизонтального масштабирования и обработки больших объемов информации.
**Реплика** - это точная копия данных внутри одного шарда' - is_modifiable: false - - id: 217 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 213 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 214 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 215 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 845 - code: clickhouseKeeperConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 218 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 219 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 220 - code: replicas - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "1" - - "3" - man: Если указан 0, то инстансы ClickHouse Keeper развёрнуты не будут - is_modifiable: false - - id: 846 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 221 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 222 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 847 - code: clickhouseConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 223 - code: version - data_type: string - required: true - default: "" - value_list: - - "25.10" - is_modifiable: false - - id: 848 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 224 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 225 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 226 - code: schedule - data_type: string - required: true - default: "" - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 849 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - sub_params: - - id: 227 - code: enabled - data_type: boolean - required: true - default: false,true - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 228 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 229 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 230 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 850 - code: clickhouseFilesConfiguration - data_type: array-map-fixed - required: false - man: 'Формат:
- path: somethings3.xml
xmlConfig: ''<clickhouse>......</clickhouse>''' - sort: 50 - sub_params: - - id: 231 - code: path - data_type: string - required: true - default: something.xml - regex: ^[a-zA-Z0-9]+\.xml$ - is_modifiable: false - - id: 232 - code: xmlConfig - data_type: string - required: true - default: '<clickhouse></clickhouse>' - man: Предварительно xml необходимо отформатировать через
```
$ tr -d '\n \t' < input.xml > output.xml
```

Позже будет исправлено - is_modifiable: false - is_sensitive: true - - name: reconcile - id: 276 - kind: action - action: reconcile - params: [] - - name: resume - id: 183 - kind: instance - action: resume - params: [] - - name: suspend - id: 182 - kind: instance - action: suspend - man: Операция приводит к удалению всех подов
Persistent Volume остаются нетронутыми - params: [] diff --git a/generated/test/resources_yaml/12_s3.yaml b/generated/test/resources_yaml/12_s3.yaml deleted file mode 100644 index ef3345a..0000000 --- a/generated/test/resources_yaml/12_s3.yaml +++ /dev/null @@ -1,130 +0,0 @@ -name: s3 -service_id: 12 -service_display_name: S3 Object Storage -service_short_name: s3 -service_man: Руководство доступно по ссылке:
https://docs.s3.msk-1.ngcloud.ru/s3/s3.description.html -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 1 - kind: instance - action: create - params: - - id: 163 - code: resourceRealm - required: true - default: ceph.tst.nubes.ru - value_list: - - ceph.tst.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: кластер S3 для подключения - sort: 20 - - id: 320 - code: displayName - data_type: string - required: true - regex: ^[a-zA-Z0-9_]{1,61}[a-zA-Z0-9]$ - descr: Человеко-читаемое имя (транслит из Elma) - передаётся имя клиента - sort: 10 - - name: create_sub_user - id: 6 - kind: subresource - action: create - subresource: sub_user - params: - - id: 60 - code: subUserName - data_type: string - required: true - - id: 61 - code: subUserAccess - data_type: string - required: true - value_list: - - read - - write - - readwrite - - full - - name: delete - id: 5 - kind: instance - action: delete - params: [] - - name: delete_sub_user - id: 7 - kind: subresource - action: delete - subresource: sub_user - params: - - id: 63 - code: subUserName - data_type: string - required: true - descr: subUserName. По идее услуга уже создана - должен передаваться из ЛК - - name: modify - id: 4 - kind: instance - action: modify - man: При первичном вызове операции услуга Trial прекращает действие - params: - - id: 50 - code: maxSizeGbPerUser - data_type: integer > 0 - required: false - maxvalue: 50000 - minvalue: 1 - descr: maxSizeGb. Передаём руками - man: maxSizeGb. Передаём руками - - id: 51 - code: maxObjectsPerBucket - data_type: integer > 0 - required: false - maxvalue: 1e+07 - minvalue: 1 - descr: Квота на бакет. Кол-во объектов. Передаём руками - man: Квота на бакет. Кол-во объектов. Передаём руками - - id: 52 - code: maxBucketsPerUser - data_type: integer > 0 - required: false - maxvalue: 1000 - minvalue: 1 - descr: Квота на пользователя. Количество бакетов пользователя. Передаём руками - man: Квота на пользователя. Количество бакетов пользователя. Передаём руками - - name: reconcile - id: 407 - kind: action - action: reconcile - params: [] - - name: resume - id: 3 - kind: instance - action: resume - params: [] - - name: suspend - id: 2 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/13_s3bucket.yaml b/generated/test/resources_yaml/13_s3bucket.yaml deleted file mode 100644 index 68e506c..0000000 --- a/generated/test/resources_yaml/13_s3bucket.yaml +++ /dev/null @@ -1,90 +0,0 @@ -name: s3bucket -service_id: 13 -service_display_name: S3 бакет -service_short_name: s3bucket -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 51 - kind: instance - action: create - params: - - id: 124 - code: s3UserUid - data_type: uuid - required: true - ref_svc_id: 12 - descr: UID Корневой услуги S3. На основе него будет создан бакет. - - id: 125 - code: bucketName - data_type: string - required: true - descr: Имя бакета s3 - - id: 126 - code: maxSize - required: true - default: "-1" - descr: Максимальный размер бакета s3. Если поставить -1, будет безлимит. Размер указывается в Bytes - man: Максимальный размер бакета s3. Если поставить 0, будет безлимит. Размер указывается в Bytes - - id: 127 - code: readAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ на чтение позволяет любому пользователю интернета скачивать объекты из бакета. - - id: 128 - code: listAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ к листингу позволяет любому пользователю интернета получить список объектов в бакете. - - id: 129 - code: corsAll - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Публичный доступ к чтению настроек позволяет любому пользователю интернета читать конфигурации CORS - - id: 130 - code: placement - data_type: string - required: true - default: HOT - value_list: - - HOT - - COLD - descr: Класс хранилища, который используется по умолчанию при загрузке объектов. Чем холоднее, тем дешевле место, но дороже обращения - - name: delete - id: 52 - kind: instance - action: delete - params: [] diff --git a/generated/test/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml b/generated/test/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml deleted file mode 100644 index 295e440..0000000 --- a/generated/test/resources_yaml/148_vc_mgmt_sthutrval_cluster.yaml +++ /dev/null @@ -1,176 +0,0 @@ -name: vc_mgmt_sthutrval_cluster -service_id: 148 -service_display_name: Менеджмент Kubernetes кластер Штурвал -service_short_name: vcMgmtSthutrvalCluster -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 258 - kind: instance - action: create - params: - - id: 771 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 91 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - descr: Облако в котором будет развёрнут тенант - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - is_modifiable: false - - id: 89 - code: clusterName - data_type: string - required: true - default: example-co5wmalx - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 772 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 90 - code: appVersion - data_type: string - required: true - default: 2.13.1 - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - id: 773 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 92 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана - is_modifiable: false - - id: 93 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - is_modifiable: false - - id: 94 - code: count - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 774 - code: workerConfiguration - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 40 - sub_params: - - id: 98 - code: count - data_type: integer > 0 - required: true - default: "2" - is_modifiable: false - - id: 95 - code: groupName - data_type: string - required: true - default: workers-60jgizhm - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 96 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy представлены исходя из ресурсной платформы, которая была выбрана - is_modifiable: false - - id: 97 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - is_modifiable: false - - id: 775 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 99 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false - - id: 100 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: 'Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**' - is_modifiable: false - - id: 101 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - id: 102 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: 'Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем
Пример: **["0.0.0.0/0"]** или **[]** или **["178.4.13.2", "178.4.14.0/24"]**' - is_modifiable: false diff --git a/generated/test/resources_yaml/149_valo_tenant.yaml b/generated/test/resources_yaml/149_valo_tenant.yaml deleted file mode 100644 index 7a7c63a..0000000 --- a/generated/test/resources_yaml/149_valo_tenant.yaml +++ /dev/null @@ -1,249 +0,0 @@ -name: valo_tenant -service_id: 149 -service_display_name: VALO Cloud -service_short_name: valoTenant -service_man: '# Инструкция по развертыванию Тенант Valo через платформу

## 1. Общая информация
Тенант Valo — логический изолированный экземпляр сервиса VALO Cloud, предоставляющий защищённое облачное хранилище, синхронизацию данных и доступ к документам с различных устройств. При создании тенант автоматически разворачивается в тестовом режиме сроком на 2 месяца. Перевод в промышленную эксплуатацию выполняется отдельной операцией.

### Доступные операции

* **create** — создание нового тенанта в тестовом режиме сроком на 2 месяца.
* **modify** — изменение настроек тенанта, включая перевод из ТЕСТ в ПРОД.
* **suspend** — выключение (приостановка работы) тенанта.
* **resume** — включение ранее приостановленного тенанта.
* **delete** — удаление тенанта; доступно только после выполнения операции suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создан тенант.
Пример значения: `valo.ngcloud.ru`.

### DNS-Префикс
Описание: Будет добавлено в зону valo.ngcloud.ru
Пример значения: `mysimpledomain`.

### Параметры хранилища
Описание: Сколько места будет выделено под использование в тенанте. Данные хранятся в S3. Размер указывается в Gigabytes
Пример значения: `10`.

### Настройки тенанта
Описание: Ряд конфигурации Valo-тенанта, такие как настройки пользователя и доступного функционала
Пример значения: `опционально в рамках настройки`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Достаточно развернуть тенант в ТЕСТ. Срок предоставляется в 2 месяца. По истечению срока тенант автоматически переводится в `suspend` на стороне ресурсной платформы

### Промышленное окружение
Для разблокировки используйте операцию `modify`

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Статус тенанта
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация
Тенант всегда создаётся в режиме ТЕСТ и не может быть сразу переведён в ПРОД без операции modify.
Удаление тенанта возможно только после его выключения с помощью операции suspend.
Срок тестового режима составляет 2 месяца с момента создания.
Операции suspend и resume используются для временного управления доступностью сервиса без потери конфигурации.
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 202 - kind: instance - action: create - params: - - id: 655 - code: resourceRealm - data_type: string - required: true - default: valo.ngcloud.ru - value_list: - - valo.ngcloud.ru - func: getAvailableResourceRealms - sort: 10 - - id: 656 - code: domain - data_type: string - required: true - regex: ^[a-z0-9](?:[a-z0-9-]{3,61}[a-z0-9])?$ - maxlength: 61 - minlength: 3 - descr: Будет добавлено в зону valo.ngcloud.ru - sort: 20 - - id: 657 - code: valoOptions - data_type: map-fixed - required: true - default: '{}' - sort: 40 - is_modifiable: true - sub_params: - - id: 20 - code: requestApproveMaxPeriod - data_type: integer > 0 - required: true - default: "2" - descr: Максимальное значение периода согласования (в днях) - is_modifiable: false - - id: 21 - code: ffOnlineEditor - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 22 - code: ffWatermark - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 23 - code: ffUserImport - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 24 - code: ffThemes - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 25 - code: ffPublicLink - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 26 - code: ffLogging - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 28 - code: userCount - data_type: integer > 0 - required: true - default: "10" - is_modifiable: false - - id: 658 - code: s3Storage - data_type: integer > 0 - required: true - default: "10" - maxvalue: 999 - minvalue: 1 - descr: Параметр указывается в `Gb` - man: Данные хранятся в отдельном экземпляре S3 - sort: 30 - is_modifiable: true - - name: delete - id: 206 - kind: instance - action: delete - params: [] - - name: modify - id: 203 - kind: instance - action: modify - params: - - id: 659 - code: valoOptions - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 32 - code: ffWatermark - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 33 - code: ffUserImport - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 34 - code: ffThemes - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 35 - code: ffPublicLink - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 36 - code: ffLogging - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 29 - code: userCount - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 30 - code: requestApproveMaxPeriod - data_type: integer > 0 - required: true - default: "" - descr: Максимальное значение периода согласования (в днях) - is_modifiable: false - - id: 31 - code: ffOnlineEditor - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Тарифная опция - is_modifiable: false - - id: 660 - code: s3Storage - data_type: integer > 0 - required: true - descr: Параметр указывается в `Gb` - man: Данные хранятся в отдельном экземпляре S3 - sort: 20 - - name: resume - id: 205 - kind: instance - action: resume - params: [] - - name: suspend - id: 204 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/150_k8s_sthutrval_cluster.yaml b/generated/test/resources_yaml/150_k8s_sthutrval_cluster.yaml deleted file mode 100644 index 1a3d0d2..0000000 --- a/generated/test/resources_yaml/150_k8s_sthutrval_cluster.yaml +++ /dev/null @@ -1,371 +0,0 @@ -name: k8s_sthutrval_cluster -service_id: 150 -service_display_name: Kubernetes кластер Штурвал -service_short_name: k8sSthutrvalCluster -service_man: Перед разворачиванием убедиться, что в облаке есть 2 свободных невыделенных IP в пуле -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 108 - kind: instance - action: create - params: - - id: 750 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 58 - code: nsxtUid - data_type: string - required: true - default: "" - is_modifiable: false - - id: 59 - code: clusterName - data_type: string - required: true - default: example-biu4ghxt - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 57 - code: vdcUid - data_type: string - required: true - default: "" - is_modifiable: false - - id: 751 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 61 - code: sizingPolicy - data_type: string - required: true - default: '[]' - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 62 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 63 - code: count - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 752 - code: workerConfiguration - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 40 - is_modifiable: true - sub_params: - - id: 64 - code: groupName - data_type: string - required: true - default: workers-g8poapqi - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 65 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 66 - code: sizingDisk - data_type: integer > 0 - required: true - default: "50" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 67 - code: count - data_type: integer > 0 - required: true - default: "2" - is_modifiable: false - - id: 753 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 50 - is_modifiable: true - sub_params: - - id: 68 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false - - id: 69 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 70 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - id: 71 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 755 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 72 - code: appVersion - data_type: string - required: true - default: 2.13.1 - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - name: create_user - id: 281 - kind: subresource - action: create - subresource: user - params: - - id: 869 - code: admins - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 10 - sub_params: - - id: 236 - code: email - data_type: string - required: true - default: "" - man: Пользователи будут добавлены, только если они зарегистрированы в личном кабинете - is_modifiable: false - - name: delete - id: 109 - kind: instance - action: delete - params: [] - - name: delete_user - id: 282 - kind: subresource - action: delete - subresource: user - params: - - id: 870 - code: admins - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 237 - code: email - data_type: string - required: true - default: "" - is_modifiable: false - - name: modify - id: 110 - kind: instance - action: modify - man: При изменении кол-ва узлов, удаления группы, если в этой группе есть поды на которых есть PV с named-дисками, удаляться ноды сами НЕ БУДУТ.
Убедитесь, что они будут смигрированы на другие ноды в процессе прежде чем группа узлов/узлы будут удалены - params: - - id: 756 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 73 - code: appVersion - data_type: string - required: true - default: "" - value_list: - - 2.13.1 - - 2.12.1 - - 2.11.0 - is_modifiable: false - - id: 757 - code: controlPlaneConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 74 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 75 - code: sizingDisk - data_type: integer > 0 - required: true - default: "" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 76 - code: count - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - is_modifiable: false - - id: 758 - code: workerConfiguration - data_type: array-map-fixed - required: true - sort: 30 - sub_params: - - id: 79 - code: sizingDisk - data_type: integer > 0 - required: true - default: "" - man: Дисковая политика берётся из услуги VDC, первый в списке - is_modifiable: false - - id: 80 - code: count - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 77 - code: groupName - data_type: string - required: true - default: workers-si00hlka - regex: (?=^.{3,40}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - man: Имена групп воркеров в рамках кластера должно быть уникальным - is_modifiable: false - - id: 78 - code: sizingPolicy - data_type: string - required: true - default: "" - value_list: - - '{}' - man: Доступные sizing Policy можно обновлять через услугу `Виртуальный датацентр` - is_modifiable: false - - id: 759 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 84 - code: accessIpListIngress - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Ingress. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 81 - code: needExternalAddressAPI - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к kubernetes API. Пока нельзя выставить false - is_modifiable: false - - id: 82 - code: accessIpListAPI - data_type: json - required: true - default: '[]' - man: Access IP List для подключения к Kubernetes API. Указывается массив. Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 83 - code: needExternalAddressIngress - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Выделяет внешний IP для доступа к Ingress - is_modifiable: false - - name: reconcile - id: 253 - kind: action - action: reconcile - params: [] - - name: resume - id: 174 - kind: instance - action: resume - params: [] - - name: suspend - id: 173 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/151_k8s_openbao.yaml b/generated/test/resources_yaml/151_k8s_openbao.yaml deleted file mode 100644 index 603abae..0000000 --- a/generated/test/resources_yaml/151_k8s_openbao.yaml +++ /dev/null @@ -1,391 +0,0 @@ -name: k8s_openbao -service_id: 151 -service_display_name: Vault -service_short_name: k8sOpenbao -service_man: '# Инструкция по развертыванию Хранилища секретов через платформу

## 1. Общая информация
Хранилище секретов — сервис централизованного и защищённого хранения чувствительных данных (секретов, ключей, конфигураций). Предназначен для использования приложениями и сервисами в Kubernetes с управлением доступом на основе ролей.
В качестве приложения используется открытый проект OpenBao с поддержкой кластерного режима RAFT.

### Доступные операции
**create** — Создание хранилища секретов и инициализация кластера.
**delete** — Удаление хранилища секретов с полной очисткой всех связанных сущностей.
**modify** — Изменение параметров развернутого хранилища секретов.
**suspend** — Приостановка работы кластера без удаления Persistent Volume.
**resume** — Возобновление работы кластера с автоматическим unseal всех нод.
**restart** — Поочередный перезапуск нод с последующим подключением в кластер.
**create_kv** — Создание Key/Value хранилища версии V2.
**delete_kv** — Удаление Key/Value хранилища со всеми содержащимися секретами.
**create_secrets** — Создание секрета внутри Key/Value хранилища.
**delete_secrets** — Удаление секрета из Key/Value хранилища.
**update_secrets** — Обновление значения секрета.
**recovery_secrets** — Откат секрета к указанной версии.
**create_role** — Создание роли для управления Key/Value хранилищем или секретами.
**update_role** — Обновление параметров роли.
**delete_role** — Удаление роли доступа.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: Кластер Kubernetes, в котором будет развернуто приложение.
Формат значения: имя Kubernetes-кластера.

### Имя домена
Описание: Доменное имя сервиса. При указании полного FQDN выполняется проверка DNS A-записи или добавляется А-запись в техническую зону.
Формат значения: secrets.domain.com / mysecretdomain.

### Кол-во реплик
Описание: Количество узлов в кластере.
Формат значения: целое число.

### Квота ядра пода
Описание: Лимит CPU для одного узла. Указывается в milicores (1000 milicores = 1 CPU).
Формат значения: целое число.

### Квота памяти пода
Описание: Лимит оперативной памяти для одного узла. Указывается в megabytes.
Формат значения: целое число.

### Размер диска под хранение секретов
Описание: Объем диска для хранения данных. Фактический объем равен значению, умноженному на количество реплик. Указывается в Gigabytes
Формат значения: целое число.

### Версия OpenBao
Описание: Версия OpenBao для развертывания сервиса.
Формат значения: выбор из предоставленного списка версий.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
Кол-во реплик: 2.
Квота ядра пода: 200.
Квота памяти пода: 256.
Размер диска под хранение секретов: 1.
Версия OpenBao: актуальная стабильная версия.

### Промышленное окружение
Кол-во реплик: от 3.
Квота ядра пода: от 1000.
Квота памяти пода: от 1024.
Размер диска под хранение секретов: от 10.
Версия OpenBao: актуальная стабильная версия.

---

## 4. Выходные параметры
Настройки внутри хранилища (kv/secrets/roles) — конфигурации Key/Value, секретов и ролей, созданные соответствующими операциями.
Настройки самого хранилище (сайзинг, объемы) — параметры ресурсов и хранилища данных, примененные к сервису.
Статус кластера (running / suspended) — текущее состояние работы кластера.
url для подключения к веб — адрес веб-интерфейса сервиса.

---

## 5. Дополнительная информация
При удалении хранилища все данные и настройки безвозвратно удаляются.
При использовании доменного имени требуется корректная настройка DNS.
Операции управления секретами и ролями выполняются только после успешного создания хранилища.
Удаление кластера производится через операцию `suspend`, а удаление становится доступно через 14 дней после остановки кластера
Правило выше не относится к операции `delete_kv` / `delete_secrets` / `delete_role`' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 210 - kind: instance - action: create - params: - - id: 663 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Кластер Kubernetes в котором будет развёрнуто приложение - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - sort: 0 - - id: 664 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 8000 - minvalue: 250 - descr: 'Пример: `500`' - man: Указывается в Milicores
`1000` Mili == `1` Ядро - sort: 30 - is_modifiable: true - - id: 665 - code: resourceInstances - data_type: integer > 0 - required: true - default: "3" - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `3`' - sort: 20 - is_modifiable: true - - id: 666 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 256 - descr: 'Пример: `2048`' - man: Указывается в Megabytes - sort: 40 - is_modifiable: true - - id: 667 - code: resourceDisk - data_type: integer > 0 - required: true - default: "10" - maxvalue: 999 - minvalue: 1 - descr: 'Пример: `10`' - man: Указывается в Gigabytes - sort: 50 - is_modifiable: true - - id: 668 - code: appVersion - data_type: string - required: true - default: 2.5.2 - value_list: - - 2.5.2 - descr: Выбрать из списка - sort: 60 - is_modifiable: true - - id: 669 - code: domain - data_type: string - required: true - default: bao-uqvjbvor - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - name: create_kv - id: 212 - kind: subresource - action: create - subresource: kv - params: - - id: 675 - code: kvName - data_type: string - required: true - default: kvname-yyz2zm23 - regex: ^[a-zA-Z0-9-]{1,24}$ - man: Будет создан `v2 тип движка`

Уникальное в рамках услуги - sort: 10 - - name: create_policy - id: 237 - kind: subresource - action: create - subresource: policy - params: - - id: 728 - code: policySettings - data_type: array-map-fixed - required: true - sort: 20 - sub_params: - - id: 50 - code: capabilities - data_type: json - required: true - default: '["read", "list"]' - man: 'Пример: `["create", "read", "update", "delete", "list", "sudo"]`
Только чтение: `["list", "read"]`
Изменение: `["read", "list", "create", "update", "delete"]`' - is_modifiable: false - - id: 49 - code: path - data_type: string - required: true - default: "" - man: 'Пример 1: `mykv/*`
Пример 2: `mykv/path/*`
Пример 3: `mykv/path/to/secret`
' - is_modifiable: false - - id: 729 - code: policyName - required: true - default: policy-r2ksc6sl - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - man: Сделано для дальнейшего удобного использования - sort: 10 - - name: create_secrets - id: 214 - kind: subresource - action: create - subresource: secrets - params: - - id: 677 - code: kvName - data_type: string - required: true - descr: Выберите из списка - man: Если список пустой, создайте kv через операцию `create_kv` - sort: 10 - - id: 678 - code: secretPath - data_type: string - required: true - default: path/to/mysecret - regex: ^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*$ - descr: 'Пример: `path` или `road/to/path`' - sort: 20 - - id: 679 - code: secretData - data_type: map - required: true - descr: Хотя бы один секрет необходимо добавить - man: Данные не сохраняются в ЛК, в дальнейшем при обновлении секретов это необходимо учитывать - sort: 30 - - name: delete - id: 231 - kind: instance - action: delete - params: [] - - name: delete_kv - id: 213 - kind: subresource - action: delete - subresource: kv - man: '**ОСТОРОЖНО**
Удаление KV подразумевает удаление ВСЕХ secrets внутри этой KV' - params: - - id: 676 - code: kvName - data_type: string - required: true - descr: Выберите из списка - sort: 10 - - name: delete_policy - id: 239 - kind: subresource - action: delete - subresource: policy - params: - - id: 732 - code: policyName - data_type: string - required: true - - name: delete_secrets - id: 233 - kind: subresource - action: delete - subresource: secrets - params: - - id: 726 - code: kvName - data_type: string - required: true - descr: Выберите из списка - sort: 10 - - id: 727 - code: secretPath - data_type: string - required: true - descr: Выберите из списка - sort: 20 - depends_on: kvName - - name: modify - id: 211 - kind: instance - action: modify - params: - - id: 670 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `3`' - sort: 10 - - id: 671 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 8000 - minvalue: 250 - descr: 'Пример: `500`' - man: Указывается в Milicores
`1000` Mili == `1` Ядро - sort: 20 - - id: 672 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 256 - descr: 'Пример: `2048`' - man: Указывается в Megabytes - sort: 30 - - id: 673 - code: resourceDisk - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: 'Пример: `10`' - man: Указывается в Gigabytes - sort: 40 - - id: 674 - code: appVersion - data_type: string - required: true - value_list: - - 2.5.2 - descr: Выбрать из списка - sort: 50 - - name: reconcile - id: 236 - kind: action - action: reconcile - params: [] - - name: recovery_secrets - id: 232 - kind: subresource - action: recovery - subresource: secrets - man: Данная операция подразумевает откат на конкретную версию в секрете.

**Это не восстановление удалённого секрета** - params: - - id: 723 - code: kvName - data_type: string - required: true - descr: Выберите из списка - sort: 10 - - id: 724 - code: secretPath - data_type: string - required: true - descr: Выберите из списка - sort: 20 - depends_on: kvName - - id: 725 - code: secretVersion - data_type: string - required: true - depends_on: kvName,secretPath - - name: restart - id: 234 - kind: action - action: restart - params: [] - - name: resume - id: 216 - kind: instance - action: resume - params: [] - - name: suspend - id: 215 - kind: instance - action: suspend - params: [] - - name: update_policy - id: 238 - kind: subresource - action: update - subresource: policy - params: - - id: 730 - code: policyName - data_type: string - required: true - sort: 10 - - id: 731 - code: policySettings - data_type: array-map-fixed - required: true - descr: Производится полная перезапись политики! - sort: 20 - sub_params: - - id: 52 - code: capabilities - data_type: string - required: true - default: "" - is_modifiable: false - - id: 51 - code: path - data_type: string - required: true - default: "" - is_modifiable: false - - name: update_secrets - id: 230 - kind: subresource - action: update - subresource: secrets - params: - - id: 719 - code: kvName - data_type: string - required: true - descr: Выберите из списка - sort: 10 - - id: 720 - code: secretPath - data_type: string - required: true - descr: Выберите из списка - man: Предварительно необходимо выбрать параметр `Имя Key/Value хранилища` - sort: 20 - depends_on: kvName - - id: 721 - code: needFullUpdateSecret - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Если выбрать `true`, будет создана новая версия секрета `с нуля`
Если выбрать `false`, будет создана новая версия секрета с данными прошлой ревизии, а переданные данные в операции либо будут модифицированы, либо будут добавлены - sort: 30 - - id: 722 - code: secretData - data_type: map - required: true - descr: Хотя бы один секрет необходимо добавить - sort: 40 diff --git a/generated/test/resources_yaml/163_llm_ai.yaml b/generated/test/resources_yaml/163_llm_ai.yaml deleted file mode 100644 index d749f60..0000000 --- a/generated/test/resources_yaml/163_llm_ai.yaml +++ /dev/null @@ -1,189 +0,0 @@ -name: llm_ai -service_id: 163 -service_display_name: LLM -service_short_name: llmAi -service_man: '## 1. Общая информация

LLMaaS (Large Language Model as a Service) — это сервис для доступа к языковым моделям через OpenAI-совместимое API.
Платформа позволяет создавать организации (тенанты), команды, выпускать и управлять API-ключами, устанавливать лимиты на использование.

---

## Доступные операции

- **create** — Создание организации в LLMaaS
- **delete** — Удаление организации

- **create_team** — Создание команды внутри организации
- **modify_team** — Редактирование команды (модели, RPM, TPM)
- **delete_team** — Удаление команды

- **create_secrets** — Создание API-ключа
- **update_secrets** — Обновление параметров API-ключа
- **delete_secrets** — Удаление API-ключа

---

## 2. Параметры создания организации

Ниже перечислены параметры, которые необходимо указать при создании организации:

* **Платформа для развертывания**
Выбирается автоматически, изменять не нужно.

* **Период бюджета**
Срок жизни организации в днях.
Пример: `30`


---

## 3. Параметры создания команды

* **Имя команды**
Уникальное имя команды в рамках организации.
Пример: `production`

* **Модель**
Название модели, доступной для команды.
Пример: `gpt-oss-120b`

* **Лимит запросов в минуту (RPM)**
Максимальное количество запросов к API в минуту.
Пример: `60`

* **Лимит токенов в минуту (TPM)**
Максимальное количество токенов (входящих + исходящих) в минуту.
Пример: `100000`

---

## 4. Параметры редактирования команды

Для существующей команды можно изменить:

* **Модель**
Новая модель для команды.
Пример: `gpt-oss-20b`

* **Лимит запросов в минуту (RPM)**
Новое значение RPM.
Пример: `120`

* **Лимит токенов в минуту (TPM)**
Новое значение TPM.
Пример: `200000`

---

## 5. Параметры создания API-ключа

Ниже перечислены параметры, которые необходимо указать при создании API-ключа:

* **Имя ключа**
Уникальное имя ключа в рамках организации.
Пример: `prod-key`

* **Команда**
Имя команды для привязки ключа. Если не указано — ключ привязывается к команде `main`.
Пример: `production`

* **Модель**
Название модели, доступной для данного ключа.
Пример: `gpt-oss-120b`

* **Лимит запросов в минуту (RPM)**
Максимальное количество запросов к API в минуту.
Пример: `60`

* **Лимит токенов в минуту (TPM)**
Максимальное количество токенов (входящих + исходящих) в минуту.
Пример: `100000`

**Важно:** Лимиты API-ключа не могут превышать лимиты команды, к которой он привязан.
При превышении операция будет отклонена с ошибкой.
Перед созданием ключа убедитесь, что `RPM <= team.RPM` и `TPM <= team.TPM`.


---

## 6. Параметры обновления API-ключа

Для уже существующего ключа можно изменить параметры. Все параметры являются опциональными:

* **Имя ключа**
Имя обновляемого ключа.
Пример: `prod-key`

* **Модель**
Новое название модели.
Пример: `gpt-oss-20b`

* **Лимит запросов в минуту (RPM)**
Новое значение RPM.
Пример: `120`

* **Лимит токенов в минуту (TPM)**
Новое значение TPM.
Пример: `200000`

**Важно:** При обновлении лимитов API-ключа новые значения не могут превышать лимиты команды.
Если указать значения выше командных, операция завершится ошибкой.
Указывайте `RPM <= team.RPM` и `TPM <= team.TPM`.

Команда определяется автоматически по стейту услуги — изменить привязку к другой команде через эту операцию нельзя.

---

## 7. Параметры удаления API-ключа

* **Команда**
Имя команды, к которой привязан ключ.
Пример: `production`

* **Имя ключа**
Имя удаляемого ключа.
Пример: `prod-key`

---

## 8. Параметры удаления команды

* **Имя команды**
Имя удаляемой команды.
Пример: `production`

---

## 9. Параметры удаления организации

Удаление организации не требует дополнительных параметров — удаляется вся организация со всеми командами и ключами.

---

## 10. Выходные параметры

После успешного создания организации платформа возвращает:

* **connectionUrl**
URL для доступа к inference API (OpenAI-совместимый).
Пример: `https://api.aillm.ru`

---


## 12. Пример использования API

После получения API-ключа клиент может обращаться к LLMaaS напрямую:

**Запрос к chat completions:**

```bash
curl https://api.aillm.ru/v1/chat/completions \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d ''{
"model": "gpt-oss-120b",
"messages": [
{"role": "user", "content": "Привет!"}
]
}''
```

**Получение списка доступных моделей:**

```bash
curl https://api.aillm.ru/v1/models \
-H "Authorization: Bearer sk-..."
```
' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 271 - kind: instance - action: create - params: - - id: 826 - code: resourceRealm - data_type: string - required: true - default: llmAi.aillm.ru - value_list: - - llmAi.aillm.ru - func: getAvailableResourceRealms - - name: create_secrets - id: 274 - kind: subresource - action: create - subresource: secrets - params: - - id: 833 - code: models - data_type: array - required: true - sort: 20 - depends_on: teamName - - id: 834 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - minvalue: 1 - - id: 835 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - minvalue: 1 - - id: 857 - code: keyName - data_type: string - required: true - sort: 10 - - id: 859 - code: teamName - data_type: string - required: true - sort: 15 - - name: create_team - id: 278 - kind: subresource - action: create - subresource: team - params: - - id: 858 - code: teamName - data_type: string - required: true - - id: 860 - code: models - data_type: array - required: true - - id: 861 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - - id: 862 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - - name: delete - id: 273 - kind: instance - action: delete - params: [] - - name: delete_secrets - id: 275 - kind: subresource - action: delete - subresource: secrets - params: - - id: 836 - code: keyName - data_type: string - required: true - depends_on: teamName - - id: 876 - code: teamName - data_type: string - required: true - sort: 10 - - name: delete_team - id: 280 - kind: subresource - action: delete - subresource: team - params: - - id: 868 - code: teamName - data_type: string - required: true - - name: modify_team - id: 279 - kind: subresource - action: modify - subresource: team - params: - - id: 864 - code: teamName - data_type: string - required: true - - id: 865 - code: models - data_type: array - required: true - depends_on: teamName - - id: 873 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - minvalue: 1 - - id: 874 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - minvalue: 1 - - name: update_secrets - id: 277 - kind: subresource - action: update - subresource: secrets - man: Данная операция предназначена для управления лимитами и моделями - params: - - id: 852 - code: keyName - data_type: string - required: true - depends_on: teamName - - id: 853 - code: models - data_type: array - required: true - depends_on: teamName - - id: 854 - code: rpmLimit - data_type: integer > 0 - required: true - default: "60" - - id: 855 - code: tpmLimit - data_type: integer > 0 - required: true - default: "100000" - - id: 875 - code: teamName - data_type: string - required: true - sort: 5 diff --git a/generated/test/resources_yaml/19_vc_org.yaml b/generated/test/resources_yaml/19_vc_org.yaml deleted file mode 100644 index 001435f..0000000 --- a/generated/test/resources_yaml/19_vc_org.yaml +++ /dev/null @@ -1,140 +0,0 @@ -name: vc_org -service_id: 19 -service_display_name: Организация в Cloud Director -service_short_name: vcOrg -service_man: '# Инструкция по развертыванию Организация в Cloud Director через платформу

## 1. Общая информация

Сервис **Организация в Cloud Director** предназначен для управления изолированными организационными единицами (тенантами) внутри инфраструктуры Cloud Director. Он обеспечивает создание, модификацию и администрирование организаций, предоставление им облачных ресурсов, а также контроль уровня доступа. Сервис используется для организации рабочих окружений потребителей, управления жизненным циклом организаций и их сетевой инфраструктурой.

### Доступные операции

**create** — Создание новой организации с заданными характеристиками и типом доступа.
**delete** — Удаление организации после перехода в состояние suspend; приводит к удалению всех связанных сущностей.
**modify** — Изменение параметров внутри организации (на текущий момент поддерживается изменение ipSpaces).
**suspend** — Заморозка организации, ограничение возможности изменения и добавления сущностей.
**resume** — Разморозка организации, восстановление функций управления.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Платформа для развертывания
Описание: платформа, на которой будет создана организация.
Пример значения: `ngcloud.ru`.

### Тип организации
Описание: тип создаваемой организации.
Варианты:
- `iaas` — организация с прямым доступом к облаку и авторизацией через Keycloak.
- `saas` — организация под управлением Nubes, без прямого доступа к облаку.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Тип организации: `iaas` для обеспечения полного доступа к облачным ресурсам и самостоятельного управления.

### Промышленное окружение
- Тип организации: `saas` (упрощённая модель использования).

---

## 4. Выходные параметры

Услуга предоставляет следующие выходные параметры (формируются в процессе операций):

### Url организации
Для быстрого доступа в тенант

### Статус организации
Отражает текущее состояние и параметры.

---

## 5. Дополнительная информация

- Для конфигурации внешних IP внутри организации необходимо предварительно создать:
-- хотя бы один **Виртуальный датацентр (VDC)** привязанный к этой организации;
-- хотя бы один **Сетевой шлюз периметра (Edge Gateway)**, привязанный к vdc выше.
- Удаление организации возможно только через 14 дней после перевода в состояние suspend.
- Удаление организации приводит к удалению всех связанных сущностей: VDC, Edge Gateway, пулов IP, сетей, ВМок и иных объектов.
- Операция suspend не останавливает виртуальные машины, но блокирует любые изменения инфраструктуры внутри организации.
- Для iaas-организаций доступ осуществляется через Keycloak.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 136 - kind: instance - action: create - man: '`IaaS` - Доступ в Keycloak предоставляется пользователю, который создаёт услугу
`SaaS` - Без доступа к Keycloak' - params: - - id: 418 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Облако в котором будет развёрнут тенант - man: Выбрать платформу из списка

**Если список не отображается**
обратитесь к сервис менеджеру с вопросом предоставления ресурсной платформы - sort: 10 - - id: 556 - code: organizationType - data_type: string - required: true - default: iaas - value_list: - - iaas - - saas - man: Доступ к организации предоставляется только при выборе `iaas` - sort: 20 - - name: create_user - id: 283 - kind: subresource - action: create - subresource: user - params: - - id: 871 - code: admins - data_type: array-map-fixed - required: true - default: '[{}]' - sort: 10 - sub_params: - - id: 238 - code: email - data_type: string - required: true - default: "" - man: Пользователи будут добавлены, только если они зарегистрированы в личном кабинете - is_modifiable: false - - name: delete - id: 137 - kind: instance - action: delete - man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого тенанта**

Если существуют инстансы VDC, Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' - params: [] - - name: delete_user - id: 284 - kind: subresource - action: delete - subresource: user - params: - - id: 872 - code: admins - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 239 - code: email - data_type: string - required: true - default: "" - is_modifiable: false - - name: modify - id: 207 - kind: instance - action: modify - man: Для возможности изменения параметров предварительно необходимо создать **Виртуальный датацентр (VDC)**, потом **Сетевой шлюз периметра (Edge Gateway)**
Также при удалении ipSpaces убедитесь, что все внешние IP удалены. В случае если хоть одна привязка vIP есть, данный ipSpace удалён **не будет** - params: - - id: 662 - code: vIPConfigure - data_type: array-map-fixed - required: true - sort: 10 - sub_params: - - id: 39 - code: name - data_type: string - required: true - default: "" - is_modifiable: false - - id: 40 - code: count - data_type: integer > 0 - required: true - default: "" - descr: 'Пример: `1`' - is_modifiable: false - - name: reconcile - id: 249 - kind: action - action: reconcile - params: [] - - name: resume - id: 144 - kind: instance - action: resume - params: [] - - name: suspend - id: 143 - kind: instance - action: suspend - man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри тенанта - params: [] diff --git a/generated/test/resources_yaml/1_dummy.yaml b/generated/test/resources_yaml/1_dummy.yaml deleted file mode 100644 index 1e0f20e..0000000 --- a/generated/test/resources_yaml/1_dummy.yaml +++ /dev/null @@ -1,339 +0,0 @@ -name: dummy -service_id: 1 -service_display_name: Болванка -service_short_name: dummy -service_man: '## Болванка для тестов.

А это просто много букв, чтобы посмотреть, как ведет себя текст при переполнении контейнера.


``` json
{
"iamServiceUrl": "https://auth-api-test.ngcloud.ru/api/v1/user",
"idpResponse": {
"prefix": {
"charset": "",
"status_text": "Request Time-out",
"text": true,
"errordetail": "",
"filecontent": "Connection Timeout",
"responseheader": {},
"header": "",
"statuscode": "408 Request Time-out",
"status_code": 408,
"mimetype": "Unable to determine MIME type of file."
}
}
}
```' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 18 - kind: instance - action: create - man: Это мануал к операции CREATE сервиса Болванка - params: - - id: 198 - code: durationMs - data_type: integer >= 0 - required: true - default: "0" - descr: перед завершением подождать (sleep) столько миллисекунд - man: перед завершением подождать (sleep) столько миллисекунд - sort: 20 - is_modifiable: true - - id: 199 - code: failAtStart - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, завершить джоб, ничего не записывая в cmdb - man: '**Операция не будет завершена**
Она будет в постоянном статусе PENDING, однако в Jenkins Job завершится с ошибкой' - sort: 30 - is_modifiable: true - - id: 200 - code: failInProgress - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - man: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - sort: 40 - is_modifiable: true - - id: 201 - code: whereFail - data_type: integer > 0 - required: false - default: "1" - value_list: - - "1" - - "2" - - "3" - descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - sort: 50 - is_modifiable: true - - id: 242 - code: resourceRealm - data_type: string - required: true - default: dummy - value_list: - - dummy - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы для подключения - sort: 10 - - id: 286 - code: bodymessage - data_type: string - required: false - descr: Дата, уходящая в Vault - man: Дата, уходящая в Vault - sort: 60 - is_modifiable: true - - id: 321 - code: mapExample - data_type: map - required: false - is_modifiable: true - sub_params: - - id: 2 - code: subparam2 - data_type: string - required: false - default: "" - is_modifiable: false - - id: 1 - code: subparam1 - data_type: string - required: false - default: "" - descr: это для администратора - man: это для пользователя - is_modifiable: false - - id: 3 - code: secret - data_type: string - required: false - default: "" - descr: это секрет - man: значение параметра маскируется. Write Only - is_modifiable: false - is_sensitive: true - - id: 322 - code: jsonExample - data_type: json - required: false - descr: Для тестирования интерфейса - man: Для тестирования интерфейса. Сюда можно писать корректный json - is_modifiable: true - - id: 396 - code: nestedRefExample - required: false - - id: 647 - code: mapFixed - data_type: map-fixed - required: false - descr: Новый тип поля - sub_params: - - id: 13 - code: bol1 - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Булевский, обязательный - man: Булевский, обязательный. Проверить, как воспринимается снятый флаг - is_modifiable: false - - id: 14 - code: minStr1 - data_type: string - required: true - default: abracadabra - value_list: - - abracadabra - descr: не меньше 10 символов - is_modifiable: false - - id: 10 - code: param1 - data_type: integer > 0 - required: true - default: "1" - descr: Integer, secret - man: integer > 0, sensitive data (secret) - is_modifiable: false - is_sensitive: true - - id: 11 - code: param2 - data_type: string - required: true - default: "1" - value_list: - - '["1"' - - '"2"' - - '"3"]' - descr: Дескрипшен - man: Выбор из списка 1,2,3 - is_modifiable: false - - id: 12 - code: param3 - data_type: integer > 0 - required: true - default: "1" - descr: целое положительное - man: int > 0, обязательный - is_modifiable: false - - id: 654 - code: arrayMapFixedExample - data_type: array-map-fixed - required: false - descr: Array of map-fixed - sub_params: - - id: 18 - code: bol1 - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - is_modifiable: false - - id: 19 - code: minStr1 - data_type: string - required: true - default: "" - value_list: - - abracadabra - descr: не меньше 10 символов - is_modifiable: false - - id: 15 - code: param1 - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - is_sensitive: true - - id: 16 - code: param2 - data_type: string - required: true - default: "" - value_list: - - '["1"' - - '"2"' - - '"3"]' - is_modifiable: false - - id: 17 - code: param3 - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 863 - code: arr - data_type: array - required: true - value_list: - - test - - "2" - - val1 - - name: delete - id: 71 - kind: instance - action: delete - params: [] - - name: modify - id: 92 - kind: instance - action: modify - params: - - id: 287 - code: durationMs - data_type: integer >= 0 - required: true - default: "0" - descr: перед завершением подождать (sleep) столько миллисекунд - man: перед завершением подождать (sleep) столько миллисекунд - sort: 10 - - id: 288 - code: failAtStart - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, завершить джоб, ничего не записывая в cmdb - sort: 20 - - id: 289 - code: failInProgress - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: если true, корректно начать выполнение джоба, но в процессе завершить аварийно, ничего не записывая в cmdb - sort: 30 - - id: 290 - code: whereFail - data_type: integer > 0 - required: false - value_list: - - "1" - - "2" - - "3" - descr: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - man: принимает 1,2,3. Работает с failInProgress. 1 - упасть в prepare. 2 - упасть внутри блока Data fill. 3 - упасть после vault write - sort: 40 - - id: 291 - code: bodymessage - required: false - descr: Дата, уходящая в Vault - man: Дата, уходящая в Vault - sort: 50 - - id: 373 - code: mapExample - data_type: map - required: false - default: '{}' - sub_params: - - id: 4 - code: secret - data_type: string - required: true - default: "" - is_modifiable: false - is_sensitive: true - - id: 374 - code: jsonExample - required: false - - name: reconcile - id: 257 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 240 - kind: action - action: redeploy - params: [] - - name: resume - id: 94 - kind: instance - action: resume - params: [] - - name: suspend - id: 93 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/20_vc_org_saas.yaml b/generated/test/resources_yaml/20_vc_org_saas.yaml deleted file mode 100644 index 12a3c8b..0000000 --- a/generated/test/resources_yaml/20_vc_org_saas.yaml +++ /dev/null @@ -1,60 +0,0 @@ -name: vc_org_saas -service_id: 20 -service_display_name: Организация [DEPRECATED] -service_short_name: vcOrgSaas -service_man: 'Верхнеуровневая сущность создаваемая для Заказчика.
Внутри Организация вы можете создать: Виртуальные датацентры, Каталоги.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 8 - kind: instance - action: create - man: Создание организации в Cloud Director
Id в клауд директоре создаётся на основе contragentCode - params: - - id: 185 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование ресурсной платформы - sort: 30 - - name: delete - id: 23 - kind: instance - action: delete - params: [] - - name: resume - id: 119 - kind: instance - action: resume - params: [] - - name: suspend - id: 118 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/21_vc_vdc.yaml b/generated/test/resources_yaml/21_vc_vdc.yaml deleted file mode 100644 index 44c3cbb..0000000 --- a/generated/test/resources_yaml/21_vc_vdc.yaml +++ /dev/null @@ -1,169 +0,0 @@ -name: vc_vdc -service_id: 21 -service_display_name: Виртуальный датацентр (vDC) -service_short_name: vc_vdc -service_man: '# Инструкция по развертыванию Виртуальный датацентр (vDC) через платформу

## 1. Общая информация

**Виртуальный датацентр (vDC)** — это логическая изолированная среда в VMware Cloud Director, предназначенная для размещения и управления виртуальными машинами, сетевой инфраструктурой и ресурсами хранения. Сервис обеспечивает выделение вычислительных ресурсов под конкретную организацию и предоставляет гибкие параметры масштабирования, контроля потребления и разделения ресурсов.

### Доступные операции

**create** — Создание виртуального датацентра (vDC) с заданными параметрами CPU, RAM, Storage и сетевой конфигурацией.
**delete** — Удаление vDC. Требуется предварительный перевод vDC в состояние *suspend*. Полное удаление возможно через 14 дней.
**modify** — Внесение изменений в параметры ресурсов (CPU/RAM/Storage), выделенных для vDC.
**suspend** — Заморозка vDC. Новые ресурсы или изменения внутри vDC становятся невозможными, работа ВМ не останавливается.
**resume** — Разморозка vDC. Восстановление возможности управления сущностями внутри vDC.

---

## 2. Параметры развертывания

Ниже перечислены входные параметры операции **create**.

### UUID Организации
Уникальный идентификатор организации в Cloud Director. Перед созданием vDC требуется предварительное создание услугу `Организация в Cloud Director`.

### Provider VDC в Cloud Director
Свойство ресурсной платформы, определяющее, какой пул физических ресурсов будет использоваться.

### Network Pool в Cloud Director
Определяет возможности сетевой конфигурации, включая создание Edge Gateway и выход во внешние сети.

### Гарантированная доля ЯДЕР
Гарантированный процент вычислительных ресурсов (CPU), закрепленный за vDC. Значение устанавливается разово при создании.

### Гарантированная доля ОЗУ
Гарантированный процент оперативной памяти, закрепленный за vDC. Также задаётся один раз при создании.

### Сколько аллоцировать на VDC ЯДЕР
Количество виртуальных ядер, выделяемых в рамках vDC.

### Сколько аллоцировать на VDC ОЗУ
Количество оперативной памяти в GB, выделяемой для vDC.

### Конфигурация Storage
Набор дисковых политик и их объёмов, выделяемых для vDC. Указывается имя политики и требуемый размер (GB).

---

## 3. Рекомендованные характеристики

### Тестовое окружение

- Количество ядер CPU: 10–20
- Аллоцируемая RAM: 16–64 GB
- Гарантированная доля CPU: 20%
- Гарантированная доля RAM: 20%
- Storage:
- Политика `SATA`
- Объём: 100–300 GB

### Промышленное окружение

- Количество ядер CPU: 16–128
- Аллоцируемая RAM: 64–512 GB
- Гарантированная доля CPU: 50%
- Гарантированная доля RAM: 50%
- Storage:
- Политики уровня `SSD`
- Объём: 1–10 TB, выбор несколько политик под разные нагрузки

---

## 4. Выходные параметры

Выходные параметры формируются операцией **create** (и используются другими операциями):

### Имя vDC
Уникальный идентификатор созданного виртуального датацентра. Используется в дальнейших операциях (modify, suspend, delete).

### Параметры выделенных ресурсов
Подтверждение итогового объёма CPU/RAM/Storage, выделенных vDC. Используются для учёта и контроля потребления.

---

## 5. Дополнительная информация

- Перед созданием vDC необходимо обеспечить наличие корректно созданной организации и доступных пула ресурсов ресурсной платформы.
- Гарантированная доля CPU и RAM задаётся **однократно при создании vDC**. После развертывания изменить эти значения невозможно; для изменения необходимо пересоздать vDC.
- Удаление vDC возможно только через **14 дней после перевода в состояние *suspend***. Удаление очищает все связанные сущности (ВМ, сети, каталоги), поэтому необходимо убедиться, что данные перенесены.
- Платформа Cloud Director использует концепцию изолированных организационных ресурсов, поэтому корректная настройка Provider VDC и Network Pool является обязательной при создании vDC.
- При изменении параметров (modify) изменяются только показатели CPU, RAM и Storage. Сетевые параметры и гарантированные доли остаются неизменными.
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 9 - kind: instance - action: create - params: - - id: 30 - code: organizationUid - data_type: uuid - required: true - ref_svc_id: 19 - man: Для создания VDC предварительно необходимо создать организацию через услугу `Организация в Cloud Director` - sort: 10 - - id: 335 - code: providerVdc - data_type: string - required: true - man: Приписка `2.8` - Скорость vCPU - 2800MHz
И так далее...
- sort: 30 - depends_on: organizationUid,networkProvider - - id: 361 - code: storageConfig - data_type: array-map-fixed - required: true - default: '[{}]' - man: Первая политика в списке указывается как "По умолчанию" - sort: 100 - is_modifiable: true - sub_params: - - id: 41 - code: name - data_type: string - required: true - default: "" - descr: getKeyListFromStruct(getResourceRealmConfig(getStringFromStruct(getInstanceState(params.organizationUid), "params.resourceRealm")), concat("vcd.hardware.providerVdcs['",params.providerVdc,"'].storage")) - is_modifiable: false - - id: 42 - code: size - data_type: integer > 0 - required: true - default: "2000" - is_modifiable: false - - id: 397 - code: cpuGuaranteed - data_type: integer > 0 - required: true - default: "20" - value_list: - - "20" - - "50" - - "80" - descr: '% резервирования' - man: Свойство нельзя изменить после создания - sort: 60 - - id: 557 - code: cpuAllocated - data_type: integer > 0 - required: true - default: "80" - maxvalue: 1000 - minvalue: 1 - descr: шт - sort: 80 - is_modifiable: true - - id: 558 - code: memAllocated - data_type: integer > 0 - required: true - default: "200" - maxvalue: 4096 - minvalue: 1 - descr: gb - sort: 90 - is_modifiable: true - - id: 746 - code: networkProvider - data_type: string - required: true - sort: 20 - depends_on: organizationUid - - name: delete - id: 24 - kind: instance - action: delete - man: 'Удалить возможно только через 14 дней после выполнения операции `suspend`
**ОСТОРЖНО: Удаление приводит к полному удалению всех сущностей внутри этого VDC**

Если существуют инстансы Edge, vApp, Vm или Kubernetes-кластер Штурвал, которые были созданы, из ЛК удалены не будут. Необходимо обратиться к `технической поддержке` для удаления инстансов' - params: [] - - name: modify - id: 112 - kind: instance - action: modify - params: - - id: 560 - code: cpuAllocated - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: шт - sort: 10 - - id: 561 - code: memAllocated - data_type: integer > 0 - required: true - maxvalue: 1024 - minvalue: 1 - descr: gb - sort: 20 - - id: 562 - code: storageConfig - data_type: array-map-fixed - required: true - man: Первая политика в списке указывается как "По умолчанию" - sort: 30 - sub_params: - - id: 43 - code: name - data_type: string - required: true - default: "" - is_modifiable: false - - id: 44 - code: size - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - name: reconcile - id: 250 - kind: action - action: reconcile - params: [] - - name: resume - id: 172 - kind: instance - action: resume - params: [] - - name: suspend - id: 171 - kind: instance - action: suspend - man: Операция **не останавливает ВМки** в облаке, но не даёт редактировать все сущности внутри VDC - params: [] diff --git a/generated/test/resources_yaml/22_vc_nsxt.yaml b/generated/test/resources_yaml/22_vc_nsxt.yaml deleted file mode 100644 index 158f59e..0000000 --- a/generated/test/resources_yaml/22_vc_nsxt.yaml +++ /dev/null @@ -1,139 +0,0 @@ -name: vc_nsxt -service_id: 22 -service_display_name: Сетевой шлюз периметра (Edge) -service_short_name: vc_nsxt -service_man: '# Инструкция по развертыванию Сетевой шлюз периметра (Edge) через платформу

## 1. Общая информация

Сервис **Сетевой шлюз периметра (Edge)** предназначен для создания периметрового сетевого шлюза в рамках одного виртуального датацентра (vDC) или группы виртуальных датацентров (groupvDC).
При создании автоматически разворачивается routed-сеть с адресным пространством `10.10.102.0/24`.
Сервис обеспечивает сетевую изоляцию, маршрутизацию, а также может включать функциональность балансировщика нагрузки AVI (ALB) для последующей интеграции, включая поддержку кластеров Штурвал.

### Доступные операции

**create** — Создание нового Edge с привязкой к vDC или groupvDC и развёртыванием routed-сети.
**delete** — Удаление ранее созданного Edge. Недоступно при наличии зависимых услуг (vApp, VM, Кластер Штурвал).
**modify** — Изменение параметров и сетевых настроек существующего Edge.

---

## 2. Параметры развертывания

Ниже приведены параметры операции **create**.

### Тип родительской услуги
Определяет контекст размещения Edge.
Допустимые значения: `vdc`, `groupvdc`.
Использование:
- При выборе `vdc` обязателен параметр **UUID VDC**.
- При выборе `groupvdc` обязателен параметр **UUID Группы VDC**.

### UUID VDC
Идентификатор виртуального датацентра.
Необходимо предварительно создать vDC через услугу «Виртуальный датацентр (vDC)».
Указывается только при выборе родительского типа `vdc`.

### UUID Группы VDC
Идентификатор группы виртуальных датацентров.
Создаётся через услугу «Группа виртуальных датацентров (groupvDC)».
Используется при выборе родительского типа `groupvdc`.

### Включить ALB
Флаг активации AVI Load Balancer в Cloud Director.
Пример значения: `true`/`false`.
Нужен для развёртывания кластера Штурвал и возможности создания Virtual Services.

### Наименование segroup
Имя сервиса групп (segroup) на уровне ресурсной платформы.
Обязательно при включённом ALB.
Определяет группу, в которой будут выделяться пулы AVI для Virtual Services.

### virtualServicesCount
Количество резервируемых виртуальных сервисов (Virtual Services) на AVI.
Типичное значение: 1–10 в зависимости от нагрузки.

---

## 3. Рекомендованные характеристики

### Тестовое окружение (Test)

- Тип родительской услуги: `vdc`
- virtualServicesCount: 1–2
- ALB: выключен по умолчанию (включать только при необходимости тестирования Штурвала)
- segroup: задаётся только при включённом ALB
- VDC гарантии CPU/RAM: минимальные (поскольку изменить их после создания нельзя)

### Промышленное окружение (Production)

- Тип родительской услуги: `groupvdc` при работе с распределёнными нагрузками или `vdc` для локализованных проектов
- virtualServicesCount: 3–10, исходя из предполагаемого количества публикаций
- ALB: включён, если требуется высокая доступность сервисов или используется кластер Штурвал
- segroup: рекомендуется использовать выделенную группу под проект
- VDC гарантии CPU/RAM: повышенные, с учётом того, что изменить их после создания невозможно без пересоздания vDC

---

## 4. Выходные параметры

### Имя Edge
Уникальное название созданного сетевого шлюза, используется для ссылок и дальнейших операций.

### Имя routed-сети
Автоматически созданная routed-сеть с подсетью `10.10.102.0/24`, используется для подключения ресурсов.

### Параметры Edge
Набор параметров, указанных пользователем при создании: тип услуги, ALB, segroup, virtualServicesCount и др.
Используются в операциях modify и для анализа состояния сервиса.

---

## 5. Дополнительная информация

- В рамках сервиса **один раз при создании задаётся гарантированная доля CPU/RAM vDC**. Изменить её невозможно — требуется пересоздание vDC.
- Удаление Edge недоступно при наличии зависимых ресурсов (vApp, VM, кластер Штурвал).
- При использовании ALB необходимо корректно указывать segroup, чтобы обеспечить корректное выделение пулов AVI.
- При выборе режима `groupvdc` следует учитывать распределение нагрузки между несколькими vDC.
- SNAT для всех ресурсов внутри Edge можно активировать через операцию modify (параметры выделения VIP и ipSpace).

' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 10 - kind: instance - action: create - params: - - id: 8 - code: vdcUid - data_type: string - required: false - ref_svc_id: 21 - descr: UUID Услуги `Виртуальный датацентр (vDC)` - man: 'Необходимо, если выбран тип подключаемой VDC: `vdc`' - sort: 10 - - id: 340 - code: needEnableAVI - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: Включение Load Balancer - man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать - sort: 30 - is_modifiable: true - - id: 341 - code: virtualServicesCount - data_type: integer > 0 - required: false - default: "1" - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые **резервируются** на AVI - man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` - sort: 50 - is_modifiable: true - - id: 621 - code: vdcType - data_type: string - required: true - default: vdc - value_list: - - vdc - - vdcGroup - descr: Тип родительской услуги - man: При выборе vdc обеспечивает сетевую доступность только в рамках этого vdc
При выборе groupvdc обеспечивает сетевую доступность между всеми vdc, которые включены в groupvdc - sort: 0 - - id: 622 - code: vdcGroupUid - data_type: string - required: false - ref_svc_id: 29 - descr: UUID Услуги `Группа виртуальных датацентров (groupvDC)` - man: 'Необходимо, если выбран тип подключаемой VDC: `groupvdc`' - sort: 20 - - id: 825 - code: qosProfile - required: false - descr: Параметр пока не работает
Должен возвращать поле из ресурсной платформы типа vc из .vcd.hardware.gatewayQoSProfiles

Как по идее должен работать.
Нужно заполнить или CFS vdcUid, или vdcGroupUid
Идеально конечно проверять `if (params.vdcType == "vdc" && params.vdcUid != "")` или `if (params.vdcType == "vdcGroup" && params.vdcGroupUid != "")`

Далее надо пойти по стейту vdc -> org -> resPlatform, взять gatewayQoSProfiles
Или пойти по стейту vdcgroup -> vdc -> org -> resPlatform, взять gatewayQoSProfiles

Регулярку могу написать (Виталя) - man: if (vdcUid != '") {
наборфункций1
}

elif (vdcGroupUid != "") {
наборфункций2
}

else {
return "Необходимо выбрать vdc или vdcgroup"
} - sort: 60 - depends_on: vdcUid,vdcGroupUid - is_modifiable: true - - name: delete - id: 25 - kind: instance - action: delete - man: Невозможно удалить если routed-сеть в Edge связана с:
- vApp
- VM
- Kubernetes-кластер Штурвал

Запустить операцию возможно, но будет ошибка - params: [] - - name: modify - id: 111 - kind: instance - action: modify - man: Для создания SNAT Правила убедитесь, что в организации есть свободные IP
Редактировать кол-во свободных IP можно в услуге `Организация в Cloud Director` -> `modify` - params: - - id: 368 - code: needEnableAVI - data_type: boolean - required: false - value_list: - - "false" - - "true" - descr: Включение Load Balancer - man: Параметры `Наименование segroup`, `Кол-во VS на AVI` необходимо также указать - sort: 10 - - id: 369 - code: virtualServicesCount - data_type: integer > 0 - required: false - maxvalue: 4 - minvalue: 1 - descr: Кол-во виртуальных сервисов, которые выделяются на AVI - man: Во избежании коллапса пока выделяется до 4
Необходимо указывать, если включён параметр `Включить ALB` - sort: 30 - - id: 372 - code: ipSpaceName - data_type: string - required: false - descr: Имя ip Space для внешнего IP - man: Необходимо указывать, если включён параметр `Выделить VIP для SNAT` - sort: 50 - - id: 856 - code: qosProfile - data_type: string - required: false - sort: 40 - - name: reconcile - id: 252 - kind: action - action: reconcile - params: [] diff --git a/generated/test/resources_yaml/23_vc_vm.yaml b/generated/test/resources_yaml/23_vc_vm.yaml deleted file mode 100644 index 470873d..0000000 --- a/generated/test/resources_yaml/23_vc_vm.yaml +++ /dev/null @@ -1,153 +0,0 @@ -name: vc_vm -service_id: 23 -service_display_name: VM в Cloud Director (старый формат) (vc_vm) -service_short_name: vc_vm -service_man: Виртуальная машина без каких-либо особенностей.
Данная услуга запускает vc_ip для выдачи белого адреса и подключения к ВМ -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 26 - kind: instance - action: create - params: - - id: 33 - code: vappUid - data_type: uuid - required: true - ref_svc_id: 26 - descr: InstanceUid vApp. vApp должен быть создан - man: InstanceUid vApp. vApp должен быть создан - sort: 10 - - id: 35 - code: vmName - data_type: string - required: true - default: vm0001 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - descr: Имя ВМ. Не поддерживает спец-символы - man: Имя ВМ. Не поддерживает спец-символы - sort: 20 - - id: 36 - code: vmCpu - data_type: string - required: true - default: "2" - descr: CPU - man: CPU - sort: 30 - is_modifiable: true - - id: 37 - code: vmRam - data_type: string - required: true - default: "2" - descr: RAM (GB) - man: RAM (GB) - sort: 40 - is_modifiable: true - - id: 38 - code: vmDisksize - data_type: string - required: true - default: "10" - descr: Размер диска под сервис (GB) - man: Размер диска под сервис (GB) - sort: 50 - is_modifiable: true - - id: 176 - code: templateName - data_type: string - required: true - default: RockyLinux_9-16G - value_list: - - RockyLinux_9-16G - - OracleLinux_9-16G - - Ubuntu_24-20G - - Ubuntu_22-20G - descr: ОС - man: ОС - sort: 60 - - id: 177 - code: accessIpList - data_type: string - required: true - default: 79.111.119.78/32 185.247.187.1/32 - descr: Вставить содержимое из 'curl ifconfig.io'
IP адреса в CIDR формате через пробел для настройки аксес листов - man: IP адреса в CIDR формате через пробел для настройки аксес листов - sort: 70 - is_modifiable: true - - id: 406 - code: ipSpaceName - data_type: string - required: true - descr: Брать из свойств ресурсной платформы
.vcd.ipSpace

Имя IP-space для выделения внешнего IP (IP выделяется обязательно) - - name: delete - id: 29 - kind: instance - action: delete - params: [] - - name: modify - id: 68 - kind: instance - action: modify - man: Операция автоматически останавливает ВМ. - params: - - id: 186 - code: vmCpu - data_type: integer > 0 - required: false - descr: CPU - man: CPU - sort: 10 - - id: 187 - code: vmRam - data_type: integer > 0 - required: false - descr: RAM - man: RAM - sort: 20 - - id: 188 - code: vmDisksize - data_type: integer > 0 - required: false - descr: Размер диска под сервис (GB) - man: Размер диска под сервис (GB) - sort: 30 - - id: 189 - code: accessIpList - data_type: string - required: false - descr: IP адреса в CIDR формате через пробел для настройки аксес листов - man: IP адреса в CIDR формате через пробел для настройки аксес листов - sort: 40 - - name: resume - id: 28 - kind: instance - action: resume - params: [] - - name: suspend - id: 27 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/25_vcexternalip.yaml b/generated/test/resources_yaml/25_vcexternalip.yaml deleted file mode 100644 index 3a91400..0000000 --- a/generated/test/resources_yaml/25_vcexternalip.yaml +++ /dev/null @@ -1,165 +0,0 @@ -name: vcexternalip -service_id: 25 -service_display_name: Публичные IP адреса -service_short_name: vcexternalip -service_man: '**Обычно эта услуга разворачивается автоматически при развёртывании более сложных услуг**
Вручную создавать можно, однако услуга требует много параметров, о которых можно просто не знать

Резервировать публичные IP адреса можно только после создания NGcloud Organization, NGcloud Виртуальный датацентр, NGcloud Каталог, Сетевой шлюз периметра.
Вы можете назначить IP адрес для существующего экземпляра сервиса.' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 56 - kind: instance - action: create - params: - - id: 140 - code: serviceUid - data_type: string - required: true - sort: 20 - is_modifiable: true - - id: 141 - code: dnatCreate - data_type: boolean - required: true - default: "true" - descr: Необходимо создать DNAT-правило. - sort: 40 - is_modifiable: true - - id: 142 - code: internalPortAccess - data_type: string - required: true - default: "5432" - descr: Порт для связки ната с адресом. Несколько значений можно передавать через пробел. Если передать all, то dnat будет на все порты. Требует dnatCreate=true
- sort: 60 - is_modifiable: true - - id: 143 - code: snatCreate - data_type: boolean - required: false - descr: Необходимо создать SNAT-правило.
- sort: 50 - is_modifiable: true - - id: 144 - code: internalAddrAccess - data_type: string - required: true - descr: Внутренний адрес для связки ната. Требует dnatCreate=true или snatCreate=true - sort: 70 - is_modifiable: true - - id: 324 - code: fromServiceNamespace - data_type: string - required: true - descr: Namespace составной услуги (для отображения правил FW) - sort: 80 - - id: 325 - code: fromServiceCloudEdgeName - data_type: string - required: false - descr: Edge составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 90 - - id: 326 - code: fromServiceCloudVdcName - required: false - descr: Vdc составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 100 - - id: 327 - code: fromServiceCloudOrgName - data_type: string - required: false - descr: Org составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 110 - - id: 328 - code: fromServiceCloudVmwareUrl - data_type: string - required: false - descr: Api-Url составной услуги (указывать, если resourceRealm связан с CloudDirector) - sort: 120 - - id: 336 - code: ipSpaceName - data_type: string - required: true - descr: Тип ip Space (internet-ipv4-v1 / internet-antiddos-v1 / internet-no-antiddos-v1) - sort: 30 - is_modifiable: true - - id: 488 - code: resourceRealm - data_type: string - required: true - default: sandbox.nubes.ru - value_list: - - sandbox.nubes.ru - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 200 - - id: 624 - code: fromServiceCloudEdgeScope - data_type: string - required: false - sort: 130 - - id: 626 - code: fromServiceVdcGroupName - data_type: string - required: false - sort: 140 - - name: delete - id: 57 - kind: instance - action: delete - params: [] - - name: modify - id: 197 - kind: instance - action: modify - params: - - id: 627 - code: serviceUid - data_type: string - required: true - sort: 20 - - id: 628 - code: ipSpaceName - data_type: string - required: true - sort: 30 - - id: 629 - code: dnatCreate - data_type: boolean - required: true - sort: 40 - - id: 630 - code: snatCreate - data_type: boolean - required: true - sort: 50 - - id: 631 - code: internalPortAccess - data_type: string - required: true - sort: 60 - - id: 632 - code: internalAddrAccess - data_type: string - required: true - sort: 70 diff --git a/generated/test/resources_yaml/26_vapp.yaml b/generated/test/resources_yaml/26_vapp.yaml deleted file mode 100644 index 9410753..0000000 --- a/generated/test/resources_yaml/26_vapp.yaml +++ /dev/null @@ -1,79 +0,0 @@ -name: vapp -service_id: 26 -service_display_name: Виртуальный каталог ВМ (vApp) -service_short_name: vapp -service_man: '# Инструкция по развертыванию Виртуальный каталог ВМ (vApp) через платформу

## 1. Общая информация

**Виртуальный каталог ВМ (vApp)** — это логическая каталог, объединённая в единый управляемый объект. Сервис предназначен для совместного развертывания, управления жизненным циклом и сетевого взаимодействия набора связанных ВМ. Основные функции включают:
- централизованное управление включением, выключением и приостановкой группы ВМ;
- унифицированный сетевой доступ через привязанный Edge-шлюз;

### Доступные операции

**create** — Создание нового vApp с привязкой к заданным услугам VDC и Edge
**delete** — Удаление существующего vApp. Требуется предварительный перевод vApp в состояние *suspend*. Полное удаление возможно через 14 дней.
**suspend** — Остановка vApp (выключение всех ВМ внутри каталога)
**resume** — Запуск vApp (включение всех ВМ внутри каталога)

---

## 2. Параметры развертывания

Параметры берутся из операции **create**.

### UUID Услуги VDC
Уникальный идентификатор услуги «Виртуальный датацентр (vDC)».
Используется для определения вычислительной инфраструктуры, в которой будет развернут vApp.

### UUID Услуги Edge
Уникальный идентификатор услуги «Сетевой шлюз периметра (Edge)».
Обеспечивает маршрутизацию и сетевое подключение. Может быть привязан к группе VDC, поэтому выбирается отдельно.

### Имя vApp
Строгое имя, соответствующее маске формирования DNS-имени.
Используется как часть в DNS-имени при создании виртуальных машин.

---

## 3. Рекомендованные характеристики
Так как является промежуточным этапом для создания ВМ, все характеристики описываются в услуге `Виртуальная машина`

---

## 4. Выходные параметры

### Параметры vApp
Заданные при создании vApp

---

## 5. Дополнительная информация

- Привязка к VDC и Edge осуществляется раздельно, поскольку один Edge может обслуживать несколько VDC (через услугу `Группа виртуальных датацентров (groupvDC)`). Это гарантирует возможность развернуть ВМ в единой сети, но в разных виртуальных датацентрах.
- Перед удалением vApp необходимо вручную перевести его в состояние **suspend**. Удаление возможно только спустя 14 дней после этого шага.
- Операция delete удаляет весь vApp полностью, включая все виртуальные машины, связанные с ним.
- Рекомендуется заранее планировать сетевые связи и назначения DNS-имён для обеспечения корректной работы сервисов, развернутых внутри vApp.
- При проектировании инфраструктуры следует учитывать зависимости между Edge-шлюзом, VDC и размещаемыми ВМ, чтобы избежать ограничений при масштабировании и интеграции.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 69 - kind: instance - action: create - params: - - id: 190 - code: nsxtUid - data_type: uuid - required: true - ref_svc_id: 22 - descr: UUID Услуги `Сетевой шлюз периметра (Edge)` - man: К какой сети может быть подключен виртуальный каталог - sort: 20 - - id: 191 - code: vappName - data_type: string - required: true - default: lvifmmxj - regex: ^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$ - minlength: 3 - descr: Уникальное в рамках организации - man: Имеет строгую маску `^[a-z0-9][a-z0-9.-]{3,61}[a-z0-9]$`

Используется в формировании DNS-Имени (для виртуальных машин) - sort: 30 - - id: 623 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - descr: UUID Услуги `Виртуальный датацентр (vDC)` - man: В каких ресурсах будет создан виртуальный каталог - sort: 10 - - name: delete - id: 70 - kind: instance - action: delete - params: [] - - name: reconcile - id: 285 - kind: action - action: reconcile - params: [] - - name: resume - id: 209 - kind: instance - action: resume - params: [] - - name: suspend - id: 208 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/27_vc_vm_v2.yaml b/generated/test/resources_yaml/27_vc_vm_v2.yaml deleted file mode 100644 index 2b1d235..0000000 --- a/generated/test/resources_yaml/27_vc_vm_v2.yaml +++ /dev/null @@ -1,71 +0,0 @@ -name: vc_vm_v2 -service_id: 27 -service_display_name: VM в Cloud Director (vc_vmV2) -service_short_name: vc_vm_v2 -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 100 - kind: instance - action: create - params: - - id: 301 - code: serverListJson - data_type: json - required: true - default: '[{}]' - maxlength: 65536 - minlength: 10 - descr: Необходимо изменить vappUid и ipSpaceName
Пример конфигурации взять по ссылке ниже

Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) - sort: 20 - is_modifiable: true - - name: delete - id: 101 - kind: instance - action: delete - params: [] - - name: modify - id: 104 - kind: instance - action: modify - params: - - id: 302 - code: serverListJson - data_type: json - required: true - default: '[{}]' - maxlength: 65536 - minlength: 10 - descr: Объект серверов вида [{}] (Map(Object))
Более подробнее смотреть в README.md (https://gitea.mgmt.nubes.ru/cloudServices/vcVmV2) - sort: 10 - - name: resume - id: 102 - kind: instance - action: resume - params: [] - - name: suspend - id: 103 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/28_vc_vm_v3.yaml b/generated/test/resources_yaml/28_vc_vm_v3.yaml deleted file mode 100644 index f05d808..0000000 --- a/generated/test/resources_yaml/28_vc_vm_v3.yaml +++ /dev/null @@ -1,278 +0,0 @@ -name: vc_vm_v3 -service_id: 28 -service_display_name: Виртуальная машина -service_short_name: vc_vm_v3 -service_man: '# Документация по разворачиванию ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\ложь, определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed" (В нем не будет работать сбор метрик zabbix).

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 132 - kind: instance - action: create - man: '# Документация по использованию услуги развёртывания ВМ в Личном кабинете

Данный сервис позволяет разворачивать виртуальные машины (ВМ) непосредственно из Личного кабинета (ЛК).

## Доступные операции
- **create** — Создание виртуальной машины
- **delete** — Удаление виртуальной машины
- **resume** — Запуск виртуальной машины
- **suspend** — Остановка виртуальной машины

---

## Параметры для развёртывания ВМ

### Обязательные параметры:

**vappUid**
UUID vApp, в которой будет создаваться виртуальная машина.
*Пример: WZ00400*

**vmName**
Уникальное имя виртуальной машины.
*Пример: test-vm*

**vmCpu**
Количество ядер процессора. Должно быть больше 0.
*Пример: 1*

**vmRam**
Объем оперативной памяти в гигабайтах. Должно быть больше 0.
*Пример: 1*


**accessPortList**
Белый список портов для доступа к виртуальной машине извне в формате JSON.
*Пример: ["22","10050]*

**imageVm**
Образ операционной системы для развёртывания.
*Доступные значения: RockyLinux_9-16G-cloudinit, Ubuntu_22-20G, Debian_13-20G*

**userLogin**
Логин пользователя для SSH-доступа.
*По умолчанию: myuser*

**userPublicKey**
Публичный SSH-ключ пользователя в формате OpenSSH.
*Пример: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...*

**needAddZabbixTemplate**
Значение истина\лож определяющее будет ли добавлен хост в zabbix.
*Пример: true*

### Опциональные параметры:

**accessIpList**
Белый список IP-адресов для доступа к виртуальной машине в формате JSON. Если оставить пустым, вм будет доступена со всех адресов.
*Пример: ["192.168.1.1", "10.0.0.5"]*

**vmDisk**
Размер дополнительного диска в гигабайтах. Должно быть больше 0.
*Пример: 50*

**ipSpaceName**
Внешний IP-адрес. Если не требуется — укажите значение "no-needed".

**cloudInit**
YAML-скрипт для кастомизации системы через cloud-init.

---

## Доступные образы операционных систем

### RockyLinux_9-16G-cloudinit
Базовый образ на основе Rocky Linux 9 (Rocky-9.6-x86_64-minimal) для серверных задач.
- Размер системного диска: 16 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Ubuntu_22-20G
Образ на основе Ubuntu Server 22.04 LTS для облачного окружения.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

### Debian_13-20G
Образ на основе Debian 13 (Trixie) для стабильной серверной работы.
- Размер системного диска: 20 ГБ
- Поддержка cloud-init
- Минимальные требования: 1 vCPU, 1 ГБ RAM

---

## Пример использования Cloud-init

```yaml
users:
- name: myuser
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh-authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...

packages:
- nginx

runcmd:
- systemctl enable --now nginx
- systemctl restart nginx
```

---

## Результат выполнения

После успешного развёртывания в Личном кабинете появится новая виртуальная машина с заданными характеристиками. SSH-доступ осуществляется с использованием указанных логина и публичного ключа.

---

## Важные примечания

### Особенности работы:
- Для отключения внешнего IP укажите значение "no-needed" в параметре ipSpaceName
- Параметр cloudInit является опциональным

### Ограничения:
- Не рекомендуется использовать продолжительные инструкции в cloud-init (таймаут 5 минут)
- Не поддерживается запись файлов через cloud-init
- Не реализовано создание резервных копий ВМ

---

## Диагностика проблем с cloud-init

### Основной лог-файл:
```bash
/var/log/cloud-init.log
```

### Пакет не устанавливается:
- Проверьте доступность пакета в репозитории
- Убедитесь в правильности написания имени пакета
- Попробуйте установить вручную: `apt install <имя_пакета>` (Debian/Ubuntu) или `dnf install <имя_пакета>` (Rocky Linux)

### Команда не выполняется:
- Проверьте наличие требуемой утилиты в системе
- Убедитесь в правильности синтаксиса команды
- Попробуйте выполнить команду вручную для диагностики' - params: - - id: 407 - code: vappUid - data_type: uuid - required: true - ref_svc_id: 26 - sort: 10 - - id: 408 - code: vmName - data_type: string - required: true - default: web01 - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - sort: 20 - - id: 409 - code: vmCpu - data_type: integer > 0 - required: true - default: "1" - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 30 - is_modifiable: true - - id: 410 - code: vmRam - data_type: integer > 0 - required: true - default: "1" - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 40 - is_modifiable: true - - id: 411 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 50 - is_modifiable: true - - id: 412 - code: ipSpaceName - data_type: string - required: true - sort: 60 - depends_on: vappUid - is_modifiable: true - - id: 413 - code: accessIpList - data_type: json - required: false - regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 70 - is_modifiable: true - - id: 414 - code: imageVm - data_type: string - required: true - sort: 90 - depends_on: vappUid - - id: 415 - code: cloudInit - data_type: yaml - required: false - descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. - sort: 200 - - id: 416 - code: userLogin - required: true - default: myuser - descr: Учётка SSH - sort: 180 - - id: 417 - code: userPublicKey - data_type: string - required: true - descr: Публичная часть SSH-Ключа для пользователя - sort: 190 - - id: 448 - code: accessPortList - data_type: string - required: true - default: '["22"]' - descr: 'По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX' - sort: 80 - is_modifiable: true - - name: delete - id: 133 - kind: instance - action: delete - params: [] - - name: modify - id: 159 - kind: instance - action: modify - params: - - id: 493 - code: vmCpu - data_type: integer > 0 - required: false - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 20 - - id: 494 - code: vmRam - data_type: integer > 0 - required: false - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 30 - - id: 495 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 40 - - id: 496 - code: ipSpaceName - data_type: string - required: false - value_list: - - no-needed - - internet-antiddos-v1 - - internet-no-antiddos-v1 - - internet-ipv4-v1 - descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev - sort: 50 - - id: 497 - code: accessIpList - data_type: json - required: false - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 60 - - id: 498 - code: accessPortList - data_type: json - required: false - descr: Формат массива
По каким портам открыть доступ к ВМ.
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX - sort: 70 - - name: reconcile - id: 286 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 164 - kind: action - action: redeploy - params: - - id: 509 - code: vmName - data_type: string - required: true - regex: ^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$ - sort: 10 - - id: 510 - code: vmCpu - data_type: integer > 0 - required: true - maxvalue: 64 - minvalue: 1 - descr: шт - sort: 20 - - id: 511 - code: vmRam - data_type: integer > 0 - required: true - maxvalue: 256 - minvalue: 1 - descr: Gb - sort: 30 - - id: 512 - code: vmDisk - data_type: integer > 0 - required: false - regex: ^(?:$|(?:0|[1-9]\d{0,2}|1\d{3}|20(?:[0-3]\d|4[0-8])))$ - descr: Основной диск у vm зависит от образа (см. документацию)
Если необходимо дополнительное место для vm, введите колличество Gb в эту форму - sort: 40 - - id: 513 - code: ipSpaceName - data_type: string - required: false - value_list: - - no-needed - - internet-antiddos-v1 - - internet-no-antiddos-v1 - - internet-ipv4-v1 - descr: no-needed, без внешнего ip
internet-antiddos-v1, с функцией защиты от ddos атак
internet-no-antiddos-v1, с функцией защиты от ddos атак
internet-ipv4-v1 тестовое простанство dev - sort: 50 - - id: 514 - code: accessIpList - data_type: json - required: false - regex: ^(?:$|\[\s*(?:"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?"(?:\s*,\s*"(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?")*)\s*\])$ - descr: Формат массива
Каким адресам будет предоставлен доступ до вм

Требует выделенного внешнего IP
Если ничего не передано, то выставлено ["0.0.0.0/0"] - sort: 60 - - id: 515 - code: imageVm - data_type: string - required: true - value_list: - - RockyLinux_9-16G-cloudinit - - Ubuntu_22-20G - - Debian_13-20G - sort: 80 - - id: 516 - code: userLogin - data_type: string - required: true - descr: Учётка SSH - sort: 180 - - id: 517 - code: userPublicKey - data_type: string - required: true - descr: Публичная часть SSH-Ключа для пользователя - sort: 190 - - id: 518 - code: cloudInit - data_type: yaml - required: false - descr: Можно передать создание дополнительных пользователей.
На текущий момент не реализовано выполнения в cloud-init, установки пакетов, выполнения команд и записи файлов. - sort: 200 - - id: 520 - code: accessPortList - data_type: json - required: false - descr: По каким портам открыть доступ к ВМ.
Порты 22, 10050 проставляются автоматически
Пока доступно:
22 - SSH,
80 - HTTP,
443 - HTTPS,
10050 - ZABBIX - sort: 70 - - name: resume - id: 135 - kind: instance - action: resume - params: [] - - name: suspend - id: 134 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/29_vc_vdc_group.yaml b/generated/test/resources_yaml/29_vc_vdc_group.yaml deleted file mode 100644 index 21afd35..0000000 --- a/generated/test/resources_yaml/29_vc_vdc_group.yaml +++ /dev/null @@ -1,74 +0,0 @@ -name: vc_vdc_group -service_id: 29 -service_display_name: Группа датацентров -service_short_name: vcVdcGroup -service_man: '# Инструкция по развертыванию Группа виртуальных датацентров (groupvDC) через платформу

## 1. Общая информация

Сервис **Группа виртуальных датацентров (groupvDC)** предназначен для логического объединения нескольких виртуальных датацентров (vDC) в единую группу. Это позволяет растягивать SDN между разными vDC и централизованно управлять сетевой связностью. Управление инфраструктурой осуществляется через Cloud Director.
Сервис применяется в случаях, когда требуется обеспечить единое сетевое пространство для распределенных виртуальных датацентров.

### Доступные операции

**create** — Создание новой группы виртуальных датацентров.
**delete** — Удаление существующей группы vDC.
**add_vdc** — Добавление дополнительного виртуального датацентра в группу.
**remove_vdc** — Удаление ранее добавленного виртуального датацентра из группы.

---

## 2. Параметры развертывания

Параметры операции **create**:

### UUID VDC
Идентификатор виртуального датацентра, который будет являться первичным в группе.
Описание: требуется предварительно создать виртуальный датацентр с помощью услуги `Виртуальный датацентр (vDC)`.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Для тестового окружения создавать группу VDC нет необходимости, так как это создаёт дополнительный слой абстракции в Cloud Director

### Промышленное окружение
- Для промышленного окружения, если в тенанте существуют разные VDC, создание groupvdc позволит снизить сетевые издержки (за счёт общей сети)

---

## 4. Выходные параметры

### Параметры groupvdc
Набор параметров, указанных пользователем при создании

---

## 5. Дополнительная информация
- Группа vDC используется для растягивания SDN между несколькими виртуальными датацентрами. Управление всеми сетевыми элементами внутри группы выполняется Cloud Director.
- Удалить первичный vDC (тот, что был указан при операции **create**) невозможно. Для его удаления необходимо удалить всю группу и создать новую.
- Перед добавлением любого vDC в группу он должен быть предварительно развернут через сервис `Виртуальный датацентр (vDC)`.
- Сети создаются не в этой услуге, а в услуге `Сетевой шлюз периметра (Edge)`. В ней необходимо будет выбрать тип `groupvdc`' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: add_vdc - id: 192 - kind: subresource - action: add - subresource: vdc - man: '**Не нужно** выбирать UUID VDC, который уже был добавлен в группу' - params: - - id: 619 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - sort: 10 - - name: create - id: 191 - kind: instance - action: create - man: '**VDC Группа** нужна для объединения всех VDC в организации в одну логическую группу.
Это позволит, к примеру, создать **общую сеть** для всех VDC в организации

- Для создания группы VDC необходимо указать **инстанс** услуги VDC (`Виртуальный датацентр`)
- **Важный момент** - удалить эту VDC из группы нельзя. Если такая потребность появится, необходимо будет **ПОЛНОСТЬЮ** удалить группу VDC и уже потом создать новую' - params: - - id: 618 - code: vdcUid - data_type: uuid - required: true - ref_svc_id: 21 - sort: 10 - - name: delete - id: 195 - kind: instance - action: delete - params: [] - - name: reconcile - id: 251 - kind: action - action: reconcile - params: [] - - name: remove_vdc - id: 193 - kind: subresource - action: remove - subresource: vdc - params: - - id: 620 - code: vdcName - data_type: string - required: true - sort: 10 diff --git a/generated/test/resources_yaml/2_template.yaml b/generated/test/resources_yaml/2_template.yaml deleted file mode 100644 index c05bb71..0000000 --- a/generated/test/resources_yaml/2_template.yaml +++ /dev/null @@ -1,224 +0,0 @@ -name: template -service_id: 2 -service_display_name: Темплейт k8s -service_short_name: template -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 74 - kind: instance - action: create - params: - - id: 813 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 174 - code: replicas - data_type: integer > 0 - required: true - default: "1" - is_modifiable: false - - id: 172 - code: cpu - data_type: integer > 0 - required: true - default: "100" - is_modifiable: false - - id: 175 - code: disk - data_type: integer > 0 - required: true - default: "5" - is_modifiable: false - - id: 173 - code: memory - data_type: integer > 0 - required: true - default: "128" - is_modifiable: false - - id: 814 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 176 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - is_modifiable: false - - id: 815 - code: accessConfiguration - data_type: map-fixed - required: true - is_modifiable: true - sub_params: - - id: 177 - code: type - data_type: string - required: true - default: ingress - value_list: - - ingress - - service - is_modifiable: false - - id: 178 - code: domain - data_type: string - required: true - default: abracadabra-pin1v6zw - regex: (?=^.{1,63}$)^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - is_modifiable: false - - id: 179 - code: masterIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 180 - code: masterAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - id: 181 - code: slaveIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 182 - code: slaveAccessList - data_type: json - required: true - default: '[]' - is_modifiable: false - - name: delete - id: 75 - kind: instance - action: delete - params: [] - - name: modify - id: 76 - kind: instance - action: modify - params: - - id: 816 - code: clusterConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 183 - code: cpu - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 185 - code: disk - data_type: integer >= 0 - required: true - default: "" - is_modifiable: false - - id: 184 - code: memory - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 186 - code: replicas - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 817 - code: accessConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 187 - code: type - data_type: string - required: true - default: "" - value_list: - - ingress - - service - is_modifiable: false - - id: 188 - code: domain - data_type: string - required: true - default: "" - is_modifiable: false - - id: 189 - code: masterIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 191 - code: masterAccessList - data_type: json - required: true - default: "" - is_modifiable: false - - id: 190 - code: slaveIpSpace - data_type: string - required: true - default: "" - is_modifiable: false - - id: 192 - code: slaveAccessList - data_type: json - required: true - default: "" - is_modifiable: false - - name: reconcile - id: 298 - kind: action - action: reconcile - params: [] - - name: resume - id: 98 - kind: instance - action: resume - params: [] - - name: suspend - id: 97 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/32_vmpostgre.yaml b/generated/test/resources_yaml/32_vmpostgre.yaml deleted file mode 100644 index f877745..0000000 --- a/generated/test/resources_yaml/32_vmpostgre.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: vmpostgre -service_id: 32 -service_display_name: vc_vm_postgresql_std -service_short_name: vmpostgre -service_man: Сервис настройки PostgreSQL для виртуальной машины.
Требует наличия ВМ -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: [] diff --git a/generated/test/resources_yaml/50_nextcloud.yaml b/generated/test/resources_yaml/50_nextcloud.yaml deleted file mode 100644 index 9414cb3..0000000 --- a/generated/test/resources_yaml/50_nextcloud.yaml +++ /dev/null @@ -1,489 +0,0 @@ -name: nextcloud -service_id: 50 -service_display_name: Nextcloud -service_short_name: nextcloud -service_man: 'Ресурсная платформа: k8s. Автоматизация: helm chart' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 16 - kind: instance - action: create - man: Для установки необходимо:
- ресурсная платформа, где разворачивается Nextcloud, обязана совпадать с ресурсными платформами, где развёрнут Redis и PostgreSQL - params: - - id: 1055 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 493 - code: psqlEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 494 - code: redisEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 495 - code: psqlUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета` - is_modifiable: false - - id: 496 - code: redisUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета` - is_modifiable: false - - id: 492 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1056 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 500 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 497 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 498 - code: memory - data_type: integer > 0 - required: true - default: "1024" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 499 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - is_modifiable: false - - id: 1057 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 501 - code: domain - data_type: string - required: true - default: nextcloud-c2xizgvp - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1058 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 502 - code: version - data_type: string - required: true - default: 33.0.4 - value_list: - - 33.0.4 - is_modifiable: false - - id: 503 - code: documentServer - data_type: string - required: true - default: collabora - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - id: 549 - code: logLevel - data_type: string - required: true - default: INFO - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - name: create_backup - id: 404 - kind: subresource - action: create - subresource: backup - man: Перед запуском обязательно надо перевести экземпляр Nextcloud в статус 'suspended' - params: - - id: 1077 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 578 - code: veleroUid - data_type: string - required: true - default: "" - man: Предварительно необходимо создать инстанс услуги `Velero`
Из этого инстанса будут взяты точки восстановления
Если восстановление производится на другую ресурсную платформу, важно чтобы предварительно все компоненты связующие были либо перенесены, либо были доступны снаружи - is_modifiable: false - - name: delete - id: 17 - kind: instance - action: delete - man: DEV-Ветка удаления Nextcloud - params: [] - - name: modify - id: 67 - kind: instance - action: modify - params: - - id: 1059 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 505 - code: memory - data_type: integer > 0 - required: true - default: "" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 506 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - is_modifiable: false - - id: 507 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 504 - code: cpu - data_type: integer > 0 - required: true - default: "" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 1060 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 508 - code: version - data_type: string - required: true - default: "" - value_list: - - 33.0.4 - is_modifiable: false - - id: 509 - code: documentServer - data_type: string - required: true - default: "" - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - id: 550 - code: logLevel - data_type: string - required: true - default: "" - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - name: reconcile - id: 390 - kind: action - action: reconcile - params: [] - - name: recovery - id: 406 - kind: action - action: recovery - params: - - id: 1078 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 558 - code: redisEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 560 - code: psqlUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить PostgreSQL из Личного кабинета`
Если откат производится на ту же версию Nextcloud, в теории возможно развернуться в существующую базу

Предпочтительно создать отдельный инстанс PostgreSQL, который был восстановлен также через Recovery - is_modifiable: false - - id: 561 - code: redisUid - data_type: uuid - required: true - default: "" - man: Необходимо выбирать, когда выбран параметр `Подключить Redis из Личного кабинета` - is_modifiable: false - - id: 556 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 570 - code: veleroUid - data_type: string - required: true - default: "" - man: Из этого инстанса будут взяты точки восстановления
Если восстановление производится на другую ресурсную платформу, важно чтобы предварительно все компоненты связующие были либо перенесены, либо были доступны снаружи - is_modifiable: false - - id: 559 - code: recoveryPoint - data_type: string - required: true - default: "" - is_modifiable: false - - id: 557 - code: psqlEnabled - data_type: boolean - required: true - default: "true" - value_list: - - "true" - man: Инстанс обязан быть развёрнут на той же ресурсной платформе, где и будет располагаться Nextcloud - is_modifiable: false - - id: 1079 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 562 - code: cpu - data_type: integer > 0 - required: true - default: "" - value_list: - - "1000" - - "2000" - - "3000" - - "4000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 563 - code: memory - data_type: integer > 0 - required: true - default: "" - value_list: - - "1024" - - "2048" - - "3072" - - "4096" - - "5120" - - "6144" - - "7168" - - "8192" - man: Указывается в Megabytes - is_modifiable: false - - id: 564 - code: replicas - data_type: integer > 0 - required: true - default: 1,2 - is_modifiable: false - - id: 565 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes
На данном диске хранятся данные Nextcloud - is_modifiable: false - - id: 1080 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 566 - code: domain - data_type: string - required: true - default: nextcloud-gx145olx - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1081 - code: appConfiguration - data_type: map-fixed - required: true - man: Версия Nextcloud будет взята из бэкапа - sort: 40 - sub_params: - - id: 567 - code: documentServer - data_type: string - required: true - default: collabora - value_list: - - collabora - man: Выбор сервера документов (onlyoffice)/(collabora). onlyoffice пока не доступен
Размер пода collabora:
1.8 ядра. 2Gi ОЗУ - is_modifiable: false - - id: 568 - code: logLevel - data_type: string - required: true - default: NONE - value_list: - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - man: Включает отображение логов самого nextcloud
Логи уходят в /dev/stdout контейнера
`NONE` отключает логирование - is_modifiable: false - - id: 569 - code: version - data_type: string - required: false - default: "" - man: Версия Nextcloud будет исправлена на ту версию, которая указана в бэкапе - is_modifiable: false - - name: restart - id: 66 - kind: action - action: restart - params: [] - - name: resume - id: 129 - kind: instance - action: resume - params: [] - - name: suspend - id: 128 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/81_superset.yaml b/generated/test/resources_yaml/81_superset.yaml deleted file mode 100644 index 75feff2..0000000 --- a/generated/test/resources_yaml/81_superset.yaml +++ /dev/null @@ -1,109 +0,0 @@ -name: superset -service_id: 81 -service_display_name: Apache Superset -service_short_name: superset -service_man: 'Apache Superset как сервис.
Ресурсная платформа: k8s.
Автоматизация: k8s operator.
Авторизация: 2FA keycloak (для доменного логина @mgmt.nubes.ru)' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 12 - kind: instance - action: create - man: DEV-Ветка сборки Superset - params: - - id: 14 - code: domain - data_type: string - required: true - default: superset-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 20 - code: emails - data_type: string - required: true - default: testuser1 - descr: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел - man: Учётная запись, которой будут выданы админские права. В формате email [ну не совсем. Для доменных учетных записей - без доменного суффикса]. Обязана находиться в Keycloak. Можно передавать несколько через пробел - sort: 70 - - id: 96 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро - man: Квота ядра пода (Указывается в mili). 1000 Mili = 1 ядро - sort: 30 - - id: 97 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 40 - - id: 98 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - - id: 109 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 227 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 60 - - name: delete - id: 13 - kind: instance - action: delete - man: DEV-Ветка удаления Superset - params: [] diff --git a/generated/test/resources_yaml/82_harbor.yaml b/generated/test/resources_yaml/82_harbor.yaml deleted file mode 100644 index 624843a..0000000 --- a/generated/test/resources_yaml/82_harbor.yaml +++ /dev/null @@ -1,126 +0,0 @@ -name: harbor -service_id: 82 -service_display_name: Container Registry -service_short_name: harbor -service_man: 'Расширенный реестр для управления Docker-образами с enterprise-функциями безопасности, репликации и контроля доступа.
Container Registry позволяет хранить, развертывать и управлять Docker-образами контейнеров в инфраструктуре NGcloud.
Сервис можно использовать в процессе разработки контейнеров. Например, вы можете настроить систему автоматизации на работу с реестром в Container Registry в рамках процесса непрерывной интеграции. Контейнеры идеально подходят для запуска небольших, независимых и легко изменяемых сервисов.
Чтобы начать работу, создайте реестр и загрузите в него Docker-образ
Авторизация: keycloak' -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 14 - kind: instance - action: create - man: DEV-Ветка сборки Harbor - params: - - id: 16 - code: domain - data_type: string - required: true - default: harbor-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 20 - - id: 110 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 30 - - id: 228 - code: resourceCPU - data_type: integer > 0 - required: true - default: "100" - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 40 - is_modifiable: true - - id: 229 - code: resourceMemory - data_type: integer > 0 - required: true - default: "200" - maxvalue: 8192 - minvalue: 512 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 50 - is_modifiable: true - - id: 230 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 60 - is_modifiable: true - - id: 231 - code: s3Uid - data_type: uuid - required: true - ref_svc_id: 12 - descr: UUID экземпляра услуги S3 - man: UUID экземпляра услуги S3 - sort: 10 - - name: delete - id: 15 - kind: instance - action: delete - man: DEV-Ветка удаления Harbor - params: [] - - name: modify - id: 270 - kind: instance - action: modify - params: - - id: 822 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - - id: 823 - code: resourceCPU - data_type: integer > 0 - required: false - maxvalue: 4000 - minvalue: 500 - - id: 824 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 512 diff --git a/generated/test/resources_yaml/86_k8s_velero.yaml b/generated/test/resources_yaml/86_k8s_velero.yaml deleted file mode 100644 index 2d8689b..0000000 --- a/generated/test/resources_yaml/86_k8s_velero.yaml +++ /dev/null @@ -1,199 +0,0 @@ -name: k8s_velero -service_id: 86 -service_display_name: Velero -service_short_name: k8sVelero -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 332 - kind: instance - action: create - params: - - id: 962 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 334 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 336 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 337 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "2" - - "3" - is_modifiable: false - - id: 963 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 338 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - is_modifiable: false - - id: 964 - code: nodeAgentConfiguration - data_type: map-fixed - required: true - man: Реплик выставляется автоматически столько, сколько Worker-узлов в кластере - sort: 30 - is_modifiable: true - sub_params: - - id: 551 - code: cpu - data_type: integer > 0 - required: true - default: "2000" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 552 - code: memory - data_type: integer > 0 - required: true - default: "2048" - man: Указывается в Megabytes - is_modifiable: false - - id: 1076 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 555 - code: s3BucketUid - data_type: uuid - required: true - default: "" - man: Экземпляр S3-Бакета обязателен
Учётная запись будет создана автоматически, если её нету - is_modifiable: false - - name: delete - id: 331 - kind: instance - action: delete - params: - - id: 1082 - code: deleteS3Bucket - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - sort: 10 - - name: modify - id: 334 - kind: instance - action: modify - params: - - id: 965 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 348 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "2" - - "3" - is_modifiable: false - - id: 345 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 347 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 966 - code: nodeAgentConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 553 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 554 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - name: reconcile - id: 336 - kind: action - action: reconcile - params: [] - - name: resume - id: 335 - kind: instance - action: resume - params: [] - - name: suspend - id: 333 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/88_k8s_ziti_controller.yaml b/generated/test/resources_yaml/88_k8s_ziti_controller.yaml deleted file mode 100644 index 7c099cb..0000000 --- a/generated/test/resources_yaml/88_k8s_ziti_controller.yaml +++ /dev/null @@ -1,95 +0,0 @@ -name: k8s_ziti_controller -service_id: 88 -service_display_name: k8sZitiController -service_short_name: k8sZitiController -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 99 - kind: instance - action: create - params: - - id: 294 - code: resourceCPU - data_type: integer > 0 - required: true - default: "300" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (milicores) - sort: 30 - - id: 295 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (MBytes) - sort: 40 - - id: 296 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - - id: 297 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 60 - - id: 298 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 20 - - id: 299 - code: domain - data_type: string - required: true - default: k8sZitiController-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - name: delete - id: 105 - kind: instance - action: delete - params: [] diff --git a/generated/test/resources_yaml/89_flask.yaml b/generated/test/resources_yaml/89_flask.yaml deleted file mode 100644 index 4ddcc48..0000000 --- a/generated/test/resources_yaml/89_flask.yaml +++ /dev/null @@ -1,244 +0,0 @@ -name: flask -service_id: 89 -service_display_name: Web-сервер с фреймворком Flask -service_short_name: flask -service_man: '# Инструкция по развертыванию Web-сервер с фреймворком Flask через платформу

## 1. Общая информация

Сервис **Web-сервер с фреймворком Flask** предназначен для развертывания веб-приложений на базе Flask.

### Доступные операции

create — включить сервис, развернуть веб-приложение с указанными параметрами.
delete — удалить сервис, только после suspend.
modify — изменить настройки сервиса и переразвернуть.
suspend — выключить веб-приложение, остановить обслуживание.
resume — включить веб-приложение после suspend.

---

## 2. Параметры развертывания

Параметры входных данных операции **create**:

### Имя домена
Описание: доменное имя для доступа к веб-приложению. Если указан FQDN, происходит проверка DNS на валидность А-записи. А-запись должна указывать на балансировщик ресурсной платформы. Если указано имя в рамках одного октета, оно будет добавлено в публичную DNS-зону.
Пример значения: строка, допустимые FQDN или имя в одном октете. Пример: example.com или myapp.

### Платформа для развертывания
Описание: кластер Kubernetes, в котором будет развернуто приложение.
Пример значения: идентификатор кластера Kubernetes.

### Квота ядра пода
Описание: количество CPU, выделяемое на под, в Milicores.
Пример значения: целое число, например 500 для 0.5 ядра.

### Квота памяти пода
Описание: количество памяти, выделяемое на под, в Megabytes.
Пример значения: целое число, например 512 для 512 MB.

### Кол-во реплик пода
Описание: количество инстансов пода для масштабирования.
Пример значения: целое число, шт, например 2.

### URL Проекта (gitpath)
Описание: полный HTTPS путь до проекта в Git-репозитории.
Пример значения: URL в формате https://github.com/myuser/myproject.git. Доступ к репозиторию должен быть публичным.

### URL HealthCheck
Описание: путь для проверки здоровья приложения внутри пода.
Пример значения: строка, путь, например /api/v1/health.

### Переменные окружения пода
Описание: структура данных, которая будет передана как переменные окружения в под.
Пример значения: ключ-значение, например {"ENV": "production"}.

---

## 3. Рекомендованные характеристики

### Тестовое окружение
- Квота ядра пода: 300m (0.3 ядра)
- Квота памяти пода: 256 MB
- Кол-во реплик пода: 1

### Промышленное окружение
- Квота ядра пода: от 1000m (1 ядро) в зависимости от нагрузки
- Квота памяти пода: от 1024 MB (1 GB) с учетом потребностей приложения
- Кол-во реплик пода: от 2 для обеспечения отказоустойчивости

---

## 4. Выходные параметры
Операция create не возвращает явных выходных параметров. Успешное выполнение операции приводит к развертыванию сервиса, доступного по указанному домену.

---

## 5. Дополнительная информация
- В корне проекта должен находиться файл requirements.txt со списком зависимостей Python.
- Исходный код веб-приложения должен располагаться в директории site.
- Запуск веб-сервера осуществляется командой python app.py.
- Используется версия Python: python:3.12-slim.
- Git-репозиторий должен быть публично доступен для платформы.
- Операция delete возможна только после suspend.
- Операция modify приводит к переразвертыванию с новыми параметрами.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 376 - kind: instance - action: create - params: - - id: 1037 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 460 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1038 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 461 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 462 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 463 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1039 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 464 - code: domain - data_type: string - required: true - default: flask-mdpw2bcy - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1040 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 467 - code: version - data_type: string - required: true - default: "3.12" - value_list: - - "3.12" - is_modifiable: false - - id: 465 - code: gitPath - data_type: string - required: true - default: https://github.com/Foxyhhd/Baldurs-Gate-test.git - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 466 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1041 - code: jsonEnv - data_type: map - required: false - default: '{}' - sort: 50 - is_modifiable: true - - name: delete - id: 379 - kind: instance - action: delete - params: [] - - name: modify - id: 375 - kind: instance - action: modify - params: - - id: 1042 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 468 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 469 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 470 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1043 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 473 - code: version - data_type: string - required: true - default: "" - value_list: - - "3.12" - is_modifiable: false - - id: 471 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 472 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1044 - code: jsonEnv - data_type: map - required: false - sort: 30 - - name: reconcile - id: 381 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 380 - kind: action - action: redeploy - params: - - id: 1045 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 474 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: resume - id: 378 - kind: instance - action: resume - params: [] - - name: suspend - id: 377 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/90_postgres.yaml b/generated/test/resources_yaml/90_postgres.yaml deleted file mode 100644 index ab3399c..0000000 --- a/generated/test/resources_yaml/90_postgres.yaml +++ /dev/null @@ -1,822 +0,0 @@ -name: postgres -service_id: 90 -service_display_name: PostgreSQL -service_short_name: postgres -service_man: '# Инструкция по развертыванию PostgreSQL через платформу

---
## 1. Общая информация

PostgreSQL — это надёжная объектно-реляционная СУБД с открытым исходным кодом, поддерживающая расширенный набор функций, транзакции и хранение сложных типов данных. Развёртывание полностью автоматизировано платформой на базе Zalando Postgres Operator с использованием Kubernetes.

## Доступные операции

- **create** — Создание кластера
- **delete** — Удаление кластера
- **modify** — Модификация кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера
- **restart** — Перезапуск кластера
- **recovery** — Восстановление из резервной копии

- **create_user** — Создание пользователя
- **delete_user** — Удаление пользователя

- **create_database** — Создание базы данных
- **delete_database** — Удаление базы данных


---
## 2. Параметры развертывания кластера

Ниже перечислены параметры, которые необходимо указать при создании ресурса PostgreSQL:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество инстансов (реплик) в кластере PostgreSQL. Пример: `2` (master + slave).

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных в гигабайтах. Пример: `20`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`

* **Выделяет внешний IP для доступа к slave:**
Флаг для выделения внешнего IP для slave (реплики чтения). Работает, если slave присутствует в кластере.

* **Имя ipSpace для публикации внешнего IP slave:**
Если включен параметр для slave, то поле необходимо заполнить.

* **UUID экземпляра услуги S3:**
UUID экземпляра S3 для хранения резервных копий.

* **Версия PostgreSQL:**
Версия PostgreSQL для развертывания. Пример: `16`

* **Расписание бэкапов (ext_BACKUP_SCHEDULE):**
Необязательный. Cron-выражение для времени запуска бэкапов. По умолчанию: `0 0 * * *` (ежедневно в полночь).

* **Количество сохраняемых бэкапов (ext_BACKUP_NUM_TO_RETAIN):**
Необязательный. Количество сохраняемых бэкапов. По умолчанию: `7`

* **PgPooler (master / slave):**
Флаги включения PgBouncer для подключений на запись и чтение. При включении создаётся отдельный под.

* **Разрешить подключение без SSL (allowNoSSL):**
По умолчанию подключение с sslmode=require. При `true` разрешается подключение без SSL.

* **Автоскейлинг PV (autoScale):**
Включает автоскейлинг тома. Требует настройки autoScalePercentage, autoScaleTechWindow, autoScaleQuotaGb.

* **Дополнительные параметры PostgreSQL (jsonParameters):**
JSON с переменными конфигурации PostgreSQL. Документация: https://www.postgresql.org/docs/current/runtime-config.html

---
## 3. Выходные параметры

После успешного развертывания ресурса PostgreSQL платформа возвращает ряд параметров.

#### externalConnect

Внешние эндпоинты для подключения к кластеру:
- **master** — для операций записи (host: `write.${namespace}`, port: 5432)
- **slave** — для операций чтения (host: `read.${namespace}`, port: 5432)

Пример ответа платформы:

```json
{
"slave": {
"ip": "",
"fqdn": "",
"uuid": "",
"isExternal": false
},
"master": {
"ip": "<ip>",
"fqdn": "write.<namespace>.<cluster-domain>",
"uuid": "<uuid>",
"isExternal": false
}
}
```

Поля: `ip` — внешний IP; `fqdn` — полное имя хоста; `uuid` — идентификатор; `isExternal` — признак внешнего доступа.

#### internalConnect

Внутренние эндпоинты (FQDN) для подключения из других сервисов внутри той же сети Kubernetes.

Пример ответа платформы:

```json
{
"slave": "",
"master": {
"slave": "",
"master": {
"slave": "",
"master": "postgresqlk8s-master.<namespace>.svc.cluster.local"
}
}
}
```

Для подключения используйте значение `master.master.master` (host для записи) или соответствующее значение slave (host для чтения).

Для подключения используется порт **5432**.

---
## 4. Управление пользователями

### create_user — Создание пользователя

* **Имя пользователя (username):**
Имя создаваемого пользователя. Используется для подключения к базе и настройки прав.
Пример: `myapp_user`

* **Роль (role):**
Тип роли пользователя. Допустимые значения:
- `app_user` — базовые права для DML-операций (SELECT, INSERT, UPDATE, DELETE)
- `ddl_user` — расширенные права: createdb, createrole (управление базами и ролями)

Защищённые имена, которые нельзя использовать: `admin`, `postgres`, `standby`.

### delete_user — Удаление пользователя

* **Имя пользователя (username):**
Имя удаляемого пользователя. Пример: `myapp_user`

Пользователя нельзя удалить, если он является владельцем базы данных. Сначала удалите или передайте владение базами данных другому пользователю.

---
## 5. Управление базами данных

### create_database — Создание базы данных

* **Имя базы данных (dbName):**
Название создаваемой базы данных. Пример: `myapp_db`

* **Владелец базы данных (dbOwner):**
Имя пользователя-владельца базы. Должен существовать в кластере (создан через create_user). Пример: `myapp_user`

### delete_database — Удаление базы данных

* **Имя базы данных (dbName):**
Название удаляемой базы данных. Пример: `myapp_db`
' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 19 - kind: instance - action: create - man: руководство - params: - - id: 788 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 104 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 105 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 106 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 107 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 789 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 108 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 790 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 110 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 111 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 112 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 109 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 791 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 113 - code: version - data_type: string - required: true - default: "17" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 116 - code: sslRequired - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 114 - code: poolerMaster - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 115 - code: poolerSlave - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 792 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 50 - is_modifiable: true - sub_params: - - id: 117 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 118 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 793 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - is_modifiable: true - sub_params: - - id: 121 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 119 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 120 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 794 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - is_modifiable: true - sub_params: - - id: 122 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 123 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 124 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 125 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - name: create_backup - id: 264 - kind: subresource - action: create - subresource: backup - params: [] - - name: create_database - id: 245 - kind: subresource - action: create - subresource: database - params: - - id: 741 - code: dbName - data_type: string - required: true - regex: ^[A-Za-z0-9]+$ - - id: 742 - code: dbOwner - data_type: string - required: true - - name: create_user - id: 241 - kind: subresource - action: create - subresource: user - params: - - id: 733 - code: username - data_type: string - required: true - regex: ^[A-Za-z0-9]+$ - maxlength: 62 - minlength: 2 - - id: 740 - code: role - data_type: string - required: true - value_list: - - app_user - - ddl_user - descr: Выбрать роль из списка - man: '`app_user` — стандартный пользователь с базовым набором прав
`ddl_user` — пользователь с расширенными правами, включая управление ролями и базами данных' - - name: delete - id: 20 - kind: instance - action: delete - params: - - id: 457 - code: deleteS3Bucket - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - descr: Также удалить технический S3 Bucket с бэкапами? - sort: 10 - - name: delete_database - id: 246 - kind: subresource - action: delete - subresource: database - params: - - id: 743 - code: dbName - data_type: string - required: true - - name: delete_user - id: 244 - kind: subresource - action: delete - subresource: user - params: - - id: 739 - code: username - data_type: string - required: true - descr: 'Пример: `username-admin`' - - name: modify - id: 48 - kind: instance - action: modify - params: - - id: 795 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 126 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 127 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 128 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 129 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 796 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 130 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 131 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 132 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 133 - code: slaveAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 797 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 134 - code: version - data_type: string - required: true - default: "" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 135 - code: sslRequired - data_type: boolean - required: true - default: false,true - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 136 - code: poolerMaster - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 137 - code: poolerSlave - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 798 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 40 - sub_params: - - id: 271 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 272 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 799 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 50 - sub_params: - - id: 193 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 138 - code: retain - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 139 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 800 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 140 - code: enabled - data_type: boolean - required: true - default: "" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 141 - code: schedule - data_type: integer >= 0 - required: true - default: "" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 142 - code: quota - data_type: integer > 0 - required: true - default: "" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - id: 143 - code: percent - data_type: integer > 0 - required: true - default: "" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - name: reconcile - id: 263 - kind: action - action: reconcile - params: [] - - name: recovery - id: 47 - kind: action - action: recovery - params: - - id: 801 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 144 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 166 - code: recoveryPoint - data_type: string - required: true - default: "" - is_modifiable: false - - id: 802 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 146 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 147 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 148 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 145 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 803 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 149 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 150 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 151 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 152 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 804 - code: postgresConfiguration - data_type: map-fixed - required: true - sort: 40 - sub_params: - - id: 155 - code: poolerMaster - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на запись). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 156 - code: poolerSlave - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает pgPooler (на чтение). Будет создан новый под
Размер пода **200m/400Mi** - is_modifiable: false - - id: 153 - code: version - data_type: string - required: true - default: "17" - value_list: - - "17" - - "16" - is_modifiable: false - - id: 154 - code: sslRequired - data_type: boolean - required: true - default: "true" - value_list: - - "false" - - "true" - man: По умолчанию подключение обязательное (sslmode=require) - is_modifiable: false - - id: 805 - code: postgresConf - data_type: array-map-fixed - required: false - sort: 50 - sub_params: - - id: 475 - code: paramName - data_type: string - required: true - default: "" - value_list: - - log_connections - - log_disconnections - is_modifiable: false - - id: 476 - code: paramValue - data_type: string - required: true - default: "" - man: Как правильно вводить значения описано здесь (https://www.postgresql.org/docs/current/runtime-config.html) - is_modifiable: false - - id: 806 - code: backupConfiguration - data_type: map-fixed - required: true - sort: 60 - sub_params: - - id: 159 - code: s3Uid - data_type: uuid - required: true - default: "" - man: Экземпляр S3 обязателен - is_modifiable: false - - id: 160 - code: retain - data_type: integer > 0 - required: true - default: "7" - is_modifiable: false - - id: 161 - code: schedule - data_type: string - required: true - default: 0 0 * * * - regex: ^((((\d+,)+\d+|(\d+(\/|-|#)\d+)|(\d+L?)|(\*(\/\d+)?)|(L(-\d+)?)|(\?)|([A-Z]{3}(-[A-Z]{3})?)) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)$ - is_modifiable: false - - id: 807 - code: autoscaleConfiguration - data_type: map-fixed - required: true - sort: 70 - sub_params: - - id: 165 - code: percent - data_type: integer > 0 - required: true - default: "10" - value_list: - - "10" - - "15" - - "20" - man: Требует включения autoScale. Позволяет указать в процентах расширение от текущего макс объема
Минимальный размер на который расширяется - **1Gb** - is_modifiable: false - - id: 162 - code: enabled - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - man: Включает автоскейлинг PV. При включении параметра необходимо настроить autoScalePercentage и autoScaleTechWindow. Алерт при срабатывании вызывает modify у текущей услуги - is_modifiable: false - - id: 163 - code: schedule - data_type: integer >= 0 - required: true - default: "0" - value_list: - - "0" - - "5" - - "12" - - "23" - man: Требует включения autoScale. Окно обновления (пока не реализовано). Указывается час в виде Integer (0 / 5 / 12 / 23) - is_modifiable: false - - id: 164 - code: quota - data_type: integer > 0 - required: true - default: "100" - man: До какого размера возможно увеличивать диск, когда включен Autoscale - is_modifiable: false - - name: restart - id: 54 - kind: action - action: restart - man: ВНИМАНИЕ - Рестарт предполагает УДАЛЕНИЕ подов.
Может быть полная поломка. Крайне осторожно запускать при работе - params: [] - - name: resume - id: 114 - kind: instance - action: resume - params: [] - - name: suspend - id: 115 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/91_redis.yaml b/generated/test/resources_yaml/91_redis.yaml deleted file mode 100644 index be78ebb..0000000 --- a/generated/test/resources_yaml/91_redis.yaml +++ /dev/null @@ -1,222 +0,0 @@ -name: redis -service_id: 91 -service_display_name: Redis -service_short_name: redis -service_man: 'Redis кластер. Под кеши.
Ресурсная платформа: k8s.
Автоматизация: k8s operator
port 6379' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 21 - kind: instance - action: create - params: - - id: 1061 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 510 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1062 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 511 - code: cpu - data_type: integer > 0 - required: true - default: "500" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 512 - code: memory - data_type: integer > 0 - required: true - default: "512" - man: Указывается в Megabytes - is_modifiable: false - - id: 513 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 514 - code: disk - data_type: integer > 0 - required: true - default: "10" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 1063 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - is_modifiable: true - sub_params: - - id: 518 - code: slaveAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 515 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 516 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 517 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - name: delete - id: 22 - kind: instance - action: delete - params: [] - - name: modify - id: 394 - kind: instance - action: modify - params: - - id: 1064 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 519 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 520 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 521 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 522 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в GIGAbytes - is_modifiable: false - - id: 1065 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 526 - code: slaveAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 523 - code: masterIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 524 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - id: 525 - code: slaveIpSpace - data_type: string - required: true - default: "" - man: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - name: reconcile - id: 391 - kind: action - action: reconcile - params: [] - - name: resume - id: 393 - kind: instance - action: resume - params: [] - - name: suspend - id: 392 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/92_mongodb.yaml b/generated/test/resources_yaml/92_mongodb.yaml deleted file mode 100644 index 71658bb..0000000 --- a/generated/test/resources_yaml/92_mongodb.yaml +++ /dev/null @@ -1,241 +0,0 @@ -name: mongodb -service_id: 92 -service_display_name: MongoDB -service_short_name: mongodb -service_man: '# Инструкция по развертыванию MongoDB через платформу

## 1. Общая информация

MongoDB — документоориентированная распределённая база данных, обеспечивающая высокую доступность, горизонтальное масштабирование и гибкую модель хранения.

### Доступные операции с кластером

* **create** — создание базы
* **delete** — удаление базы данных
* **suspend** — остановка базы данных
* **resume** — запуск базы данных
* **modify** — изменение базы данных

### Доступные операции с пользователями

* **create_user** — создание пользователя
* **modify_user** — модификация пользователя
* **delete_user** — удаление пользователя

---

## 2. Параметры развертывания базы данных

Эти параметры необходимо указать при создании базы данных.

### Платформа для развертывания

Среда, в которой будет развернут кластер. Выбирается из предложенного списка.

### Количество узлов

Количество реплик в кластере MongoDB.
Оптимально выбирать нечётное значение, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

> **Примечание:** уменьшение количества узлов после создания кластера не рекомендуется.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных в гигабайтах.
Пример: `20`

### Имя ipSpace для публикации внешнего IP

Имя ipSpace, через который будет выделен внешний IP для доступа к master-узлу.
Укажите `no-needed`, если внешний адрес не требуется.
Пример: `internet-no-antiddos-v1`

### Список разрешённых IP-адресов

Список IP-адресов или подсетей, с которых разрешён доступ к кластеру.
Если оставить пустым — доступ открыт для всех источников.
Пример: `["10.0.0.0/24", "192.168.1.10/32"]`

---

## 3. Параметры модификации базы данных

Все параметры, которые вы указываете при модификации, являются необязательными — система автоматически подставит ранее сохранённые значения, и при необходимости вы можете изменить их на нужные.

### Количество узлов

Количество реплик в кластере MongoDB.
Оптимально выбирать нечётное значение, так как это обеспечивает корректный процесс выбора ведущего узла и сохраняет работоспособность базы данных даже при отказе одной из нод.

> **Примечание:** уменьшение количества узлов после создания кластера не рекомендуется.

### Квота памяти пода (MB)

Объём памяти для одного пода в мегабайтах.
Пример: `2048`

### Квота CPU пода (mCPU)

CPU-квота для пода в миликорах (1000 mCPU = 1 vCPU).
Пример: `1000`

### Размер диска для базы данных (GB)

Объём диска для хранения данных указывается в гигабайтах. Его можно увеличить при необходимости, однако уменьшить уже выделенный размер невозможно.
Пример: `20`

### Имя ipSpace для публикации внешнего IP

Имя ipSpace, через который будет выделен внешний IP для доступа к master-узлу.
Укажите `no-needed`, если внешний адрес не требуется.
Пример: `internet-no-antiddos-v1`

### Список разрешённых IP-адресов

Список IP-адресов или подсетей, с которых разрешён доступ к кластеру.
Если оставить пустым — доступ открыт для всех источников.
Пример: `["10.0.0.0/24", "192.168.1.10/32"]`

---

## 3. Параметры создания пользователя

### Имя пользователя

Указывается имя пользователя (любой регистр).
Пример: `readApp`

### Имя базы данных

Название базы, в которой будет создан пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read** — доступ только на чтение
* **readWrite** — чтение и изменение данных
* **dbOwner** — полный контроль над базой данных

---

## 4. Параметры модификации пользователя

### Имя пользователя

Необходимо выбрать существующего пользователя.
Если список пуст, требуется предварительно создать пользователя.

### Имя базы данных

База данных, к которой относится пользователь.
Если база отсутствует, она будет создана автоматически.
Пример: `Apps_9392`

### Имя роли

* **read**
* **readWrite**
* **dbOwner**

---

## 5. Параметры удаления пользователя

### Имя пользователя

Выберите пользователя из списка для удаления.

---

## 6. Выходные параметры

После успешного развертывания базы данных платформа возвращает ключевые параметры.

### internalConnect

Внутренний эндпоинт для подключения к кластеру из сервисов, работающих в той же сети.
Используется для внутренних интеграций и системных сервисов.

### externalConnect

Внешний FQDN для подключения к кластеру извне.
Порт подключения — **27017**.

---

## 7. Пример подключения

Перед подключением необходимо установить **mongosh**.

Команда подключения к базе данных:

```
mongosh "mongodb://<username>:<password>@<endpoint_db>:27017/<dbName>"
```

Где:

* `<username>` — имя пользователя
* `<password>` — пароль
* `<endpoint_db>` — internalConnect или externalConnect
* `<dbName>` — имя базы данных

## 8. Дополнительная информация

Сейчас соединение с базой работает без шифрования, и доступ к кластеру осуществляется только по логину и паролю.' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 37 - kind: instance - action: create - params: - - id: 1089 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 594 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - descr: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1090 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 596 - code: memory - data_type: integer > 0 - required: true - default: "1024" - is_modifiable: false - - id: 597 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 598 - code: disk - data_type: integer > 0 - required: true - default: "5" - man: Указывается в Gb - is_modifiable: false - - id: 595 - code: cpu - data_type: integer > 0 - required: true - default: "1000" - is_modifiable: false - - id: 1091 - code: accessConfiguration - data_type: map-fixed - required: true - is_modifiable: true - sub_params: - - id: 599 - code: masterIpSpace - data_type: string - required: true - default: "" - descr: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 600 - code: masterAccessList - data_type: json - required: true - default: '[]' - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: create_user - id: 198 - kind: subresource - action: create - subresource: user - params: - - id: 635 - code: username - data_type: string - required: true - - id: 636 - code: dbName - data_type: string - required: true - - id: 643 - code: role - data_type: string - required: true - value_list: - - dbOwner - - read - - readWrite - - name: delete - id: 190 - kind: instance - action: delete - params: [] - - name: delete_user - id: 199 - kind: subresource - action: delete - subresource: user - params: - - id: 644 - code: username - data_type: string - required: true - - name: modify - id: 201 - kind: instance - action: modify - params: - - id: 1092 - code: clusterConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 604 - code: disk - data_type: integer > 0 - required: true - default: "" - man: Указывается в Gb - is_modifiable: false - - id: 601 - code: cpu - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 602 - code: memory - data_type: integer > 0 - required: true - default: "" - is_modifiable: false - - id: 603 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1093 - code: accessConfiguration - data_type: map-fixed - required: true - sub_params: - - id: 605 - code: masterIpSpace - data_type: string - required: true - default: "" - descr: Из какого Ip-Space резервировать внешний IP
Список предоставляется из Организации, в котором развёрнута ресурсная платформа - is_modifiable: false - - id: 606 - code: masterAccessList - data_type: json - required: true - default: "" - man: Необходимо настраивать, когда зарезервирован внешний адрес
Если передан пустой массив, доступ выделяется всем - is_modifiable: false - - name: modify_user - id: 200 - kind: subresource - action: modify - subresource: user - params: - - id: 640 - code: username - data_type: string - required: true - - id: 641 - code: dbName - data_type: string - required: true - - id: 642 - code: role - data_type: string - required: true - value_list: - - dbOwner - - read - - readWrite - - name: reconcile - id: 409 - kind: action - action: reconcile - params: [] - - name: resume - id: 196 - kind: instance - action: resume - params: [] - - name: suspend - id: 194 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/93_rabbitmq.yaml b/generated/test/resources_yaml/93_rabbitmq.yaml deleted file mode 100644 index 9a95ab7..0000000 --- a/generated/test/resources_yaml/93_rabbitmq.yaml +++ /dev/null @@ -1,169 +0,0 @@ -name: rabbitmq -service_id: 93 -service_display_name: RabbitMQ -service_short_name: rabbitmq -service_man: '# Инструкция по развертыванию RabbitMQ через платформу

---
## 1. Общая информация
RabbitMQ — это брокер сообщений с открытым исходным кодом, который обеспечивает надёжную доставку сообщений между приложениями.
Позволяет надёжно передавать сообщения между сервисами и системами, интегрироваться с внешними решениями и использовать как внутренние, так и защищённые TLS-подключения. Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание кластера
- **modify** — Изменение параметров кластера
- **delete** — Удаление кластера
- **suspend** — Остановка кластера
- **resume** — Запуск кластера


---
## 2. Параметры развертывания кластера
Ниже перечислены параметры, которые необходимо указать при создании ресурса RabbitMQ:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развернут кластер). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик в кластере RabbitMQ. При значении больше 1 создаётся кластер с репликацией для обеспечения высокой доступности.
Пример: `1` (single node) или `3` (кластер с репликацией)

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `2048`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `1000`

* **Размер (gigabyte) диска для базы данных:**
Размер диска для хранения данных RabbitMQ в гигабайтах. Пример: `50`

* **Выделяет внешний IP для доступа к master:**
Флаг, определяющий, нужно ли выделять внешний IP для доступа к master-узлу. При включении создаётся Service типа LoadBalancer и настраивается VIP на порту 5672.
Пример: `true` или `false`

* **Имя ipSpace для публикации внешнего IP master:**
Если включен предыдущий параметр, то поле необходимо заполнить. Пример: `internet-no-antiddos-v1`


---
## 3. Модификация кластера
Операция **modify** позволяет изменить параметры существующего кластера RabbitMQ.

**Изменяемые параметры:**
* **Квота (megabyte) памяти пода** — можно увеличить или уменьшить
* **Квота (millicore) ядра пода** — можно увеличить или уменьшить
* **Размер (gigabyte) диска для базы данных** — можно только увеличить (уменьшение не поддерживается)
* **Количество узлов в сети** — можно только увеличить (уменьшение не поддерживается)
* **Выделяет внешний IP для доступа к master** — можно включить или выключить
* **Имя ipSpace для публикации внешнего IP master** — можно изменить при включенном внешнем IP

**Ограничения:**
* Уменьшение размера диска не поддерживается
* Уменьшение количества реплик не поддерживается

---
## 4. Выходные параметры
После успешного развертывания ресурса RabbitMQ платформа возвращает ряд параметров в разделе "Поля состояния (сгенерированные значения)":

**clusterDomain**
Домен кластера Kubernetes, в котором развернут RabbitMQ.
Пример: `cluster.local`

**inernalConnect**
Внутренние эндпоинты для подключения к кластеру RabbitMQ из других сервисов, развернутых внутри той же сети.
Содержит:
* **master** — FQDN для подключения к кластеру. Пример: `rabbitmqk8s.{namespace}.svc.{clusterDomain}`

**externalConnect**
Внешние эндпоинты для подключения к кластеру RabbitMQ извне.
Содержит объекты с полями:
* **admin** — доступ к веб-интерфейсу управления:
* `fqdn` — полное доменное имя для веб-интерфейса. Пример: `admin.{namespace}.{domain}`
* `ip` — IP-адрес
* **master** — доступ к AMQP протоколу (если включен внешний IP):
* `fqdn` — полное доменное имя для AMQP. Пример: `write.{namespace}.{domain}`
* `ip` — IP-адрес

**monitoring**
Информация о дашбордах мониторинга:
* **allDashboards** — URL для доступа ко всем дашбордам Grafana

Для подключения используются следующие порты:

* **5672** — AMQP протокол (внутренний и внешний доступ, если включен внешний IP)
* **15672** — веб-интерфейс управления (доступен через Ingress по HTTPS)

**Учётные данные**
Данные для подключения к RabbitMQ доступны в разделе "Учётные данные":
* **adminUser** — имя пользователя администратора
* **adminPass** — пароль администратора


---
## 5. Примеры подключения через консоль (rabbitmqadmin)

### Работа с очередями через rabbitmqadmin

Для работы с RabbitMQ через консоль используется утилита `rabbitmqadmin`. Установите её или используйте образ с предустановленной утилитой.

Задайте переменные окружения с учётными данными:

```bash
export RABBITMQ_USERNAME=<adminUser>
export RABBITMQ_PASSWORD=<adminPass>
export RABBIT_HOST=<host>
```

Где:
* `<adminUser>` и `<adminPass>` — учётные данные из раздела "Учётные данные"
* `<host>` — адрес для подключения:
* Для внешнего подключения: `externalConnect.admin.fqdn` или `externalConnect.master.fqdn`

**Создание очереди:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
declare queue name=healthcheck durable=false
```

**Листинг очередей:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
list queues
```

**Отправка сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
publish exchange=amq.default routing_key=healthcheck payload="ping-$(date +%s)"
```

**Чтение сообщения:**

```bash
rabbitmqadmin \
-H "$RABBIT_HOST" -P 15672 \
-u "$RABBITMQ_USERNAME" -p "$RABBITMQ_PASSWORD" \
get queue=healthcheck ackmode=ack_requeue_false
```

---
## 6. Дополнительная информация
Для подключения к кластеру RabbitMQ используйте полученные эндпоинты из раздела "Поля состояния (сгенерированные значения)" и учётные данные из раздела "Учётные данные".

Веб-интерфейс управления доступен по адресу, указанному в параметре `externalConnect.admin.fqdn` через HTTPS.

Для внутреннего подключения используйте адрес из `inernalConnect.master` на порту 5672.

---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 38 - kind: instance - action: create - params: - - id: 116 - code: resourceCPU - data_type: integer > 0 - required: true - default: "1000" - maxvalue: 4000 - minvalue: 500 - descr: 'Пример: `2000`' - sort: 20 - is_modifiable: true - - id: 117 - code: resourceMemory - data_type: integer > 0 - required: true - default: "1124" - maxvalue: 8192 - minvalue: 512 - descr: 'Пример: `2048`' - sort: 30 - is_modifiable: true - - id: 118 - code: resourceDisk - data_type: integer > 0 - required: true - default: "30" - maxvalue: 999 - minvalue: 5 - descr: 'Пример: `20`' - sort: 40 - is_modifiable: true - - id: 119 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Выбрать платформу из списка - sort: 10 - - id: 224 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `1`' - sort: 50 - is_modifiable: true - - id: 225 - code: needExternalAddressMaster - data_type: boolean - required: true - default: "false" - value_list: - - "false" - - "true" - descr: 'Флаг, определяющий, нужно ли выделять внешний IP для доступа к master. Пример: `true` или `false`' - sort: 60 - is_modifiable: true - - id: 543 - code: ipSpaceNameMaster - data_type: string - required: false - descr: 'Пример: `internet-no-antiddos-v1`' - is_modifiable: true - - name: delete - id: 39 - kind: instance - action: delete - params: [] - - name: modify - id: 106 - kind: instance - action: modify - params: - - id: 304 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 500 - descr: 'Пример: `2000`' - sort: 10 - - id: 305 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 512 - descr: 'Пример: `2048`' - sort: 20 - - id: 306 - code: resourceDisk - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: 'Пример: `20`' - sort: 10 - - id: 307 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: 'Пример: `1`' - sort: 40 - - id: 308 - code: needExternalAddressMaster - data_type: boolean - required: true - value_list: - - "false" - - "true" - descr: Выделяет внешний (белый) IP для доступа к master - sort: 50 - - id: 545 - code: ipSpaceNameMaster - data_type: string - required: false - descr: 'Пример: `internet-no-antiddos-v1`' - - name: resume - id: 117 - kind: instance - action: resume - params: [] - - name: suspend - id: 116 - kind: instance - action: suspend - man: Пока не работает из за того что RabbitmqCluster нельзя занулить - params: [] diff --git a/generated/test/resources_yaml/94_lucee.yaml b/generated/test/resources_yaml/94_lucee.yaml deleted file mode 100644 index 3f11b88..0000000 --- a/generated/test/resources_yaml/94_lucee.yaml +++ /dev/null @@ -1,252 +0,0 @@ -name: lucee -service_id: 94 -service_display_name: Lucee -service_short_name: lucee -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 49 - kind: instance - action: create - params: - - id: 1019 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 430 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1020 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 431 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 432 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 433 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1021 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 434 - code: domain - data_type: string - required: true - default: lucee-k7rjv4db - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1022 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 436 - code: gitPath - data_type: string - required: true - default: https://github.com/xahys/testlucee - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 437 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 435 - code: version - data_type: string - required: true - default: "6.0" - value_list: - - "5.4" - - "6.0" - is_modifiable: false - - id: 1023 - code: jsonEnv - data_type: map - required: false - default: '{}' - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 50 - is_modifiable: true - - name: delete - id: 50 - kind: instance - action: delete - params: [] - - name: modify - id: 55 - kind: instance - action: modify - params: - - id: 1024 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 440 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 438 - code: cpu - data_type: string - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 439 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 1025 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 441 - code: version - data_type: string - required: true - default: "" - value_list: - - "6.0" - - "5.4" - is_modifiable: false - - id: 442 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 443 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1026 - code: jsonEnv - data_type: map - required: false - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 30 - - name: reconcile - id: 373 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 53 - kind: action - action: redeploy - params: - - id: 1027 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 444 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: restart - id: 163 - kind: action - action: restart - params: [] - - name: resume - id: 95 - kind: instance - action: resume - params: [] - - name: suspend - id: 96 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/95_nodejs.yaml b/generated/test/resources_yaml/95_nodejs.yaml deleted file mode 100644 index b149744..0000000 --- a/generated/test/resources_yaml/95_nodejs.yaml +++ /dev/null @@ -1,252 +0,0 @@ -name: nodejs -service_id: 95 -service_display_name: NodeJS -service_short_name: nodejs -service_man: Развертывает контейнер в k8s кластере с node, который можно использовать для запуска своих приложений.
Приложение должно быть доступно по ссылки в git репозитории, которая является входным параметром. -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 58 - kind: instance - action: create - params: - - id: 1028 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 445 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - id: 1029 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 20 - is_modifiable: true - sub_params: - - id: 446 - code: cpu - data_type: integer > 0 - required: true - default: "200" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 447 - code: memory - data_type: integer > 0 - required: true - default: "256" - man: Указывается в Megabytes - is_modifiable: false - - id: 448 - code: replicas - data_type: integer > 0 - required: true - default: "1" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1030 - code: accessConfiguration - data_type: map-fixed - required: true - sort: 30 - sub_params: - - id: 449 - code: domain - data_type: string - required: true - default: node-evwflsbl - man: 'Если указан FQDN в формате: `myapp.example.com`, предварительно будет проверяться DNS-запись (А-запись **должна смотреть на IP Ingress кластера ресурсной платформы**)

Если указан в формате `myapp`, DNS-запись будет автоматически создана в служебной DNS-зоне' - is_modifiable: false - - id: 1031 - code: appConfiguration - data_type: map-fixed - required: true - sort: 40 - is_modifiable: true - sub_params: - - id: 450 - code: version - data_type: string - required: true - default: "23" - value_list: - - "18" - - "20" - - "22" - - "23" - is_modifiable: false - - id: 451 - code: gitPath - data_type: string - required: true - default: https://github.com/xahys/testnode.git - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 452 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1032 - code: jsonEnv - data_type: map - required: false - default: '{}' - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 50 - is_modifiable: true - - name: delete - id: 61 - kind: instance - action: delete - params: [] - - name: modify - id: 59 - kind: instance - action: modify - params: - - id: 1033 - code: clusterConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 453 - code: cpu - data_type: integer > 0 - required: true - default: "" - man: Указывается в Milicores
`1000` Mili == `1` Ядро - is_modifiable: false - - id: 454 - code: memory - data_type: integer > 0 - required: true - default: "" - man: Указывается в Megabytes - is_modifiable: false - - id: 455 - code: replicas - data_type: integer > 0 - required: true - default: "" - value_list: - - "1" - - "3" - - "5" - - "7" - is_modifiable: false - - id: 1034 - code: appConfiguration - data_type: map-fixed - required: true - sort: 20 - sub_params: - - id: 456 - code: version - data_type: string - required: true - default: "" - value_list: - - "18" - - "20" - - "22" - - "23" - is_modifiable: false - - id: 457 - code: gitPath - data_type: string - required: true - default: "" - man: 'Путь до репозитория приложения
Обязательно должен быть передан как URL: `https://....`

Должен быть сетевой доступ до URL для скачивания исходников' - is_modifiable: false - - id: 458 - code: healthPath - data_type: string - required: false - default: "" - man: Путь до healtcheck для запуска пода в Kubernetes, который отдаст 200 код
Пустое значение означает, что будет проверяться исключительно доступность приложения по порту - is_modifiable: false - - id: 1035 - code: jsonEnv - data_type: map - required: false - man: Передаётся в формате КЛЮЧ:ЗНАЧЕНИЕ - sort: 30 - - name: reconcile - id: 374 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 60 - kind: action - action: redeploy - params: - - id: 1036 - code: startupConfiguration - data_type: map-fixed - required: true - sort: 10 - sub_params: - - id: 459 - code: resourceRealm - data_type: string - required: true - default: "" - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - man: Кластер Kubernetes, на котором будет развернут экземпляр - is_modifiable: false - - name: resume - id: 121 - kind: instance - action: resume - params: [] - - name: suspend - id: 120 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/96_pgadmin.yaml b/generated/test/resources_yaml/96_pgadmin.yaml deleted file mode 100644 index 8c15bde..0000000 --- a/generated/test/resources_yaml/96_pgadmin.yaml +++ /dev/null @@ -1,168 +0,0 @@ -name: pgadmin -service_id: 96 -service_display_name: pgAdmin -service_short_name: pgadmin -service_man: Инструмент с графическим интерфейсом для управления базами данных PostgreSQL -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 62 - kind: instance - action: create - params: - - id: 164 - code: domain - data_type: string - required: true - default: pgadmin-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 165 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (milicores). 1000Mili = 1 Ядро - man: Квота ядра пода (milicores). 1000Mili = 1 Ядро - sort: 30 - is_modifiable: true - - id: 166 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 512 - descr: Квота памяти пода (MBytes) - man: Квота памяти пода (MBytes) - sort: 40 - is_modifiable: true - - id: 167 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 50 - is_modifiable: true - - id: 169 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 170 - code: login - data_type: string - required: true - regex: ^([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)$ - maxlength: 255 - minlength: 3 - descr: Логин для подключения к pgAdmin, в формате email (login@email.net) - man: Логин для подключения к pgAdmin, в формате email (login@email.net) - sort: 60 - - id: 171 - code: password - data_type: string - required: true - descr: Пароль для подключения к pgAdmin - man: Пароль для подключения к pgAdmin - sort: 70 - is_sensitive: true - - name: delete - id: 63 - kind: instance - action: delete - params: [] - - name: modify - id: 64 - kind: instance - action: modify - params: - - id: 172 - code: resourceCPU - data_type: integer > 0 - required: false - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - - id: 173 - code: resourceMemory - data_type: integer > 0 - required: false - maxvalue: 8192 - minvalue: 512 - descr: Квота памяти пода (MBytes) - man: Квота памяти пода (MBytes) - - id: 174 - code: resourceDisk - data_type: integer > 0 - required: false - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - - name: redeploy - id: 65 - kind: action - action: redeploy - params: - - id: 277 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Платформа для развертывания - - name: resume - id: 123 - kind: instance - action: resume - params: [] - - name: suspend - id: 122 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/97_nodered.yaml b/generated/test/resources_yaml/97_nodered.yaml deleted file mode 100644 index aa4c3f4..0000000 --- a/generated/test/resources_yaml/97_nodered.yaml +++ /dev/null @@ -1,97 +0,0 @@ -name: nodered -service_id: 97 -service_display_name: NodeRed -service_short_name: nodered -lifecycle: - suspend_on_destroy_default: false - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 72 - kind: instance - action: create - params: - - id: 193 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 30 - - id: 194 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - sort: 40 - - id: 195 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 60 - - id: 196 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 197 - code: domain - data_type: string - required: true - default: nodered-3 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru - man: Наименование поддомена, будет добавлено в k8stemplate.dev.nubes.ru - sort: 10 - - id: 323 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - sort: 50 - - name: delete - id: 73 - kind: instance - action: delete - params: [] diff --git a/generated/test/resources_yaml/98_http.yaml b/generated/test/resources_yaml/98_http.yaml deleted file mode 100644 index 93858c4..0000000 --- a/generated/test/resources_yaml/98_http.yaml +++ /dev/null @@ -1,170 +0,0 @@ -name: http -service_id: 98 -service_display_name: Простой HTTP контейнер -service_short_name: http -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 77 - kind: instance - action: create - params: - - id: 212 - code: domain - data_type: string - required: true - default: http-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 10 - - id: 213 - code: resourceCPU - data_type: integer > 0 - required: true - default: "100" - maxvalue: 4000 - minvalue: 100 - descr: Квота ядра пода (Указывается в mili) - man: Квота ядра пода (Указывается в mili) - sort: 30 - is_modifiable: true - - id: 214 - code: resourceMemory - data_type: integer > 0 - required: true - default: "200" - maxvalue: 8192 - minvalue: 128 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 40 - is_modifiable: true - - id: 215 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество реплик приложения. Трафик будет распределяться между подами в режиме Round-Robin - sort: 50 - is_modifiable: true - - id: 216 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Платформа для развертывания - man: Наименование кластера Kubernetes для подключения - sort: 20 - - id: 217 - code: registryPath - data_type: string - required: true - default: nexus-sa.tst.nubes.ru/docker-nubes/jolt - descr: Путь до image, который будет установлен как deployment - man: Путь до image, который будет установлен как deployment - sort: 60 - - id: 241 - code: jsonEnv - data_type: map - required: false - man: Массив переменных, будут переданы в env приложения - sort: 70 - - id: 782 - code: ingressAnnotations - data_type: map - required: false - default: '{}' - man: Формат ключ/значение
Поддерживает пока простые аннотации - sort: 80 - - name: delete - id: 78 - kind: instance - action: delete - params: [] - - name: modify - id: 81 - kind: instance - action: modify - params: - - id: 218 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 100 - - id: 219 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 128 - - id: 220 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - - name: reconcile - id: 403 - kind: action - action: reconcile - params: [] - - name: redeploy - id: 79 - kind: action - action: redeploy - params: - - id: 272 - code: resourceRealm - data_type: string - required: true - value_list: - - iot-naeel - - k8s-3-sandbox-nubes-ru - - k8s-4-sandbox-nubes-ru - - naeel-test-3 - func: getAvailableResourceRealms - descr: Платформа для развертывания - sort: 10 - - name: resume - id: 130 - kind: instance - action: resume - params: [] - - name: suspend - id: 131 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/99_gitea.yaml b/generated/test/resources_yaml/99_gitea.yaml deleted file mode 100644 index 25b5b95..0000000 --- a/generated/test/resources_yaml/99_gitea.yaml +++ /dev/null @@ -1,169 +0,0 @@ -name: gitea -service_id: 99 -service_display_name: Gitea -service_short_name: gitea -service_man: '# Инструкция по развертыванию Gitea через платформу

---
## 1. Общая информация
Gitea — это лёгкий, быстрый и самодостаточный сервис хостинга Git-репозиториев с веб-интерфейсом.
Позволяет создавать и управлять репозиториями, организациями, командами, а также настраивать CI/CD через интеграции.
Развёртывание полностью автоматизировано платформой.

## Доступные операции
- **create** — Создание сервиса Gitea
- **delete** — Удаление сервиса Gitea
- **modify** — Модификация сервиса Gitea
- **suspend** — Остановка сервиса Gitea
- **resume** — Запуск сервиса Gitea

---
## 2. Параметры развертывания
Ниже перечислены параметры, которые необходимо указать при создании ресурса Gitea:

* **Платформа для развертывания:**
Платформа для развертывания (среда, в которой будет развёрнут сервис). Пример: `k8s-3.ext.nubes.ru`.

* **Количество узлов в сети:**
Количество реплик подов Gitea. Пример: `1`

* **Квота (megabyte) памяти пода:**
Квота памяти пода в мегабайтах. Пример: `1024`

* **Квота (millicore) ядра пода:**
Квота CPU пода в миликорах (1000 mCPU = 1 vCPU). Пример: `500`

* **Размер (gigabyte) диска для базы данных:**
Размер PVC для хранения репозиториев и данных Gitea в гигабайтах. Пример: `10`

* **FQDN / Домен:**
Доменное имя, по которому будет доступен сервис. Пример: `git.example.com`

* **Инстанс услуги PostgreSQL:**
UUID услуги k8sPgsql, в которой будет создана база данных `gitea` и пользователь `techgitea`.

* **Дополнительные параметры сервера (iniServerParams):**
JSON-объект с дополнительными параметрами секции `[server]` в `app.ini`. Пример:
```json
{"MAX_CONTENT_LENGTH": "524288000"}
```

* **Дополнительные annotations для Ingress (ingressExtraAnnotations):**
JSON-объект с дополнительными annotations для Ingress-контроллера. Пример:
```json
{"nginx.ingress.kubernetes.io/proxy-body-size": "500m"}
```

---
## 3. Особенности подключения и работы с репозиториями

### 3.1. Доступ только по HTTPS
Сервис развёртывается с **rootless-образом** Gitea. SSH-сервер внутри и он **доступен только внутри кластера Kubernetes**.

**Внешний доступ к репозиториям осуществляется исключительно по HTTPS через Ingress.**

```bash
# Пример клонирования
https://<домен>/<пользователь>/<репозиторий>.git
```

### 3.2. Настройка git для передачи логина и пароля
Для работы с приватными репозиториями через HTTPS необходимо передать учётные данные. Рекомендуется использовать один из следующих способов:

**Встроить креды в URL remote**
```bash
git remote set-url origin https://<логин>:<пароль>@<домен>/<пользователь>/<репозиторий>.git
```
> **Важно:** Логин и пароль администратора создаются автоматически при развертывании и хранятся в Vault.

### 3.3. Работа с большими файлами
По умолчанию `MAX_CONTENT_LENGTH` установлен в **500 МБ**, а Ingress пропускает pack-файлы до **500 МБ**.

Если необходимо запушить файлы близкие к лимиту, увеличьте HTTP-буфер git на стороне клиента:
```bash
git config http.postBuffer 524288000
```

Для файлов **более 500 МБ** рекомендуется использовать **Git LFS**.

---
## 4. Выходные параметры
После успешного развертывания ресурса Gitea платформа возвращает ряд параметров.

Ключевым из них является:

**connectionUrl**
Это внешний URL для доступа к веб-интерфейсу и Git-репозиториям по HTTPS. Пример:
```
https://git.example.com
```

**internalConnect**
Внутренний эндпоинт для подключения к PostgreSQL из других сервисов внутри кластера.

---
## 5. Дополнительная информация
- База данных `gitea` и пользователь `techgitea` создаются автоматически в указанной услуге PostgreSQL.
- DNS-запись и TLS-сертификат (Let''s Encrypt) настраиваются автоматически на основе указанного домена.
- Для изменения параметров сервера Gitea (`app.ini`) используйте операцию **modify** с полем `iniServerParams`.
- Для добавления дополнительных Ingress-annotations используйте операцию **modify** с полем `ingressExtraAnnotations`.
---' -lifecycle: - suspend_on_destroy_default: true - adopt_existing_on_create_default: false -outputs: - params: - - code: state_params - type: map - - code: state_out - type: map - - code: state_params_flat - type: map - - code: state_out_flat - type: map - - code: vault_secrets - type: map - sensitive: true - - code: vault_url - type: string - - code: vault_user_path - type: string - - code: vault_fields - type: list -operations: - - name: create - id: 82 - kind: instance - action: create - params: - - id: 232 - code: resourceCPU - data_type: integer > 0 - required: true - default: "500" - maxvalue: 4000 - minvalue: 300 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - man: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 40 - is_modifiable: true - - id: 233 - code: resourceMemory - data_type: integer > 0 - required: true - default: "512" - maxvalue: 8192 - minvalue: 256 - descr: Квота памяти пода (Указывается в Megabytes) - man: Квота памяти пода (Указывается в Megabytes) - sort: 50 - is_modifiable: true - - id: 234 - code: resourceDisk - data_type: integer > 0 - required: true - default: "1" - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 60 - is_modifiable: true - - id: 235 - code: resourceInstances - data_type: integer > 0 - required: true - default: "1" - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 70 - is_modifiable: true - - id: 237 - code: domain - data_type: string - required: true - default: gitea-1 - regex: ^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ - unique_scope: provider - descr: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - man: Если будет указан FQDN, то происходят проверки DNS на валидность А-записи, или будет добавлено в зону resourceRealm - sort: 20 - - id: 238 - code: psqlUid - data_type: uuid - required: true - ref_svc_id: 90 - descr: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически - man: UUID Услуги k8sPgsql. Пользователя techGitea, базу данных gitea создаёт автоматически - sort: 10 - - id: 818 - code: ingressExtraAnnotations - data_type: map - required: false - is_modifiable: true - - id: 821 - code: iniServerParams - data_type: map - required: false - is_modifiable: true - - name: delete - id: 83 - kind: instance - action: delete - params: [] - - name: modify - id: 91 - kind: instance - action: modify - params: - - id: 259 - code: resourceCPU - data_type: integer > 0 - required: true - maxvalue: 4000 - minvalue: 500 - descr: Квота ядра пода (Указывается в mili). 1000Mili = 1 Ядро - sort: 10 - - id: 260 - code: resourceMemory - data_type: integer > 0 - required: true - maxvalue: 8192 - minvalue: 256 - descr: Квота памяти пода (Указывается в Megabytes) - sort: 20 - - id: 261 - code: resourceDisk - data_type: integer > 0 - required: true - maxvalue: 999 - minvalue: 1 - descr: Квота диска (Указывается в GIGAbytes) - man: Квота диска (Указывается в GIGAbytes) - sort: 30 - - id: 262 - code: resourceInstances - data_type: integer > 0 - required: true - maxvalue: 7 - minvalue: 1 - descr: Количество узлов - man: Количество узлов в сети - sort: 40 - - id: 819 - code: ingressExtraAnnotations - data_type: map - required: false - man: 'Примеры:
- `nginx.ingress.kubernetes.io/proxy-body-size: 500m` (Размер пакета)
- `nginx.ingress.kubernetes.io/proxy-read-timeout: "600"` (Время ожидания при чтении)
- `nginx.ingress.kubernetes.io/proxy-send-timeout: "600"` (Время ожидания отправке)' - - id: 820 - code: iniServerParams - data_type: map - required: false - man: 'Примеры:
- `MAX_CONTENT_LENGTH: 524288000(~500мб)` (Размер пакета в байтах)' - - name: resume - id: 127 - kind: instance - action: resume - params: [] - - name: suspend - id: 126 - kind: instance - action: suspend - params: [] diff --git a/generated/test/resources_yaml/embed.go b/generated/test/resources_yaml/embed.go deleted file mode 100644 index 72e3ff2..0000000 --- a/generated/test/resources_yaml/embed.go +++ /dev/null @@ -1,8 +0,0 @@ -package resources_yaml - -import "embed" - -// Files embeds unified service YAML specs for provider runtime lookup. -// -//go:embed *.yaml -var Files embed.FS