diff --git a/CHANGELOG.md b/CHANGELOG.md index c0fb7280c..d5de3632c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,7 @@ nav_order: 1 `organization:projects:write`, `organization:users:write`, `project:services:write`, `role:organization:admin`, `role:services:maintenance`, `role:services:recover`, `service:data:write`, `service:secrets:read`, `service:users:write`, remove `services:maintenance` +- Add `aiven_kafka_native_acl` resource ## [4.28.0] - 2024-10-21 diff --git a/docs/resources/kafka_native_acl.md b/docs/resources/kafka_native_acl.md new file mode 100644 index 000000000..ec8ae6fc3 --- /dev/null +++ b/docs/resources/kafka_native_acl.md @@ -0,0 +1,47 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "aiven_kafka_native_acl Resource - terraform-provider-aiven" +subcategory: "" +description: |- + Manages native acls in kafka service https://aiven.io/docs/products/kafka/concepts/acl. +--- + +# aiven_kafka_native_acl (Resource) + +Manages native acls in [kafka service](https://aiven.io/docs/products/kafka/concepts/acl). + + + + +## Schema + +### Required + +- `operation` (String) The operation. The possible values are `All`, `Alter`, `AlterConfigs`, `ClusterAction`, `Create`, `CreateTokens`, `Delete`, `Describe`, `DescribeConfigs`, `DescribeTokens`, `IdempotentWrite`, `Read` and `Write`. Changing this property forces recreation of the resource. +- `pattern_type` (String) Resource pattern used to match specified resources. The possible values are `LITERAL` and `PREFIXED`. Changing this property forces recreation of the resource. +- `permission_type` (String) The permission type. The possible values are `ALLOW` and `DENY`. Changing this property forces recreation of the resource. +- `principal` (String) Principal is in type:name' format. Maximum length: `256`. Changing this property forces recreation of the resource. +- `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. +- `resource_name` (String) The kafka resource name. Maximum length: `256`. Changing this property forces recreation of the resource. +- `resource_type` (String) The kafka resource type. The possible values are `Topic`, `Group`, `Cluster`, `TransactionalId`, `DelegationToken` and `User`. 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 + +- `host` (String) The host or `*` for all hosts. Maximum length: `256`. Changing this property forces recreation of the resource. +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) + +### Read-Only + +- `id` (String) The acl identifier + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) +- `default` (String) +- `delete` (String) +- `read` (String) +- `update` (String) diff --git a/internal/sdkprovider/provider/provider.go b/internal/sdkprovider/provider/provider.go index ef1634d89..654564c69 100644 --- a/internal/sdkprovider/provider/provider.go +++ b/internal/sdkprovider/provider/provider.go @@ -248,6 +248,7 @@ func Provider(version string) (*schema.Provider, error) { "aiven_kafka": kafka.ResourceKafka(), "aiven_kafka_user": kafka.ResourceKafkaUser(), "aiven_kafka_acl": kafka.ResourceKafkaACL(), + "aiven_kafka_native_acl": kafka.ResourceKafkaNativeACL(), "aiven_kafka_schema_registry_acl": kafkaschema.ResourceKafkaSchemaRegistryACL(), "aiven_kafka_topic": kafkatopic.ResourceKafkaTopic(), "aiven_kafka_schema": kafkaschema.ResourceKafkaSchema(), diff --git a/internal/sdkprovider/service/kafka/kafka_native_acl.go b/internal/sdkprovider/service/kafka/kafka_native_acl.go new file mode 100644 index 000000000..74a913870 --- /dev/null +++ b/internal/sdkprovider/service/kafka/kafka_native_acl.go @@ -0,0 +1,169 @@ +package kafka + +import ( + "context" + "fmt" + + avngen "github.com/aiven/go-client-codegen" + "github.com/aiven/go-client-codegen/handler/kafka" + + "github.com/aiven/terraform-provider-aiven/internal/common" + "github.com/aiven/terraform-provider-aiven/internal/plugin/errmsg" + "github.com/aiven/terraform-provider-aiven/internal/schemautil" + "github.com/aiven/terraform-provider-aiven/internal/schemautil/userconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +var aivenKafkaNativeACLSchema = map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Description: "The acl identifier", + Computed: true, + }, + "project": schemautil.CommonSchemaProjectReference, + "service_name": schemautil.CommonSchemaServiceNameReference, + "resource_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 256), + Description: userconfig.Desc("The kafka resource name").ForceNew().MaxLen(256).Build(), + }, + "resource_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(kafka.ResourceTypeChoices(), false), + Description: userconfig.Desc("The kafka resource type").ForceNew().PossibleValuesString(kafka.ResourceTypeChoices()...).Build(), + }, + "pattern_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(kafka.PatternTypeChoices(), false), + Description: userconfig.Desc("Resource pattern used to match specified resources").ForceNew().PossibleValuesString(kafka.PatternTypeChoices()...).Build(), + }, + "principal": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 256), + Description: userconfig.Desc("Principal is in type:name' format").ForceNew().MaxLen(256).Build(), + }, + "host": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 256), + Description: userconfig.Desc("The host or `*` for all hosts").ForceNew().MaxLen(256).Build(), + }, + "operation": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(kafka.OperationTypeChoices(), false), + Description: userconfig.Desc("The operation").ForceNew().PossibleValuesString(kafka.OperationTypeChoices()...).Build(), + }, + "permission_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(kafka.KafkaAclPermissionTypeChoices(), false), + Description: userconfig.Desc("The permission type").ForceNew().PossibleValuesString(kafka.KafkaAclPermissionTypeChoices()...).Build(), + }, +} + +func resourceKafkaNativeACLCreate(ctx context.Context, d *schema.ResourceData, client avngen.Client) error { + var req kafka.ServiceKafkaNativeAclAddIn + err := schemautil.ResourceDataGet(d, &req) + if err != nil { + return err + } + + project := d.Get("project").(string) + serviceName := d.Get("service_name").(string) + + acl, err := client.ServiceKafkaNativeAclAdd( + ctx, + project, + serviceName, + &req, + ) + if err != nil { + return err + } + + err = schemautil.ResourceDataSet(aivenKafkaNativeACLSchema, d, acl) + if err != nil { + return err + } + + d.SetId(schemautil.BuildResourceID(project, serviceName, *acl.Id)) + return resourceKafkaNativeACLRead(ctx, d, client) +} + +func resourceKafkaNativeACLRead(ctx context.Context, d *schema.ResourceData, client avngen.Client) error { + project, serviceName, aclID, err := schemautil.SplitResourceID3(d.Id()) + if err != nil { + return err + } + + acl, err := client.ServiceKafkaNativeAclGet( + ctx, + project, + serviceName, + aclID, + ) + if err != nil { + return err + } + + if acl == nil { + return fmt.Errorf( + errmsg.AivenResourceNotFound, + "aiven_kafka_native_acl", + aclID, + ) + } + + err = schemautil.ResourceDataSet(aivenKafkaNativeACLSchema, d, acl) + if err != nil { + return err + } + + return nil +} + +func resourceKafkaNativeACLDelete(ctx context.Context, d *schema.ResourceData, client avngen.Client) error { + project, serviceName, aclID, err := schemautil.SplitResourceID3(d.Id()) + if err != nil { + return err + } + + _, err = client.ServiceKafkaNativeAclDelete( + ctx, + project, + serviceName, + aclID, + ) + if err != nil { + return err + } + + return nil +} + +func ResourceKafkaNativeACL() *schema.Resource { + return &schema.Resource{ + Description: userconfig.Desc(`Manages native acls in [kafka service](https://aiven.io/docs/products/kafka/concepts/acl)`).Build(), + CreateContext: common.WithGenClient(resourceKafkaNativeACLCreate), + ReadContext: common.WithGenClient(resourceKafkaNativeACLRead), + DeleteContext: common.WithGenClient(resourceKafkaNativeACLDelete), + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Timeouts: schemautil.DefaultResourceTimeouts(), + Schema: aivenKafkaNativeACLSchema, + } +} diff --git a/internal/sdkprovider/service/kafka/kafka_native_acl_test.go b/internal/sdkprovider/service/kafka/kafka_native_acl_test.go new file mode 100644 index 000000000..a019404d6 --- /dev/null +++ b/internal/sdkprovider/service/kafka/kafka_native_acl_test.go @@ -0,0 +1,110 @@ +package kafka_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + acc "github.com/aiven/terraform-provider-aiven/internal/acctest" + "github.com/aiven/terraform-provider-aiven/internal/schemautil" +) + +// TestKafkaNativeAcl tests the kafka acl resource. +func TestKafkaNativeAcl(t *testing.T) { + projectName := os.Getenv("AIVEN_PROJECT_NAME") + serviceName := acc.RandStr() + resourceName := "aiven_kafka_native_acl.foo" + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: testAccCheckAivenKafkaNativeACLResourceDestroy, + Steps: []resource.TestStep{ + { + Config: testKafkaACLConfig(projectName, serviceName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "project"), + resource.TestCheckResourceAttrSet(resourceName, "service_name"), + resource.TestCheckResourceAttrSet(resourceName, "resource_name"), + resource.TestCheckResourceAttrSet(resourceName, "resource_type"), + resource.TestCheckResourceAttrSet(resourceName, "pattern_type"), + resource.TestCheckResourceAttrSet(resourceName, "principal"), + resource.TestCheckResourceAttrSet(resourceName, "host"), + resource.TestCheckResourceAttrSet(resourceName, "operation"), + resource.TestCheckResourceAttrSet(resourceName, "permission_type"), + ), + }, + }, + }) +} + +func testKafkaACLConfig(projectName string, serviceName string) string { + return fmt.Sprintf(` +data "aiven_project" "foo" { + project = "%s" +} + +resource "aiven_kafka" "bar" { + project = data.aiven_project.foo.project + cloud_name = "google-europe-west1" + plan = "startup-2" + service_name = "%s" + maintenance_window_dow = "monday" + maintenance_window_time = "10:00:00" + + kafka_user_config { + kafka { + group_max_session_timeout_ms = 70000 + log_retention_bytes = 1000000000 + } + } +} + +resource "aiven_kafka_native_acl" "foo" { + project = data.aiven_project.foo.project + service_name = aiven_kafka.bar.service_name + resource_name = "name-test" + resource_type = "Topic" + pattern_type = "LITERAL" + principal = "principal-test" + host = "host-test" + operation = "Create" + permission_type = "ALLOW" +}`, projectName, serviceName) +} + +func testAccCheckAivenKafkaNativeACLResourceDestroy(s *terraform.State) error { + client, err := acc.GetTestGenAivenClient() + if err != nil { + return fmt.Errorf("failed to check resource destroy") + } + + ctx := context.Background() + + // loop through the resources in state, verifying each kafka ACL is destroyed + for _, rs := range s.RootModule().Resources { + if rs.Type != "aiven_kafka_native_acl" { + continue + } + + projectName, serviceName, aclID, err := schemautil.SplitResourceID3(rs.Primary.ID) + if err != nil { + return err + } + + acl, err := client.ServiceKafkaNativeAclGet(ctx, projectName, serviceName, aclID) + if err != nil { + return err + } + + if acl != nil { + return fmt.Errorf("kafka native ACL (%s) still exists", aclID) + } + } + + return nil +} diff --git a/internal/sdkprovider/service/valkey/valkey_user.go b/internal/sdkprovider/service/valkey/valkey_user.go index a0049a3de..ff99a5c1e 100644 --- a/internal/sdkprovider/service/valkey/valkey_user.go +++ b/internal/sdkprovider/service/valkey/valkey_user.go @@ -125,7 +125,7 @@ func resourceValkeyUserCreate(ctx context.Context, d *schema.ResourceData, clien if _, ok := d.GetOk("password"); ok { var req = service.ServiceUserCredentialsModifyIn{NewPassword: schemautil.OptionalStringPointer(d, "password"), - Operation: service.OperationTypeResetCredentials} + Operation: service.ServiceUserCredentialsModifyOperationTypeResetCredentials} _, err := client.ServiceUserCredentialsModify(ctx, projectName, serviceName, username, &req) if err != nil { return err