Skip to content

Commit

Permalink
feat(kafka_quota): added support for kafka quota
Browse files Browse the repository at this point in the history
  • Loading branch information
vmyroslav committed Dec 27, 2024
1 parent 0f2962e commit 56ea60a
Show file tree
Hide file tree
Showing 15 changed files with 1,498 additions and 5 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ __debug_bin
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Go workspace file
go.work
go.work.sum

# Dependency directories
vendor/
packrd/
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ nav_order: 1
<!-- Always keep the following header in place: -->
<!--## [MAJOR.MINOR.PATCH] - YYYY-MM-DD -->


## [MAJOR.MINOR.PATCH] - YYYY-MM-DD

- Add `aiven_kafka_quota` resource

## [4.31.1] - 2024-12-23

- Validate whether the `aiven_project.billing_group` field has changed before calling admin API
Expand Down
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -132,7 +134,6 @@ lint-docs: $(TFPLUGINDOCS)

fmt: fmt-test fmt-imports


fmt-test: $(TERRAFMT)
$(TERRAFMT) fmt ./internal -fv

Expand Down
84 changes: 84 additions & 0 deletions docs/resources/kafka_quota.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "aiven_kafka_quota Resource - terraform-provider-aiven"
subcategory: ""
description: |-
Creates and manages quotas for an Aiven for Apache Kafka® service user.
---

# aiven_kafka_quota (Resource)

Creates and manages quotas for an Aiven for Apache Kafka® service user.

## Example Usage

```terraform
resource "aiven_kafka_quota" "example_quota" {
project = data.aiven_project.foo.project
service_name = aiven_kafka.example_kafka.service_name
user = "example-kafka-user"
client_id = "example_client"
consumer_byte_rate = 1000
producer_byte_rate = 1000
request_percentage = 50
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `project` (String) The name of the project this resource belongs to. To set up proper dependencies please refer to this variable as a reference. Changing this property forces recreation of the resource.
- `service_name` (String) The name of the service that this resource belongs to. To set up proper dependencies please refer to this variable as a reference. Changing this property forces recreation of the resource.

### Optional

- `client_id` (String) Represents a logical group of clients, assigned a unique name by the client application.
Quotas can be applied based on user, client-id, or both.
The most relevant quota is chosen for each connection.
All connections within a quota group share the same quota.
It is possible to set default quotas for each (user, client-id), user or client-id group by specifying 'default'
- `consumer_byte_rate` (Number) 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.
Exceeding this limit results in client throttling.
- `producer_byte_rate` (Number) 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.
Exceeding this limit results in client throttling.
- `request_percentage` (Number) 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.
The quota, expressed as a percentage, also indicates the total allowable CPU usage for the client groups sharing the quota.
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
- `user` (String) Represents a logical group of clients, assigned a unique name by the client application.
Quotas can be applied based on user, client-id, or both.
The most relevant quota is chosen for each connection.
All connections within a quota group share the same quota.
It is possible to set default quotas for each (user, client-id), user or client-id group by specifying 'default'

### Read-Only

- `id` (String) The ID of this resource.

<a id="nestedblock--timeouts"></a>
### Nested Schema for `timeouts`

Optional:

- `create` (String)
- `default` (String)
- `delete` (String)
- `read` (String)
- `update` (String)

## Import

Import is supported using the following syntax:

```shell
# When both USER and CLIENT_ID are specified
terraform import aiven_kafka_quota.example_quota PROJECT/SERVICE_NAME/CLIENT_ID/USER
# When only USER is specified
terraform import aiven_kafka_quota.example_quota PROJECT/SERVICE_NAME//USER
# When only CLIENT_ID is specified
terraform import aiven_kafka_quota.example_quota PROJECT/SERVICE_NAME/CLIENT_ID/
```
6 changes: 6 additions & 0 deletions examples/resources/aiven_kafka_quota/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# When both USER and CLIENT_ID are specified
terraform import aiven_kafka_quota.example_quota PROJECT/SERVICE_NAME/CLIENT_ID/USER
# When only USER is specified
terraform import aiven_kafka_quota.example_quota PROJECT/SERVICE_NAME//USER
# When only CLIENT_ID is specified
terraform import aiven_kafka_quota.example_quota PROJECT/SERVICE_NAME/CLIENT_ID/
9 changes: 9 additions & 0 deletions examples/resources/aiven_kafka_quota/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
resource "aiven_kafka_quota" "example_quota" {
project = data.aiven_project.foo.project
service_name = aiven_kafka.example_kafka.service_name
user = "example-kafka-user"
client_id = "example_client"
consumer_byte_rate = 1000
producer_byte_rate = 1000
request_percentage = 50
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.23

require (
github.com/aiven/aiven-go-client/v2 v2.33.0
github.com/aiven/go-client-codegen v0.74.0
github.com/aiven/go-client-codegen v0.76.0
github.com/avast/retry-go v3.0.0+incompatible
github.com/dave/jennifer v1.7.1
github.com/docker/go-units v0.5.0
Expand Down Expand Up @@ -65,7 +65,7 @@ require (
github.com/hashicorp/hcl/v2 v2.23.0 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.21.0 // indirect
github.com/hashicorp/terraform-json v0.23.0 // indirect
github.com/hashicorp/terraform-json v0.23.0
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/aiven/go-api-schemas v1.106.0 h1:qncRsbiaGnU9JE9fmTFHclTCBem+t+6EPMXG
github.com/aiven/go-api-schemas v1.106.0/go.mod h1:z7dGvufm6If4gOdVr7dWTuFZmll9FOZr5Z5CSxGpebA=
github.com/aiven/go-client-codegen v0.74.0 h1:CqZUq8aGdhgcKJuM+YbC8RVUVgnCw9aA6yk0hMfvXWg=
github.com/aiven/go-client-codegen v0.74.0/go.mod h1:QKN/GgLMGWd6+gPEucXlZPi5vC3C6RpD3UeBRQOLI1Y=
github.com/aiven/go-client-codegen v0.76.0 h1:4Od3FmkF9ApdQU+FBgxQLxFge9cmpnMziXpKddkeWow=
github.com/aiven/go-client-codegen v0.76.0/go.mod h1:QKN/GgLMGWd6+gPEucXlZPi5vC3C6RpD3UeBRQOLI1Y=
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
Expand Down
98 changes: 98 additions & 0 deletions internal/acctest/plancheck.go
Original file line number Diff line number Diff line change
@@ -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,
)
}
}
Loading

0 comments on commit 56ea60a

Please sign in to comment.