From c7646a955e5a100404f4603c6e64fe0e0b085d8f Mon Sep 17 00:00:00 2001 From: Mike Wang <52522981+mikechesterwang@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:06:59 +0800 Subject: [PATCH] feat: hide component id (#20) * update * feat: hide component id * rm component type data sources --- docs/data-sources/component_type.md | 28 -- docs/resources/cluster.md | 43 ++-- .../component_type/component_type.tf | 4 - examples/resources/cluster/resource.tf | 53 ++-- internal/cloudsdk/fake/fake.go | 81 +++--- .../acc/data_source_component_type_test.go | 34 --- .../provider/acc/resource_cluster_test.go | 52 ++-- .../provider/data_source_component_type.go | 194 -------------- internal/provider/provider.go | 4 +- internal/provider/resource_cluster.go | 243 ++++++++++++------ 10 files changed, 284 insertions(+), 452 deletions(-) delete mode 100644 docs/data-sources/component_type.md delete mode 100644 examples/data-sources/component_type/component_type.tf delete mode 100644 internal/provider/acc/data_source_component_type_test.go delete mode 100644 internal/provider/data_source_component_type.go diff --git a/docs/data-sources/component_type.md b/docs/data-sources/component_type.md deleted file mode 100644 index 28f45ab..0000000 --- a/docs/data-sources/component_type.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "risingwavecloud_component_type Data Source - terraform-provider-risingwavecloud" -subcategory: "" -description: |- - The type of the component of the RisingWave cluster ---- - -# risingwavecloud_component_type (Data Source) - -The type of the component of the RisingWave cluster - - - - -## Schema - -### Required - -- `component` (String) The component in a RisingWave cluster. Valid values are: `compute`, `compactor`, `frontend`, `meta`, `etcd` -- `memory_gib` (Number) Memory size in GiB -- `platform` (String) -- `region` (String) -- `vcpu` (Number) The number of the virtual CPU cores - -### Read-Only - -- `id` (String) The id of the RisingWave cluster. Valid values are diff --git a/docs/resources/cluster.md b/docs/resources/cluster.md index 277e82a..59014c6 100644 --- a/docs/resources/cluster.md +++ b/docs/resources/cluster.md @@ -48,14 +48,15 @@ Optional: Required: -- `resource` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--compactor--resource)) +- `default_node_group` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--compactor--default_node_group)) - -### Nested Schema for `spec.compactor.resource` + +### Nested Schema for `spec.compactor.default_node_group` Required: -- `id` (String) The component type ID of the node +- `cpu` (String) The CPU of the node +- `memory` (String) The memory size in of the node Optional: @@ -68,14 +69,15 @@ Optional: Required: -- `resource` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--compute--resource)) +- `default_node_group` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--compute--default_node_group)) - -### Nested Schema for `spec.compute.resource` + +### Nested Schema for `spec.compute.default_node_group` Required: -- `id` (String) The component type ID of the node +- `cpu` (String) The CPU of the node +- `memory` (String) The memory size in of the node Optional: @@ -88,14 +90,15 @@ Optional: Required: -- `resource` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--frontend--resource)) +- `default_node_group` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--frontend--default_node_group)) - -### Nested Schema for `spec.frontend.resource` + +### Nested Schema for `spec.frontend.default_node_group` Required: -- `id` (String) The component type ID of the node +- `cpu` (String) The CPU of the node +- `memory` (String) The memory size in of the node Optional: @@ -108,18 +111,19 @@ Optional: Required: -- `resource` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--meta--resource)) +- `default_node_group` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--meta--default_node_group)) Optional: - `etcd_meta_store` (Attributes) (see [below for nested schema](#nestedatt--spec--meta--etcd_meta_store)) - -### Nested Schema for `spec.meta.resource` + +### Nested Schema for `spec.meta.default_node_group` Required: -- `id` (String) The component type ID of the node +- `cpu` (String) The CPU of the node +- `memory` (String) The memory size in of the node Optional: @@ -131,18 +135,19 @@ Optional: Required: -- `resource` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--meta--etcd_meta_store--resource)) +- `default_node_group` (Attributes) The resource specification of the component (see [below for nested schema](#nestedatt--spec--meta--etcd_meta_store--default_node_group)) Optional: - `etcd_config` (String) The environment variable list of the etcd configuration - + ### Nested Schema for `spec.meta.etcd_meta_store.etcd_config` Required: -- `id` (String) The component type ID of the node +- `cpu` (String) The CPU of the node +- `memory` (String) The memory size in of the node Optional: diff --git a/examples/data-sources/component_type/component_type.tf b/examples/data-sources/component_type/component_type.tf deleted file mode 100644 index a8cd3f6..0000000 --- a/examples/data-sources/component_type/component_type.tf +++ /dev/null @@ -1,4 +0,0 @@ -data "risingwavecloud_component_type" "example" { - vcpu = 2 - memory_gib = 4 -} diff --git a/examples/resources/cluster/resource.tf b/examples/resources/cluster/resource.tf index 06c12fd..3f04e1f 100644 --- a/examples/resources/cluster/resource.tf +++ b/examples/resources/cluster/resource.tf @@ -1,27 +1,42 @@ -resource "risingwavecloud_cluster" "example" { - name = "dev" - version = "v1.3.0" - region = "us-east-2" - resourcev1 = { +resource "risingwavecloud_cluster" "my_cluster" { + region = "us-east-1" + name = "my_cluster" + version = "v1.8.0" + spec = { compute = { - type = "p-1c2g" - replica = 1 + default_node_group = { + cpu = "2" + memory = "8 GB" + replica = 1 + } } compactor = { - type = "p-2c4g" - replica = 3 - }, + default_node_group = { + cpu = "1" + memory = "4 GB" + replica = 1 + } + } frontend = { - type = "p-1c1g" - replica = 1 - }, + default_node_group = { + cpu = "1" + memory = "4 GB" + replica = 1 + } + } meta = { - type = "p-1c1g" - replica = 1 - }, - etcd = { - type = "p-1c1g" - replica = 3 + default_node_group = { + cpu = "1" + memory = "4 GB" + replica = 1 + } + etcd_meta_store = { + default_node_group = { + cpu = "1" + memory = "4 GB" + replica = 1 + } + } } } } diff --git a/internal/cloudsdk/fake/fake.go b/internal/cloudsdk/fake/fake.go index 859bf74..fa472c6 100644 --- a/internal/cloudsdk/fake/fake.go +++ b/internal/cloudsdk/fake/fake.go @@ -185,31 +185,30 @@ func (acc *FakeCloudClient) CreateClusterAwait(ctx context.Context, region strin return &cluster, nil } -func (acc *FakeCloudClient) GetTiers(ctx context.Context, _ string) ([]apigen_mgmt.Tier, error) { - debugFuncCaller() +var availableComponentTypes = []apigen_mgmt.AvailableComponentType{ + { + Id: "p-1c4g", + Cpu: "1", + Memory: "4 GB", + Maximum: 3, + }, + { + Id: "p-2c8g", + Cpu: "2", + Memory: "8 GB", + Maximum: 3, + }, +} - nodes := []apigen_mgmt.AvailableComponentType{ - { - Id: "p-1c4g", - Cpu: "1", - Memory: "4 GB", - Maximum: 3, - }, - { - Id: "p-2c8g", - Cpu: "2", - Memory: "8 GB", - Maximum: 3, - }, - } +func (acc *FakeCloudClient) GetTiers(ctx context.Context, _ string) ([]apigen_mgmt.Tier, error) { return []apigen_mgmt.Tier{ { Id: ptr.Ptr(apigen_mgmt.Standard), - AvailableMetaNodes: nodes, - AvailableComputeNodes: nodes, - AvailableCompactorNodes: nodes, - AvailableEtcdNodes: nodes, - AvailableFrontendNodes: nodes, + AvailableMetaNodes: availableComponentTypes, + AvailableComputeNodes: availableComponentTypes, + AvailableCompactorNodes: availableComponentTypes, + AvailableEtcdNodes: availableComponentTypes, + AvailableFrontendNodes: availableComponentTypes, AllowEnableComputeNodeFileCache: true, MaximumEtcdSizeGiB: 20, }, @@ -217,8 +216,6 @@ func (acc *FakeCloudClient) GetTiers(ctx context.Context, _ string) ([]apigen_mg } func (acc *FakeCloudClient) GetAvailableComponentTypes(ctx context.Context, region string, targetTier apigen_mgmt.TierId, component string) ([]apigen_mgmt.AvailableComponentType, error) { - debugFuncCaller() - tiers, err := acc.GetTiers(ctx, region) if err != nil { return nil, err @@ -315,26 +312,11 @@ func (acc *FakeCloudClient) UpdateEtcdConfigByNsIDAwait(ctx context.Context, nsI func reqResouceToClusterResource(reqResource *apigen_mgmt.TenantResourceRequest) apigen_mgmt.TenantResource { return apigen_mgmt.TenantResource{ Components: apigen_mgmt.TenantResourceComponents{ - Compute: &apigen_mgmt.ComponentResource{ - ComponentTypeId: reqResource.Components.Compute.ComponentTypeId, - Replica: reqResource.Components.Compute.Replica, - }, - Compactor: &apigen_mgmt.ComponentResource{ - ComponentTypeId: reqResource.Components.Compactor.ComponentTypeId, - Replica: reqResource.Components.Compactor.Replica, - }, - Frontend: &apigen_mgmt.ComponentResource{ - ComponentTypeId: reqResource.Components.Frontend.ComponentTypeId, - Replica: reqResource.Components.Frontend.Replica, - }, - Meta: &apigen_mgmt.ComponentResource{ - ComponentTypeId: reqResource.Components.Meta.ComponentTypeId, - Replica: reqResource.Components.Meta.Replica, - }, - Etcd: apigen_mgmt.ComponentResource{ - ComponentTypeId: reqResource.Components.Etcd.ComponentTypeId, - Replica: reqResource.Components.Etcd.Replica, - }, + Compute: componentReqToComponent(reqResource.Components.Compute), + Compactor: componentReqToComponent(reqResource.Components.Compactor), + Frontend: componentReqToComponent(reqResource.Components.Frontend), + Meta: componentReqToComponent(reqResource.Components.Meta), + Etcd: *componentReqToComponent(&reqResource.Components.Etcd), }, EnableComputeFileCache: reqResource.EnableComputeFileCache, EtcdVolumeSizeGiB: reqResource.EtcdVolumeSizeGiB, @@ -344,8 +326,15 @@ func reqResouceToClusterResource(reqResource *apigen_mgmt.TenantResourceRequest) } func componentReqToComponent(req *apigen_mgmt.ComponentResourceRequest) *apigen_mgmt.ComponentResource { - return &apigen_mgmt.ComponentResource{ - ComponentTypeId: req.ComponentTypeId, - Replica: req.Replica, + for _, c := range availableComponentTypes { + if c.Id == req.ComponentTypeId { + return &apigen_mgmt.ComponentResource{ + ComponentTypeId: req.ComponentTypeId, + Replica: req.Replica, + Cpu: c.Cpu, + Memory: c.Memory, + } + } } + return nil } diff --git a/internal/provider/acc/data_source_component_type_test.go b/internal/provider/acc/data_source_component_type_test.go deleted file mode 100644 index 1fa303a..0000000 --- a/internal/provider/acc/data_source_component_type_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package provider - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" -) - -func TestComponentTypeDataSource(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - // Read testing - { - Config: testComponentTypeDataSourceConfig("compute"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("data.risingwavecloud_component_type.test", "id", "p-2c8g"), - ), - }, - }, - }) -} - -func testComponentTypeDataSourceConfig(component string) string { - return fmt.Sprintf(`data "risingwavecloud_component_type" "test" { - platform = "aws" - region = "us-east-1" - vcpu = 2 - memory_gib = 8 - component = "%s" -}`, component) -} diff --git a/internal/provider/acc/resource_cluster_test.go b/internal/provider/acc/resource_cluster_test.go index 0ab9329..31b6807 100644 --- a/internal/provider/acc/resource_cluster_test.go +++ b/internal/provider/acc/resource_cluster_test.go @@ -61,7 +61,7 @@ func TestClusterResource(t *testing.T) { { Config: testClusterResourceUpdateConfig("v1.6.0", clusterName), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("risingwavecloud_cluster.test", "spec.compactor.resource.replica", "2"), + resource.TestCheckResourceAttr("risingwavecloud_cluster.test", "spec.compactor.default_node_group.replica", "2"), resource.TestCheckResourceAttr("risingwavecloud_cluster.test", "spec.risingwave_config", "[server]\nheartbeat_interval_ms = 997\n"), resource.TestCheckResourceAttr("risingwavecloud_cluster.test", "spec.meta.etcd_meta_store.etcd_config", "ETCD_MAX_REQUEST_BYTES: \"100000000\"\n"), ), @@ -79,31 +79,36 @@ resource "risingwavecloud_cluster" "test" { version = "%s" spec = { compute = { - resource = { - id = "p-2c8g" + default_node_group = { + cpu = "2" + memory = "8 GB" replica = 1 } } compactor = { - resource = { - id = "p-1c4g" + default_node_group = { + cpu = "1" + memory = "4 GB" replica = 1 } } frontend = { - resource = { - id = "p-1c4g" + default_node_group = { + cpu = "1" + memory = "4 GB" replica = 1 } } meta = { - resource = { - id = "p-1c4g" + default_node_group = { + cpu = "1" + memory = "4 GB" replica = 1 } etcd_meta_store = { - resource = { - id = "p-1c4g" + default_node_group = { + cpu = "1" + memory = "4 GB" replica = 1 } } @@ -122,31 +127,36 @@ resource "risingwavecloud_cluster" "test" { version = "%s" spec = { compute = { - resource = { - id = "p-2c8g" + default_node_group = { + cpu = "2" + memory = "8 GB" replica = 1 } } compactor = { - resource = { - id = "p-1c4g" + default_node_group = { + cpu = "1" + memory = "4 GB" replica = 2 } } frontend = { - resource = { - id = "p-1c4g" + default_node_group = { + cpu = "1" + memory = "4 GB" replica = 1 } } meta = { - resource = { - id = "p-1c4g" + default_node_group = { + cpu = "1" + memory = "4 GB" replica = 1 } etcd_meta_store = { - resource = { - id = "p-1c4g" + default_node_group = { + cpu = "1" + memory = "4 GB" replica = 1 } etcd_config = <<-EOT diff --git a/internal/provider/data_source_component_type.go b/internal/provider/data_source_component_type.go deleted file mode 100644 index 4c5af3f..0000000 --- a/internal/provider/data_source_component_type.go +++ /dev/null @@ -1,194 +0,0 @@ -package provider - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/risingwavelabs/terraform-provider-risingwavecloud/internal/cloudsdk" - - apigen_mgmt "github.com/risingwavelabs/terraform-provider-risingwavecloud/internal/cloudsdk/apigen/mgmt" -) - -const ( - ComponentCompute = "compute" - ComponentCompactor = "compactor" - ComponentFrontend = "frontend" - ComponentMeta = "meta" - ComponentEtcd = "etcd" -) - -var ( - ComponentMenu = fmt.Sprintf("`%s`, `%s`, `%s`, `%s`, `%s`", - ComponentCompute, - ComponentCompactor, - ComponentFrontend, - ComponentMeta, - ComponentEtcd, - ) -) - -var ( - DefaultTier = apigen_mgmt.Standard -) - -// Ensure provider defined types fully satisfy framework interfaces. -var _ datasource.DataSource = &ComponentTypeDataSource{} - -func NewComponentTypeDataSource() datasource.DataSource { - return &ComponentTypeDataSource{} -} - -type ComponentTypeDataSource struct { - client cloudsdk.CloudClientInterface -} - -type ComponentTypeDataSourceModel struct { - Platform types.String `tfsdk:"platform"` - Region types.String `tfsdk:"region"` - Component types.String `tfsdk:"component"` - VCPU types.Int64 `tfsdk:"vcpu"` - MemoryGiB types.Int64 `tfsdk:"memory_gib"` - ID types.String `tfsdk:"id"` -} - -func (d *ComponentTypeDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_component_type" -} - -func (d *ComponentTypeDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = schema.Schema{ - MarkdownDescription: "The type of the component of the RisingWave cluster", - Attributes: map[string]schema.Attribute{ - "platform": schema.StringAttribute{ - Required: true, - }, - "region": schema.StringAttribute{ - Required: true, - }, - "vcpu": schema.Int64Attribute{ - MarkdownDescription: "The number of the virtual CPU cores", - Required: true, - }, - "memory_gib": schema.Int64Attribute{ - MarkdownDescription: "Memory size in GiB", - Required: true, - }, - "component": schema.StringAttribute{ - MarkdownDescription: fmt.Sprintf( - "The component in a RisingWave cluster. Valid values are: %s", ComponentMenu, - ), - Required: true, - }, - "id": schema.StringAttribute{ - MarkdownDescription: "The id of the RisingWave cluster. Valid values are", - Computed: true, - }, - }, - } -} - -func (d *ComponentTypeDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { - // Prevent panic if the provider has not been configured. - if req.ProviderData == nil { - return - } - - client, ok := req.ProviderData.(cloudsdk.CloudClientInterface) - - if !ok { - resp.Diagnostics.AddError( - "Unexpected Data Source Configure Type", - fmt.Sprintf("Expected cloudsdk.AccountServiceClientInterface, got: %T. Please report this issue to the provider developers.", req.ProviderData), - ) - - return - } - - d.client = client -} - -func (d *ComponentTypeDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var data ComponentTypeDataSourceModel - - // Read Terraform configuration data into the model - resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - var ( - platform = data.Platform.ValueString() - region = data.Region.ValueString() - component = data.Component.ValueString() - vCPU = data.VCPU.ValueInt64() - memoryGiB = data.MemoryGiB.ValueInt64() - tier = DefaultTier - ) - - if len(platform) == 0 { - resp.Diagnostics.AddError( - "Missing platform", - "Platform is required to setup the provider.", - ) - return - } - - if len(region) == 0 { - resp.Diagnostics.AddError( - "Missing region", - "Region is required to setup the provider.", - ) - return - } - - if len(component) == 0 { - resp.Diagnostics.AddError( - "Missing component", - fmt.Sprintf("Component is required to setup the provider. Valid values are: %s", ComponentMenu), - ) - return - } - - availableComponentTypes, err := d.client.GetAvailableComponentTypes(ctx, region, tier, component) - if err != nil { - resp.Diagnostics.AddError( - "Failed to get available component types", - err.Error(), - ) - return - } - - ok := false - for _, c := range availableComponentTypes { - if fmt.Sprintf("%d", vCPU) == c.Cpu && fmt.Sprintf("%d GB", memoryGiB) == c.Memory { - data.ID = types.StringValue(c.Id) - ok = true - break - } - } - - if !ok { - resp.Diagnostics.AddError( - "Cannot found the corresponding component type", - fmt.Sprintf( - "The component type %s with CPU %d cores and memory %d GB is not available for the tier %s", - component, vCPU, memoryGiB, tier, - ), - ) - return - } - - // Write logs using the tflog package - // Documentation: https://terraform.io/plugin/log - tflog.Trace(ctx, "read a data source") - - // Save data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 6e0074a..19b640c 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -149,9 +149,7 @@ func (p *RisingWaveCloudProvider) Resources(ctx context.Context) []func() resour } func (p *RisingWaveCloudProvider) DataSources(ctx context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{ - NewComponentTypeDataSource, - } + return []func() datasource.DataSource{} } func New(version string) func() provider.Provider { diff --git a/internal/provider/resource_cluster.go b/internal/provider/resource_cluster.go index 45d1f42..862631d 100644 --- a/internal/provider/resource_cluster.go +++ b/internal/provider/resource_cluster.go @@ -2,9 +2,10 @@ package provider import ( "context" - "errors" "fmt" + "github.com/pkg/errors" + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -22,6 +23,7 @@ import ( ) var ( + DefaultTier = apigen_mgmt.Standard DefaultEnableComputeFileCache = true DefaultComputeFileCacheSizeGB = 20 DefaultEtcdVolumeSizeGB = 10 @@ -39,55 +41,56 @@ type ClusterResource struct { client cloudsdk.CloudClientInterface } -var resourceAttrTypes = map[string]attr.Type{ - "id": types.StringType, +var defaultNodeGroup = map[string]attr.Type{ + "cpu": types.StringType, + "memory": types.StringType, "replica": types.Int64Type, } var componentAttrTypes = map[string]attr.Type{ - "resource": types.ObjectType{ - AttrTypes: resourceAttrTypes, + "default_node_group": types.ObjectType{ + AttrTypes: defaultNodeGroup, }, } type EtcdMetaStoreModel struct { - Resource types.Object `tfsdk:"resource"` - EtcdConfig types.String `tfsdk:"etcd_config"` + DefaultNodeGroup types.Object `tfsdk:"default_node_group"` + EtcdConfig types.String `tfsdk:"etcd_config"` } var etcdMetaStoreAttrTypes = map[string]attr.Type{ - "resource": types.ObjectType{ - AttrTypes: resourceAttrTypes, + "default_node_group": types.ObjectType{ + AttrTypes: defaultNodeGroup, }, "etcd_config": types.StringType, } type ComputeSpecModel struct { - Resource types.Object `tfsdk:"resource"` + DefaultNodeGroup types.Object `tfsdk:"default_node_group"` } var computeAttrTypes = componentAttrTypes type CompactorSpecModel struct { - Resource types.Object `tfsdk:"resource"` + DefaultNodeGroup types.Object `tfsdk:"default_node_group"` } var compactorAttrTypes = componentAttrTypes type FrontendSpecModel struct { - Resource types.Object `tfsdk:"resource"` + DefaultNodeGroup types.Object `tfsdk:"default_node_group"` } var frontendAttrTypes = componentAttrTypes type MetaSpecModel struct { - Resource types.Object `tfsdk:"resource"` - EtcdMetaStore types.Object `tfsdk:"etcd_meta_store"` + DefaultNodeGroup types.Object `tfsdk:"default_node_group"` + EtcdMetaStore types.Object `tfsdk:"etcd_meta_store"` } var metaAttrTypes = map[string]attr.Type{ - "resource": types.ObjectType{ - AttrTypes: resourceAttrTypes, + "default_node_group": types.ObjectType{ + AttrTypes: defaultNodeGroup, }, "etcd_meta_store": types.ObjectType{ AttrTypes: etcdMetaStoreAttrTypes, @@ -126,8 +129,9 @@ type ClusterModel struct { Spec types.Object `tfsdk:"spec"` } -type ResourceModel struct { - Id types.String `tfsdk:"id"` +type NodeGroupModel struct { + CPU types.String `tfsdk:"cpu"` + Memory types.String `tfsdk:"memory"` Replica types.Int64 `tfsdk:"replica"` } @@ -136,10 +140,14 @@ func (r *ClusterResource) Metadata(ctx context.Context, req resource.MetadataReq } func (r *ClusterResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resourceAttribute := schema.SingleNestedAttribute{ + defauleNodeGroupAttribute := schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - MarkdownDescription: "The component type ID of the node", + "cpu": schema.StringAttribute{ + MarkdownDescription: "The CPU of the node", + Required: true, + }, + "memory": schema.StringAttribute{ + MarkdownDescription: "The memory size in of the node", Required: true, }, "replica": schema.Int64Attribute{ @@ -177,28 +185,28 @@ func (r *ClusterResource) Schema(ctx context.Context, req resource.SchemaRequest Attributes: map[string]schema.Attribute{ "compute": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ - "resource": resourceAttribute, + "default_node_group": defauleNodeGroupAttribute, }, Required: true, }, "compactor": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ - "resource": resourceAttribute, + "default_node_group": defauleNodeGroupAttribute, }, Required: true, }, "frontend": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ - "resource": resourceAttribute, + "default_node_group": defauleNodeGroupAttribute, }, Required: true, }, "meta": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ - "resource": resourceAttribute, + "default_node_group": defauleNodeGroupAttribute, "etcd_meta_store": schema.SingleNestedAttribute{ Attributes: map[string]schema.Attribute{ - "resource": resourceAttribute, + "default_node_group": defauleNodeGroupAttribute, "etcd_config": schema.StringAttribute{ MarkdownDescription: "The environment variable list of the etcd configuration", Optional: true, @@ -245,11 +253,75 @@ func (r *ClusterResource) Configure(ctx context.Context, req resource.ConfigureR r.client = client } +func (r *ClusterResource) nodeGroupModelToComponentResource( + ctx context.Context, diags diag.Diagnostics, nodeGroup *NodeGroupModel, region string, tier apigen_mgmt.TierId, component string, +) *apigen_mgmt.ComponentResource { + + var ( + reqCPU = nodeGroup.CPU.ValueString() + reqMem = nodeGroup.Memory.ValueString() + reqReplica = nodeGroup.Replica.ValueInt64() + ) + + availableTypes, err := r.client.GetAvailableComponentTypes(ctx, region, tier, component) + if err != nil { + diags.AddError( + "Failed to get available component types", + err.Error(), + ) + return nil + } + + var candidates []apigen_mgmt.AvailableComponentType + for _, availableType := range availableTypes { + if availableType.Cpu == reqCPU && availableType.Memory == reqMem { + candidates = append(candidates, availableType) + } + } + + if len(candidates) == 0 { + var availableCfg []string + for _, availableType := range availableTypes { + availableCfg = append(availableCfg, fmt.Sprintf("(%s, %s)", availableType.Cpu, availableType.Memory)) + } + errStr := "configuration (%s, %s) is not allowed for %s component in %s tier, available configurations are: %v" + diags.AddError( + "Invalid configuration", + fmt.Sprintf(errStr, reqCPU, reqMem, component, tier, availableCfg), + ) + return nil + } + + maximumReplica := 0 + chosenType := apigen_mgmt.AvailableComponentType{} + for _, candidate := range candidates { + if candidate.Maximum > maximumReplica { + maximumReplica = candidate.Maximum + chosenType = candidate + } + } + if reqReplica > int64(maximumReplica) { + diags.AddError( + "Invalid replica", + fmt.Sprintf("requested replica is greater than maximum replica %d", maximumReplica), + ) + return nil + } + + return &apigen_mgmt.ComponentResource{ + ComponentTypeId: chosenType.Id, + Replica: int(reqReplica), + Cpu: chosenType.Cpu, + Memory: chosenType.Memory, + } +} + func clusterToDataModel(cluster *apigen_mgmt.Tenant, data *ClusterModel) { data.Name = types.StringValue(cluster.TenantName) data.Version = types.StringValue(cluster.ImageTag) data.ID = types.StringValue(cluster.NsId.String()) data.Region = types.StringValue(cluster.Region) + data.Spec = types.ObjectValueMust( clusterSpecAttrTypes, map[string]attr.Value{ @@ -257,10 +329,11 @@ func clusterToDataModel(cluster *apigen_mgmt.Tenant, data *ClusterModel) { "compute": types.ObjectValueMust( computeAttrTypes, map[string]attr.Value{ - "resource": types.ObjectValueMust( - resourceAttrTypes, + "default_node_group": types.ObjectValueMust( + defaultNodeGroup, map[string]attr.Value{ - "id": types.StringValue(cluster.Resources.Components.Compute.ComponentTypeId), + "cpu": types.StringValue(cluster.Resources.Components.Compute.Cpu), + "memory": types.StringValue(cluster.Resources.Components.Compute.Memory), "replica": types.Int64Value(int64(cluster.Resources.Components.Compute.Replica)), }, ), @@ -269,10 +342,11 @@ func clusterToDataModel(cluster *apigen_mgmt.Tenant, data *ClusterModel) { "compactor": types.ObjectValueMust( compactorAttrTypes, map[string]attr.Value{ - "resource": types.ObjectValueMust( - resourceAttrTypes, + "default_node_group": types.ObjectValueMust( + defaultNodeGroup, map[string]attr.Value{ - "id": types.StringValue(cluster.Resources.Components.Compactor.ComponentTypeId), + "cpu": types.StringValue(cluster.Resources.Components.Compactor.Cpu), + "memory": types.StringValue(cluster.Resources.Components.Compactor.Memory), "replica": types.Int64Value(int64(cluster.Resources.Components.Compactor.Replica)), }, ), @@ -281,10 +355,11 @@ func clusterToDataModel(cluster *apigen_mgmt.Tenant, data *ClusterModel) { "frontend": types.ObjectValueMust( frontendAttrTypes, map[string]attr.Value{ - "resource": types.ObjectValueMust( - resourceAttrTypes, + "default_node_group": types.ObjectValueMust( + defaultNodeGroup, map[string]attr.Value{ - "id": types.StringValue(cluster.Resources.Components.Frontend.ComponentTypeId), + "cpu": types.StringValue(cluster.Resources.Components.Frontend.Cpu), + "memory": types.StringValue(cluster.Resources.Components.Frontend.Memory), "replica": types.Int64Value(int64(cluster.Resources.Components.Frontend.Replica)), }, ), @@ -293,20 +368,22 @@ func clusterToDataModel(cluster *apigen_mgmt.Tenant, data *ClusterModel) { "meta": types.ObjectValueMust( metaAttrTypes, map[string]attr.Value{ - "resource": types.ObjectValueMust( - resourceAttrTypes, + "default_node_group": types.ObjectValueMust( + defaultNodeGroup, map[string]attr.Value{ - "id": types.StringValue(cluster.Resources.Components.Meta.ComponentTypeId), + "cpu": types.StringValue(cluster.Resources.Components.Meta.Cpu), + "memory": types.StringValue(cluster.Resources.Components.Meta.Memory), "replica": types.Int64Value(int64(cluster.Resources.Components.Meta.Replica)), }, ), "etcd_meta_store": types.ObjectValueMust( etcdMetaStoreAttrTypes, map[string]attr.Value{ - "resource": types.ObjectValueMust( - resourceAttrTypes, + "default_node_group": types.ObjectValueMust( + defaultNodeGroup, map[string]attr.Value{ - "id": types.StringValue(cluster.Resources.Components.Etcd.ComponentTypeId), + "cpu": types.StringValue(cluster.Resources.Components.Etcd.Cpu), + "memory": types.StringValue(cluster.Resources.Components.Etcd.Memory), "replica": types.Int64Value(int64(cluster.Resources.Components.Etcd.Replica)), }, ), @@ -319,7 +396,7 @@ func clusterToDataModel(cluster *apigen_mgmt.Tenant, data *ClusterModel) { ) } -func dataModelToCluster(ctx context.Context, data *ClusterModel, cluster *apigen_mgmt.Tenant) diag.Diagnostics { +func (r *ClusterResource) dataModelToCluster(ctx context.Context, data *ClusterModel, cluster *apigen_mgmt.Tenant) diag.Diagnostics { diags := diag.Diagnostics{} objectAsOptions := basetypes.ObjectAsOptions{ UnhandledUnknownAsEmpty: true, @@ -327,19 +404,19 @@ func dataModelToCluster(ctx context.Context, data *ClusterModel, cluster *apigen } var ( - spec ClusterSpecModel - compactorSpec CompactorSpecModel - compactorResource ResourceModel - computeSpec ComputeSpecModel - computeResource ResourceModel - frontendSpec FrontendSpecModel - frontendResource ResourceModel - metaSpec MetaSpecModel - metaResource ResourceModel - - useEtcdMetaStore bool - etcdMetaStore EtcdMetaStoreModel - etcdResource ResourceModel + spec ClusterSpecModel + compactorSpec CompactorSpecModel + compactorDefaultNodeGroup NodeGroupModel + computeSpec ComputeSpecModel + computeDefaultNodeGroup NodeGroupModel + frontendSpec FrontendSpecModel + frontendDefaultNodeGroup NodeGroupModel + metaSpec MetaSpecModel + metaDefaultNodeGroup NodeGroupModel + + useEtcdMetaStore bool + etcdMetaStore EtcdMetaStoreModel + etcdDefaultNodeGroup NodeGroupModel ) tflog.Trace(ctx, "parsing spec") @@ -347,25 +424,25 @@ func dataModelToCluster(ctx context.Context, data *ClusterModel, cluster *apigen tflog.Trace(ctx, "parsing compactorSpec") diags.Append(spec.CompactorSpec.As(ctx, &compactorSpec, objectAsOptions)...) - diags.Append(compactorSpec.Resource.As(ctx, &compactorResource, objectAsOptions)...) + diags.Append(compactorSpec.DefaultNodeGroup.As(ctx, &compactorDefaultNodeGroup, objectAsOptions)...) tflog.Trace(ctx, "parsing computeSpec") diags.Append(spec.ComputeSpec.As(ctx, &computeSpec, objectAsOptions)...) - diags.Append(computeSpec.Resource.As(ctx, &computeResource, objectAsOptions)...) + diags.Append(computeSpec.DefaultNodeGroup.As(ctx, &computeDefaultNodeGroup, objectAsOptions)...) tflog.Trace(ctx, "parsing frontendSpec") diags.Append(spec.FrontendSpec.As(ctx, &frontendSpec, objectAsOptions)...) - diags.Append(frontendSpec.Resource.As(ctx, &frontendResource, objectAsOptions)...) + diags.Append(frontendSpec.DefaultNodeGroup.As(ctx, &frontendDefaultNodeGroup, objectAsOptions)...) tflog.Trace(ctx, "parsing metaSpec") diags.Append(spec.MetaSpec.As(ctx, &metaSpec, objectAsOptions)...) - diags.Append(metaSpec.Resource.As(ctx, &metaResource, objectAsOptions)...) + diags.Append(metaSpec.DefaultNodeGroup.As(ctx, &metaDefaultNodeGroup, objectAsOptions)...) if !metaSpec.EtcdMetaStore.IsNull() { tflog.Trace(ctx, "parsing etcdMetaStore") useEtcdMetaStore = true diags.Append(metaSpec.EtcdMetaStore.As(ctx, &etcdMetaStore, objectAsOptions)...) - diags.Append(etcdMetaStore.Resource.As(ctx, &etcdResource, objectAsOptions)...) + diags.Append(etcdMetaStore.DefaultNodeGroup.As(ctx, &etcdDefaultNodeGroup, objectAsOptions)...) } if !useEtcdMetaStore { @@ -394,28 +471,24 @@ func dataModelToCluster(ctx context.Context, data *ClusterModel, cluster *apigen cluster.RwConfig = spec.RisingWaveConfig.ValueString() cluster.EtcdConfig = etcdMetaStore.EtcdConfig.ValueString() cluster.Region = data.Region.ValueString() + + computeResource := r.nodeGroupModelToComponentResource(ctx, diags, &computeDefaultNodeGroup, cluster.Region, cluster.Tier, "compute") + compactorResource := r.nodeGroupModelToComponentResource(ctx, diags, &compactorDefaultNodeGroup, cluster.Region, cluster.Tier, "compactor") + frontendResource := r.nodeGroupModelToComponentResource(ctx, diags, &frontendDefaultNodeGroup, cluster.Region, cluster.Tier, "frontend") + metaResource := r.nodeGroupModelToComponentResource(ctx, diags, &metaDefaultNodeGroup, cluster.Region, cluster.Tier, "meta") + etcdResuorce := r.nodeGroupModelToComponentResource(ctx, diags, &etcdDefaultNodeGroup, cluster.Region, cluster.Tier, "etcd") + + if diags.HasError() { + return diags + } + cluster.Resources = apigen_mgmt.TenantResource{ Components: apigen_mgmt.TenantResourceComponents{ - Compactor: &apigen_mgmt.ComponentResource{ - ComponentTypeId: compactorResource.Id.ValueString(), - Replica: int(compactorResource.Replica.ValueInt64()), - }, - Compute: &apigen_mgmt.ComponentResource{ - ComponentTypeId: computeResource.Id.ValueString(), - Replica: int(computeResource.Replica.ValueInt64()), - }, - Frontend: &apigen_mgmt.ComponentResource{ - ComponentTypeId: frontendResource.Id.ValueString(), - Replica: int(frontendResource.Replica.ValueInt64()), - }, - Meta: &apigen_mgmt.ComponentResource{ - ComponentTypeId: metaResource.Id.ValueString(), - Replica: int(metaResource.Replica.ValueInt64()), - }, - Etcd: apigen_mgmt.ComponentResource{ - ComponentTypeId: etcdResource.Id.ValueString(), - Replica: int(etcdResource.Replica.ValueInt64()), - }, + Compactor: compactorResource, + Compute: computeResource, + Frontend: frontendResource, + Meta: metaResource, + Etcd: *etcdResuorce, }, ComputeFileCacheSizeGiB: DefaultComputeFileCacheSizeGB, EnableComputeFileCache: DefaultEnableComputeFileCache, @@ -447,7 +520,7 @@ func (r *ClusterResource) Create(ctx context.Context, req resource.CreateRequest return } - resp.Diagnostics.Append(dataModelToCluster(ctx, &data, &cluster)...) + resp.Diagnostics.Append(r.dataModelToCluster(ctx, &data, &cluster)...) if resp.Diagnostics.HasError() { return @@ -576,7 +649,9 @@ func (r *ClusterResource) Read(ctx context.Context, req resource.ReadRequest, re } func resourceEqual(a, b *apigen_mgmt.ComponentResource) bool { - return a.ComponentTypeId == b.ComponentTypeId && a.Replica == b.Replica + return a.Cpu == b.Cpu && + a.Memory == b.Memory && + a.Replica == b.Replica } func (r *ClusterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { @@ -608,7 +683,7 @@ func (r *ClusterResource) Update(ctx context.Context, req resource.UpdateRequest var updated = apigen_mgmt.Tenant{} - resp.Diagnostics.Append(dataModelToCluster(ctx, &data, &updated)...) + resp.Diagnostics.Append(r.dataModelToCluster(ctx, &data, &updated)...) previous, err := r.client.GetClusterByNsID(ctx, nsID) if err != nil { @@ -657,7 +732,7 @@ func (r *ClusterResource) Update(ctx context.Context, req resource.UpdateRequest if !resourceEqual(&previous.Resources.Components.Etcd, &updated.Resources.Components.Etcd) { resp.Diagnostics.AddError( "Cannot update immutable field", - "Etcd resource cannot be changed", + fmt.Sprintf("Etcd resource cannot be changed, previous: %v, updated: %v", previous.Resources.Components.Etcd, updated.Resources.Components.Etcd), ) } if resp.Diagnostics.HasError() {