From b6b98ba91b754ec3fc56ecec11d82fd5478e177b Mon Sep 17 00:00:00 2001 From: Myroslav Vivcharyk Date: Mon, 23 Dec 2024 17:14:11 +0100 Subject: [PATCH] feat(kafka_quota): added support for kafka quota --- Makefile | 7 +- internal/acctest/plancheck.go | 98 +++++++ internal/acctest/template_test.go | 59 +++++ .../sdkprovider/service/kafka/kafka_quota.go | 31 ++- .../service/kafka/kafka_quota_test.go | 249 ++++++++++++++---- 5 files changed, 380 insertions(+), 64 deletions(-) create mode 100644 internal/acctest/plancheck.go diff --git a/Makefile b/Makefile index e6eb454ac..a81334d07 100644 --- a/Makefile +++ b/Makefile @@ -112,10 +112,12 @@ lint: lint-go lint-test lint-docs lint-go: $(GOLANGCILINT) $(GOLANGCILINT) run --build-tags all --timeout=30m ./... +# Exclude files that use templates from linting +TERRAFMT_EXCLUDE = -not -path "./internal/acctest/*" \ + -not -path "./internal/sdkprovider/service/kafka/kafka_quota_test.go" lint-test: $(TERRAFMT) - $(TERRAFMT) diff ./internal -cfq - + find ./internal -type f $(TERRAFMT_EXCLUDE) -exec $(TERRAFMT) diff {} -cfq \; lint-docs: $(TFPLUGINDOCS) PROVIDER_AIVEN_ENABLE_BETA=1 $(TFPLUGINDOCS) generate --rendered-website-dir tmp @@ -132,7 +134,6 @@ lint-docs: $(TFPLUGINDOCS) fmt: fmt-test fmt-imports - fmt-test: $(TERRAFMT) $(TERRAFMT) fmt ./internal -fv diff --git a/internal/acctest/plancheck.go b/internal/acctest/plancheck.go new file mode 100644 index 000000000..894a132e3 --- /dev/null +++ b/internal/acctest/plancheck.go @@ -0,0 +1,98 @@ +package acctest + +import ( + "context" + "fmt" + + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + +type attributeChangeCheck struct { + resourceAddr string + attrs []string +} + +func ExpectOnlyAttributesChanged(resourceAddr string, attrs ...string) plancheck.PlanCheck { + return &attributeChangeCheck{ + resourceAddr: resourceAddr, + attrs: attrs, + } +} + +func (c *attributeChangeCheck) CheckPlan(_ context.Context, req plancheck.CheckPlanRequest, resp *plancheck.CheckPlanResponse) { + var targetResource *tfjson.ResourceChange + + // Find our resource in the changes + for _, rc := range req.Plan.ResourceChanges { + if rc.Address == c.resourceAddr { + targetResource = rc + break + } + } + + if targetResource == nil { + resp.Error = fmt.Errorf("resource %s not found in plan", c.resourceAddr) + return + } + + if targetResource.Change == nil { + resp.Error = fmt.Errorf("no changes found for resource %s", c.resourceAddr) + return + } + + // Convert Before and After to maps + before, ok := targetResource.Change.Before.(map[string]interface{}) + if !ok { + resp.Error = fmt.Errorf("before state for resource %s is not a map", c.resourceAddr) + return + } + + after, ok := targetResource.Change.After.(map[string]interface{}) + if !ok { + resp.Error = fmt.Errorf("after state for resource %s is not a map", c.resourceAddr) + + return + } + + // Create a set of expected changes + expectedChanges := make(map[string]struct{}) + for _, attr := range c.attrs { + expectedChanges[attr] = struct{}{} + } + + // Check all attributes in the after state + for key, afterValue := range after { + beforeValue, existsInBefore := before[key] + + // If value changed + if !existsInBefore || beforeValue != afterValue { + // Check if this change was expected + if _, expected := expectedChanges[key]; !expected { + resp.Error = fmt.Errorf( + "unexpected change in attribute %q for resource %s: before=%v, after=%v", + key, + c.resourceAddr, + beforeValue, + afterValue, + ) + return + } + // Remove from expected changes as we found it + delete(expectedChanges, key) + } + } + + // Check if all expected changes were found + if len(expectedChanges) > 0 { + remaining := make([]string, 0, len(expectedChanges)) + for attr := range expectedChanges { + remaining = append(remaining, attr) + } + resp.Error = fmt.Errorf( + "expected changes in attributes %v for resource %s were not found in plan", + remaining, + c.resourceAddr, + ) + } +} diff --git a/internal/acctest/template_test.go b/internal/acctest/template_test.go index 747ee483f..a997fa0d1 100644 --- a/internal/acctest/template_test.go +++ b/internal/acctest/template_test.go @@ -182,6 +182,65 @@ resource "aiven_project" "example_project" { assert.Equal(t, normalizeHCL(expected), normalizeHCL(result)) } +func TestTemplateNilAndMissingValues(t *testing.T) { + registry := NewTemplateRegistry("test") + + templateStr := `resource "aiven_service" "example" { + project = "{{ .project }}" + service_name = "{{ .service_name }}" + {{- if .maintenance_window_dow }} + maintenance_window_dow = "{{ .maintenance_window_dow }}" + {{- end }} + {{- if .maintenance_window_time }} + maintenance_window_time = "{{ .maintenance_window_time }}" + {{- end }} +}` + + registry.MustAddTemplate(t, "service", templateStr) + + tests := []struct { + name string + config map[string]any + expected string + }{ + { + name: "explicit_nil_value", + config: map[string]any{ + "project": "test-project", + "service_name": "test-service", + "maintenance_window_dow": nil, + "maintenance_window_time": "10:00:00", + }, + expected: `resource "aiven_service" "example" { + project = "test-project" + service_name = "test-service" + maintenance_window_time = "10:00:00" +}`, + }, + { + name: "missing_key", + config: map[string]any{ + "project": "test-project", + "service_name": "test-service", + // maintenance_window_dow is not in config at all + "maintenance_window_time": "10:00:00", + }, + expected: `resource "aiven_service" "example" { + project = "test-project" + service_name = "test-service" + maintenance_window_time = "10:00:00" +}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := registry.MustRender(t, "service", tt.config) + assert.Equal(t, normalizeHCL(tt.expected), normalizeHCL(result)) + }) + } +} + func TestCompositionBuilder(t *testing.T) { tests := []struct { name string diff --git a/internal/sdkprovider/service/kafka/kafka_quota.go b/internal/sdkprovider/service/kafka/kafka_quota.go index 6299b57c4..35a265b9b 100644 --- a/internal/sdkprovider/service/kafka/kafka_quota.go +++ b/internal/sdkprovider/service/kafka/kafka_quota.go @@ -43,7 +43,6 @@ It is possible to set default quotas for each (user, client-id), user or client- "consumer_byte_rate": { Type: schema.TypeInt, Optional: true, - ForceNew: true, Description: ` Defines the bandwidth limit in bytes/sec for each group of clients sharing a quota. Every distinct client group is allocated a specific quota, as defined by the cluster, on a per-broker basis. @@ -53,7 +52,6 @@ Exceeding this limit results in client throttling.`, "producer_byte_rate": { Type: schema.TypeInt, Optional: true, - ForceNew: true, Description: ` Defines the bandwidth limit in bytes/sec for each group of clients sharing a quota. Every distinct client group is allocated a specific quota, as defined by the cluster, on a per-broker basis. @@ -63,7 +61,6 @@ Exceeding this limit results in client throttling.`, "request_percentage": { Type: schema.TypeInt, Optional: true, - ForceNew: true, Description: ` Sets the maximum percentage of CPU time that a client group can use on request handler I/O and network threads per broker within a quota window. Exceeding this limit triggers throttling. @@ -75,8 +72,9 @@ The quota, expressed as a percentage, also indicates the total allowable CPU usa func ResourceKafkaQuota() *schema.Resource { return &schema.Resource{ Description: "Creates and manages quotas for an Aiven for Apache Kafka® service user.", - CreateContext: common.WithGenClient(resourceKafkaQuotaCreate), ReadContext: common.WithGenClient(resourceKafkaQuotaRead), + CreateContext: common.WithGenClient(resourceKafkaQuotaCreate), + UpdateContext: common.WithGenClient(resourceKafkaQuotaUpdate), DeleteContext: common.WithGenClient(resourceKafkaQuotaDelete), Timeouts: schemautil.DefaultResourceTimeouts(), @@ -112,6 +110,31 @@ func resourceKafkaQuotaCreate(ctx context.Context, d *schema.ResourceData, clien return resourceKafkaQuotaRead(ctx, d, client) } +func resourceKafkaQuotaUpdate(ctx context.Context, d *schema.ResourceData, client avngen.Client) error { + var ( + req kafka.ServiceKafkaQuotaCreateIn + ) + + project, service, _, _, err := schemautil.SplitResourceID4(d.Id()) + if err != nil { + return err + } + + if err := schemautil.ResourceDataGet( + d, + &req, + schemautil.RenameAlias("client_id", "client-id"), + ); err != nil { + return err + } + + if err := client.ServiceKafkaQuotaCreate(ctx, project, service, &req); err != nil { + return err + } + + return resourceKafkaQuotaRead(ctx, d, client) +} + func resourceKafkaQuotaRead(ctx context.Context, d *schema.ResourceData, client avngen.Client) error { project, serviceName, clientID, user, err := schemautil.SplitResourceID4(d.Id()) if err != nil { diff --git a/internal/sdkprovider/service/kafka/kafka_quota_test.go b/internal/sdkprovider/service/kafka/kafka_quota_test.go index 2a808f0e0..b1e794dd1 100644 --- a/internal/sdkprovider/service/kafka/kafka_quota_test.go +++ b/internal/sdkprovider/service/kafka/kafka_quota_test.go @@ -10,6 +10,7 @@ import ( "github.com/aiven/go-client-codegen/handler/kafka" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" acc "github.com/aiven/terraform-provider-aiven/internal/acctest" @@ -46,40 +47,25 @@ resource "aiven_kafka" "bar" { maintenance_window_time = "10:00:00" }`) - registry.MustAddTemplate(t, "kafka_quota_full", ` + registry.MustAddTemplate(t, "kafka_quota", ` resource "aiven_kafka_quota" "{{ .resource_name }}" { - project = data.aiven_project.foo.project - service_name = aiven_kafka.bar.service_name - user = "{{ .user }}" - client_id = "{{ .client_id }}" - consumer_byte_rate = {{ .consumer_byte_rate }} - producer_byte_rate = {{ .producer_byte_rate }} - request_percentage = {{ .request_percentage }} -}`) - - registry.MustAddTemplate(t, "kafka_quota_user", ` -resource "aiven_kafka_quota" "{{ .resource_name }}" { - project = data.aiven_project.foo.project - service_name = aiven_kafka.bar.service_name - user = "{{ .user }}" - request_percentage = {{ .request_percentage }} -}`) - - registry.MustAddTemplate(t, "kafka_quota_client_id", ` -resource "aiven_kafka_quota" "{{ .resource_name }}" { - project = data.aiven_project.foo.project - service_name = aiven_kafka.bar.service_name - client_id = "{{ .client_id }}" - producer_byte_rate = {{ .producer_byte_rate }} -}`) - - registry.MustAddTemplate(t, "wrong_configuration", ` -resource "aiven_kafka_quota" "{{ .resource_name }}" { - project = data.aiven_project.foo.project - service_name = aiven_kafka.bar.service_name - consumer_byte_rate = {{ .consumer_byte_rate }} - producer_byte_rate = {{ .producer_byte_rate }} - request_percentage = {{ .request_percentage }} + project = data.aiven_project.foo.project + service_name = aiven_kafka.bar.service_name + {{- if .user }} + user = "{{ .user }}" + {{- end }} + {{- if .client_id }} + client_id = "{{ .client_id }}" + {{- end }} + {{- if .consumer_byte_rate }} + consumer_byte_rate = {{ .consumer_byte_rate }} + {{- end }} + {{- if .producer_byte_rate }} + producer_byte_rate = {{ .producer_byte_rate }} + {{- end }} + {{- if .request_percentage }} + request_percentage = {{ .request_percentage }} + {{- end }} }`) var newComposition = func() *acc.CompositionBuilder { @@ -98,21 +84,22 @@ resource "aiven_kafka_quota" "{{ .resource_name }}" { Steps: []resource.TestStep{ { Config: newComposition(). - Add("kafka_quota_full", map[string]any{ + Add("kafka_quota", map[string]any{ "resource_name": "full", "service_name": serviceName, "user": user, "client_id": clientID, "consumer_byte_rate": 1000, "producer_byte_rate": 1000, - "request_percentage": 101, + "request_percentage": 101, // invalid value }). MustRender(t), ExpectError: regexp.MustCompile(`expected .+ to be in the range \(\d+ - \d+\), got \d+`), }, { + // missing user and client_id Config: newComposition(). - Add("wrong_configuration", map[string]any{ + Add("kafka_quota", map[string]any{ "resource_name": "full", "service_name": serviceName, "consumer_byte_rate": 1000, @@ -123,8 +110,9 @@ resource "aiven_kafka_quota" "{{ .resource_name }}" { ExpectError: regexp.MustCompile(`at least one of user or client_id must be specified`), }, { + // valid configuration Config: newComposition(). - Add("kafka_quota_full", map[string]any{ + Add("kafka_quota", map[string]any{ "resource_name": "full", "service_name": serviceName, "user": user, @@ -133,59 +121,206 @@ resource "aiven_kafka_quota" "{{ .resource_name }}" { "producer_byte_rate": 1000, "request_percentage": 10, }). - Add("kafka_quota_user", map[string]any{ - "resource_name": "user", + MustRender(t), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "project", projectName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "service_name", serviceName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "user", user), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "client_id", clientID), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "consumer_byte_rate", "1000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "producer_byte_rate", "1000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "request_percentage", "10"), + ), + }, + { + // check that the resource is not updated without changes + Config: newComposition(). + Add("kafka_quota", map[string]any{ + "resource_name": "full", "service_name": serviceName, "user": user, - "request_percentage": 20, + "client_id": clientID, + "consumer_byte_rate": 1000, + "producer_byte_rate": 1000, + "request_percentage": 10, + }). + MustRender(t), + PlanOnly: true, + ExpectNonEmptyPlan: false, + }, + { + // check plan that resource should be updated + Config: newComposition(). + Add("kafka_quota", map[string]any{ + "resource_name": "full", + "service_name": serviceName, + "user": user, + "client_id": clientID, + "consumer_byte_rate": 2000, + "producer_byte_rate": 2000, + "request_percentage": 100, }). MustRender(t), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + { + // check that the update action is triggered, only changed attributes are updated + Config: newComposition(). + Add("kafka_quota", map[string]any{ + "resource_name": "full", + "service_name": serviceName, + "user": user, + "client_id": clientID, + "consumer_byte_rate": 3000, + "producer_byte_rate": 3000, + "request_percentage": 10, + }). + MustRender(t), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(fmt.Sprintf("%s.full", kafkaQuotaResource), plancheck.ResourceActionUpdate), + acc.ExpectOnlyAttributesChanged(fmt.Sprintf("%s.full", kafkaQuotaResource), "consumer_byte_rate", "producer_byte_rate"), + }, + }, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "project", projectName), resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "service_name", serviceName), resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "user", user), resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "client_id", clientID), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "consumer_byte_rate", "1000"), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "producer_byte_rate", "1000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "consumer_byte_rate", "3000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "producer_byte_rate", "3000"), resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "request_percentage", "10"), + ), + }, + { + // check that resource is replaced when user is updated + Config: newComposition(). + Add("kafka_quota", map[string]any{ + "resource_name": "full", + "service_name": serviceName, + "user": fmt.Sprintf("%s_updated", user), + "client_id": clientID, + "consumer_byte_rate": 3000, + "producer_byte_rate": 3000, + "request_percentage": 10, + }). + MustRender(t), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectNonEmptyPlan(), + plancheck.ExpectResourceAction(fmt.Sprintf("%s.full", kafkaQuotaResource), plancheck.ResourceActionReplace), + }, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "project", projectName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "service_name", serviceName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "user", fmt.Sprintf("%s_updated", user)), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "client_id", clientID), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "consumer_byte_rate", "3000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "producer_byte_rate", "3000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "request_percentage", "10"), + ), + }, + { + // create new resource with only consumer_byte_rate set + Config: newComposition(). + Add("kafka_quota", map[string]any{ + "resource_name": "byte_rate", + "service_name": serviceName, + "user": user, + "client_id": clientID, + "consumer_byte_rate": 100, + }). + MustRender(t), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "project", projectName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "service_name", serviceName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "user", user), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "client_id", clientID), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "consumer_byte_rate", "100"), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "project", projectName), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "service_name", serviceName), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "user", user), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "request_percentage", "20"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "producer_byte_rate"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "request_percentage"), ), }, { + // check that the resource is updated and only consumer_byte_rate was modified Config: newComposition(). - Add("kafka_quota_client_id", map[string]any{ - "resource_name": "client", + Add("kafka_quota", map[string]any{ + "resource_name": "byte_rate", "service_name": serviceName, + "user": user, "client_id": clientID, - "producer_byte_rate": 1000, + "consumer_byte_rate": 3000, }). MustRender(t), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), plancheck.ResourceActionUpdate), + acc.ExpectOnlyAttributesChanged(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "consumer_byte_rate"), + }, + }, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "project", projectName), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "service_name", serviceName), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "client_id", clientID), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "producer_byte_rate", "1000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "project", projectName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "service_name", serviceName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "user", user), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "client_id", clientID), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "consumer_byte_rate", "3000"), + + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "producer_byte_rate"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.byte_rate", kafkaQuotaResource), "request_percentage"), ), }, { - Taint: []string{fmt.Sprintf("%s.client", kafkaQuotaResource)}, + // craete multiple resources with different configurations Config: newComposition(). - Add("kafka_quota_client_id", map[string]any{ + Add("kafka_quota", map[string]any{ + "resource_name": "full", + "service_name": serviceName, + "user": user, + "client_id": clientID, + "consumer_byte_rate": 4000, + "producer_byte_rate": 4000, + "request_percentage": 40, + }). + Add("kafka_quota", map[string]any{ + "resource_name": "user", + "service_name": serviceName, + "user": user, + "request_percentage": 20, + }). + Add("kafka_quota", map[string]any{ "resource_name": "client", "service_name": serviceName, "client_id": clientID, - "producer_byte_rate": 1000, + "producer_byte_rate": 2000, }). MustRender(t), Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "project", projectName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "service_name", serviceName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "user", user), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "client_id", clientID), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "consumer_byte_rate", "4000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "producer_byte_rate", "4000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.full", kafkaQuotaResource), "request_percentage", "40"), + + resource.TestCheckResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "project", projectName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "service_name", serviceName), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "user", user), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "request_percentage", "20"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "client_id"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "consumer_byte_rate"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.user", kafkaQuotaResource), "producer_byte_rate"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "project", projectName), resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "service_name", serviceName), resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "client_id", clientID), - resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "producer_byte_rate", "1000"), + resource.TestCheckResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "producer_byte_rate", "2000"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "user"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "consumer_byte_rate"), + resource.TestCheckNoResourceAttr(fmt.Sprintf("%s.client", kafkaQuotaResource), "request_percentage"), ), }, },