From 0fed0e5de7ff5195cb91427d4164e5ec8b967b50 Mon Sep 17 00:00:00 2001 From: Guangning E Date: Wed, 26 Mar 2025 16:22:38 +0800 Subject: [PATCH 1/6] Support volume --- cloud/data_source_volume.go | 88 +++++++++++++ cloud/resource_volume.go | 252 ++++++++++++++++++++++++++++++++++++ docs/data-sources/volume.md | 29 +++++ docs/resources/volume.md | 30 +++++ examples/volume/main.tf | 45 +++++++ go.mod | 35 +++-- go.sum | 25 ++-- 7 files changed, 482 insertions(+), 22 deletions(-) create mode 100644 cloud/data_source_volume.go create mode 100644 cloud/resource_volume.go create mode 100644 docs/data-sources/volume.md create mode 100644 docs/resources/volume.md create mode 100644 examples/volume/main.tf diff --git a/cloud/data_source_volume.go b/cloud/data_source_volume.go new file mode 100644 index 0000000..cf5fe29 --- /dev/null +++ b/cloud/data_source_volume.go @@ -0,0 +1,88 @@ +package cloud + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func dataSourceVolume() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceVolumeRead, + Schema: map[string]*schema.Schema{ + "organization": { + Type: schema.TypeString, + Required: true, + Description: descriptions["organization"], + ValidateFunc: validateNotBlank, + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: descriptions["volume_name"], + ValidateFunc: validateNotBlank, + }, + "bucket": { + Type: schema.TypeString, + Description: descriptions["bucket"], + Computed: true, + }, + "path": { + Type: schema.TypeString, + Description: descriptions["path"], + Computed: true, + }, + "region": { + Type: schema.TypeString, + Description: descriptions["bucket_region"], + Computed: true, + }, + "role_arn": { + Type: schema.TypeString, + Description: descriptions["role_arn"], + Computed: true, + }, + }, + } +} + +func dataSourceVolumeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + namespace := d.Get("organization").(string) + name := d.Get("name").(string) + clientSet, err := getClientSet(getFactoryFromMeta(meta)) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_INIT_CLIENT_ON_READ_VOLUME: %w", err)) + } + volume, err := clientSet.CloudV1alpha1().Volumes(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + d.SetId("") + return nil + } + return diag.FromErr(fmt.Errorf("ERROR_READ_VOLUME: %w", err)) + } + d.SetId(fmt.Sprintf("%s/%s", namespace, name)) + if err = d.Set("organization", volume.Namespace); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_ORGANIZATION: %w", err)) + } + if err = d.Set("name", volume.Name); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_NAME: %w", err)) + } + if err = d.Set("bucket", volume.Spec.Bucket); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_BUCKET: %w", err)) + } + if err = d.Set("path", volume.Spec.Path); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_PATH: %w", err)) + } + if err = d.Set("region", volume.Spec.AWS.Region); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_REGION: %w", err)) + } + if err = d.Set("role_arn", volume.Spec.AWS.RoleArn); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_ROLE_ARN: %w", err)) + } + d.SetId(fmt.Sprintf("%s/%s", volume.Namespace, volume.Name)) + return nil +} diff --git a/cloud/resource_volume.go b/cloud/resource_volume.go new file mode 100644 index 0000000..d53f42c --- /dev/null +++ b/cloud/resource_volume.go @@ -0,0 +1,252 @@ +package cloud + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/streamnative/cloud-api-server/pkg/apis/cloud" + "github.com/streamnative/cloud-api-server/pkg/apis/cloud/v1alpha1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "time" +) + +func resourceVolume() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceVolumeCreate, + ReadContext: resourceVolumeRead, + UpdateContext: resourceVolumeUpdate, + DeleteContext: resourceVolumeDelete, + Schema: map[string]*schema.Schema{ + "organization": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: descriptions["organization"], + ValidateFunc: validateNotBlank, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: descriptions["volume_name"], + ValidateFunc: validateNotBlank, + }, + "bucket": { + Type: schema.TypeString, + Required: true, + Description: descriptions["bucket"], + ValidateFunc: validateNotBlank, + }, + "path": { + Type: schema.TypeString, + Required: true, + Description: descriptions["path"], + ValidateFunc: validateNotBlank, + }, + "region": { + Type: schema.TypeString, + Required: true, + Description: descriptions["bucket_region"], + ValidateFunc: validateNotBlank, + }, + "role_arn": { + Type: schema.TypeString, + Required: true, + Description: descriptions["role_arn"], + ValidateFunc: validateNotBlank, + }, + "ready": { + Type: schema.TypeString, + Computed: true, + Description: descriptions["volume_ready"], + }, + }, + } +} + +func resourceVolumeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + namespace := d.Get("organization").(string) + name := d.Get("name").(string) + bucket := d.Get("bucket").(string) + path := d.Get("path").(string) + region := d.Get("region").(string) + roleArn := d.Get("role_arn").(string) + clientSet, err := getClientSet(getFactoryFromMeta(meta)) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_INIT_CLIENT_ON_CREATE_VOLUME: %w", err)) + } + v := &v1alpha1.Volume{ + TypeMeta: metav1.TypeMeta{ + Kind: "Volume", + APIVersion: v1alpha1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha1.VolumeSpec{ + Bucket: bucket, + Path: path, + Type: "aws", + AWS: &v1alpha1.AWSSpec{ + RoleArn: roleArn, + Region: region, + }, + }, + } + volume, err := clientSet.CloudV1alpha1().Volumes(namespace).Create(ctx, v, metav1.CreateOptions{ + FieldManager: "terraform-create", + }) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_CREATE_VOLUME: %w", err)) + } + if volume.Status.Conditions != nil && len(volume.Status.Conditions) > 0 { + ready := false + for _, condition := range volume.Status.Conditions { + if condition.Type == "Ready" && condition.Status == "True" { + ready = true + } + } + if ready { + _ = d.Set("organization", namespace) + _ = d.Set("name", name) + return resourceVolumeRead(ctx, d, meta) + } + } + d.SetId(fmt.Sprintf("%s/%s", namespace, name)) + err = retry.RetryContext(ctx, 10*time.Minute, func() *retry.RetryError { + dia := resourceVolumeRead(ctx, d, meta) + if dia.HasError() { + return retry.RetryableError(fmt.Errorf("ERROR_READ_VOLUME: %w", dia[0].Summary)) + } + ready := d.Get("ready").(string) + if ready == "False" { + return retry.RetryableError(fmt.Errorf( + "CONTINUE_WAITING_VOLUME_READY: %s", "volume is not ready yet")) + } + return nil + }) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_WAIT_VOLUME_READY: %w", err)) + } + return nil +} + +func resourceVolumeDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + namespace := d.Get("organization").(string) + name := d.Get("name").(string) + clientSet, err := getClientSet(getFactoryFromMeta(meta)) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_INIT_CLIENT_ON_DELETE_VOLUME: %w", err)) + } + err = clientSet.CloudV1alpha1().Volumes(namespace).Delete(ctx, name, metav1.DeleteOptions{}) + err = retry.RetryContext(ctx, 10*time.Minute, func() *retry.RetryError { + _, err := clientSet.CloudV1alpha1().Volumes(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return retry.RetryableError(fmt.Errorf("ERROR_DELETE_VOLUME: %w", err)) + } + return retry.RetryableError(fmt.Errorf("CONTINUE_WAITING_VOLUME_DELETE: %s", "volume is not deleted yet")) + }) + d.SetId("") + return nil +} + +func resourceVolumeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + namespace := d.Get("organization").(string) + name := d.Get("name").(string) + clientSet, err := getClientSet(getFactoryFromMeta(meta)) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_INIT_CLIENT_ON_READ_VOLUME: %w", err)) + } + _ = d.Set("ready", "False") + volume, err := clientSet.CloudV1alpha1().Volumes(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + d.SetId("") + return nil + } + return diag.FromErr(fmt.Errorf("ERROR_READ_VOLUME: %w", err)) + } + if err = d.Set("organization", volume.Namespace); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_ORGANIZATION: %w", err)) + } + if err = d.Set("name", volume.Name); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_NAME: %w", err)) + } + if err = d.Set("bucket", volume.Spec.Bucket); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_BUCKET: %w", err)) + } + if err = d.Set("path", volume.Spec.Path); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_PATH: %w", err)) + } + if err = d.Set("region", volume.Spec.AWS.Region); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_REGION: %w", err)) + } + if err = d.Set("role_arn", volume.Spec.AWS.RoleArn); err != nil { + return diag.FromErr(fmt.Errorf("ERROR_SET_ROLE_ARN: %w", err)) + } + d.SetId(fmt.Sprintf("%s/%s", volume.Namespace, volume.Name)) + if volume.Status.Conditions != nil && len(volume.Status.Conditions) > 0 { + for _, condition := range volume.Status.Conditions { + if condition.Type == "Ready" { + _ = d.Set("ready", condition.Status) + } + } + } + return nil +} + +func resourceVolumeUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + namespace := d.Get("organization").(string) + name := d.Get("name").(string) + bucket := d.Get("bucket").(string) + path := d.Get("path").(string) + region := d.Get("region").(string) + roleArn := d.Get("role_arn").(string) + clientSet, err := getClientSet(getFactoryFromMeta(meta)) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_INIT_CLIENT_ON_UPDATE_VOLUME: %w", err)) + } + volume, err := clientSet.CloudV1alpha1().Volumes(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_GET_VOLUME_ON_UPDATE: %w", err)) + } + labels := volume.GetLabels() + if labels != nil { + if l, ok := labels[cloud.AnnotationVolumeAttachCluster]; ok && l != "" { + return diag.FromErr(fmt.Errorf( + "ERROR_UPDATE_VOLUME_ATTACHED_CLUSTER: this volume has been attached one cluster, it don't support update, %w", err)) + } + } + volume.Spec.Bucket = bucket + volume.Spec.Path = path + volume.Spec.AWS.Region = region + volume.Spec.AWS.RoleArn = roleArn + _, err = clientSet.CloudV1alpha1().Volumes(namespace).Update(ctx, volume, metav1.UpdateOptions{}) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_UPDATE_VOLUME: %w", err)) + } + err = retry.RetryContext(ctx, 10*time.Minute, func() *retry.RetryError { + dia := resourceVolumeRead(ctx, d, meta) + if dia.HasError() { + return retry.RetryableError(fmt.Errorf("ERROR_READ_VOLUME: %w", dia[0].Summary)) + } + ready := d.Get("ready").(string) + if ready == "False" { + return retry.RetryableError(fmt.Errorf( + "CONTINUE_WAITING_VOLUME_READY: %s", "volume is not ready yet")) + } + return nil + }) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_WAIT_VOLUME_READY: %w", err)) + } + d.SetId(fmt.Sprintf("%s/%s", namespace, name)) + return nil +} diff --git a/docs/data-sources/volume.md b/docs/data-sources/volume.md new file mode 100644 index 0000000..631fa30 --- /dev/null +++ b/docs/data-sources/volume.md @@ -0,0 +1,29 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "streamnative_volume Data Source - terraform-provider-streamnative" +subcategory: "" +description: |- + +--- + +# streamnative_volume (Data Source) + + + + + + +## Schema + +### Required + +- `name` (String) +- `organization` (String) The organization name + +### Read-Only + +- `bucket` (String) The bucket name +- `id` (String) The ID of this resource. +- `path` (String) The path of the bucket +- `region` (String) The region of the bucket +- `role_arn` (String) The role arn of the bucket, it is used to access the bucket diff --git a/docs/resources/volume.md b/docs/resources/volume.md new file mode 100644 index 0000000..8d07fd2 --- /dev/null +++ b/docs/resources/volume.md @@ -0,0 +1,30 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "streamnative_volume Resource - terraform-provider-streamnative" +subcategory: "" +description: |- + +--- + +# streamnative_volume (Resource) + + + + + + +## Schema + +### Required + +- `bucket` (String) The bucket name +- `name` (String) +- `organization` (String) The organization name +- `path` (String) The path of the bucket +- `region` (String) The region of the bucket +- `role_arn` (String) The role arn of the bucket, it is used to access the bucket + +### Read-Only + +- `id` (String) The ID of this resource. +- `ready` (String) Volume is ready, it will be set to 'True' after the volume is ready diff --git a/examples/volume/main.tf b/examples/volume/main.tf new file mode 100644 index 0000000..ce72e15 --- /dev/null +++ b/examples/volume/main.tf @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +terraform { + required_providers { + streamnative = { + version = "0.1.0" + source = "streamnative/streamnative" + } + } +} + +provider "streamnative" { + # Please replace path use your own key file path + key_file_path = "" +} + +resource "streamnative_volume" "test-volume" { + organization = "max" + name = "test-volume" + bucket = "test-pulsar" + path = "test-pulsar/data" + region = "us-west-2" + role_arn = "arn:aws:iam::123456789012:role/role-name" +} + +data "streamnative_volume" "test-volume" { + depends_on = [streamnative_volume.test-volume] + organization = streamnative_volume.test-volume.organization + name = streamnative_volume.test-volume.name +} diff --git a/go.mod b/go.mod index 610faf5..8af2c90 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/streamnative/terraform-provider-streamnative -go 1.22.4 +go 1.22.11 + +toolchain go1.23.7 require ( github.com/99designs/keyring v1.2.1 @@ -11,14 +13,14 @@ require ( github.com/lestrrat-go/jwx/v2 v2.0.21 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 - github.com/streamnative/cloud-api-server v1.25.2-0.20250110052043-df94d8adecc9 + github.com/streamnative/cloud-api-server v1.33.3-0.20250304171918-06119c9f160b github.com/streamnative/cloud-cli v0.22.0-rc.1 github.com/stretchr/testify v1.10.0 github.com/xhit/go-str2duration/v2 v2.1.0 - k8s.io/apimachinery v0.30.0 + k8s.io/apimachinery v0.30.9 k8s.io/cli-runtime v0.29.4 k8s.io/client-go v12.0.0+incompatible - k8s.io/kubectl v0.29.4 + k8s.io/kubectl v0.30.9 k8s.io/utils v0.0.0-20230726121419-3b25d923346b ) @@ -58,6 +60,7 @@ require ( github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/fatih/color v1.13.0 // indirect @@ -80,6 +83,7 @@ require ( github.com/google/cel-go v0.21.0 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-jsonnet v0.20.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect @@ -108,6 +112,7 @@ require ( github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jetstack/cert-manager v1.7.3 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -138,8 +143,9 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.0.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/onsi/gomega v1.33.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pierrec/lz4 v2.0.5+incompatible // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/posener/complete v1.2.3 // indirect github.com/prometheus/client_golang v1.18.0 // indirect @@ -157,6 +163,10 @@ require ( github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/streamnative/function-mesh/api v0.0.0-20240802074023-ee53ec49a51d // indirect + github.com/streamnative/sn-operator/api v0.0.0-00010101000000-000000000000 // indirect + github.com/streamnative/sn-operator/api/commons v0.0.0-00010101000000-000000000000 // indirect + github.com/streamnative/sn-operator/pkg/commons v0.6.4 // indirect github.com/stripe/stripe-go/v74 v74.5.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect @@ -189,6 +199,7 @@ require ( golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect @@ -197,13 +208,17 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.30.0 // indirect + istio.io/api v1.24.2-0.20241206152109-43afb8563706 // indirect + istio.io/client-go v1.24.2 // indirect + k8s.io/api v0.30.9 // indirect + k8s.io/apiextensions-apiserver v0.30.1 // indirect k8s.io/apiserver v0.29.12 // indirect - k8s.io/component-base v0.29.12 // indirect + k8s.io/component-base v0.30.9 // indirect k8s.io/klog v1.0.0 // indirect k8s.io/klog/v2 v2.120.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 // indirect + sigs.k8s.io/controller-runtime v0.18.6 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect @@ -265,9 +280,9 @@ replace sigs.k8s.io/external-dns => sigs.k8s.io/external-dns v0.11.1 // Pulsar Operator replace ( github.com/operator-framework/api => github.com/operator-framework/api v0.14.0 - github.com/streamnative/sn-operator/api => github.com/streamnative/sn-operator/api v0.8.0-rc.27 - github.com/streamnative/sn-operator/api/commons => github.com/streamnative/sn-operator/api/commons v0.8.0-rc.27 - github.com/streamnative/sn-operator/pkg/commons => github.com/streamnative/sn-operator/pkg/commons v0.8.0-rc.27 + github.com/streamnative/sn-operator/api => github.com/streamnative/sn-operator/api v0.9.3 + github.com/streamnative/sn-operator/api/commons => github.com/streamnative/sn-operator/api/commons v0.9.3 + github.com/streamnative/sn-operator/pkg/commons => github.com/streamnative/sn-operator/pkg/commons v0.9.3 ) replace sigs.k8s.io/apiserver-builder-alpha => github.com/streamnative/apiserver-builder-alpha v0.0.0-20240326220620-ce0d72b3e222 // v1.23.sn diff --git a/go.sum b/go.sum index 6715369..9c0b7f4 100644 --- a/go.sum +++ b/go.sum @@ -607,6 +607,7 @@ github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo8 github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/AthenZ/athenz v1.10.39 h1:mtwHTF/v62ewY2Z5KWhuZgVXftBej1/Tn80zx4DcawY= github.com/AthenZ/athenz v1.10.39/go.mod h1:3Tg8HLsiQZp81BJY58JBeU2BR6B/H4/0MQGfCwhHNEA= +github.com/Azure/azure-sdk-for-go v56.2.0+incompatible h1:2GrG1JkTSMqLquy1pqVsjeRJhNtZLjss2+rx8ogZXx4= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 h1:GJHeeA2N7xrG3q30L2UXDyuWRzDM900/65j70wcM4Ww= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= @@ -1207,8 +1208,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= @@ -1288,22 +1289,22 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/streamnative/cloud-api-server v1.25.2-0.20250110052043-df94d8adecc9 h1:vQxe71KM7U7LW2iNeF+3IdKI3kxRa3FYtXxIXJv6468= -github.com/streamnative/cloud-api-server v1.25.2-0.20250110052043-df94d8adecc9/go.mod h1:sui1Yai7xZUzq0eqKfw/VnRI5b1F+tql9IRpXUrNONA= +github.com/streamnative/cloud-api-server v1.33.3-0.20250304171918-06119c9f160b h1:YqbekYUJ7iYGJwFC+HXbZ36M2o/wRAfg7uZUsx+qErw= +github.com/streamnative/cloud-api-server v1.33.3-0.20250304171918-06119c9f160b/go.mod h1:laTekg7igB5v51b65iyW4dl5tmXiGUbck49dJZS731U= github.com/streamnative/cloud-cli v0.22.0-rc.1 h1:I1CHsQf4joFNnDvNwwW60W9c2VcJnRuNPBMxTdEf6N8= github.com/streamnative/cloud-cli v0.22.0-rc.1/go.mod h1:LocRogsIAggk1yso5WX0ucYuaYt8GwXvrOzWjHzdNN8= github.com/streamnative/function-mesh/api v0.0.0-20240802074023-ee53ec49a51d h1:s0BpMQcsvRBwvlOEkTB8gavWvMjLYtdjHt3+8KzmvtQ= github.com/streamnative/function-mesh/api v0.0.0-20240802074023-ee53ec49a51d/go.mod h1:/X8sD37+1UVShYNyXu662s09J6lQb0AR3QEruNFZpwM= github.com/streamnative/kube-instrumentation v0.3.2 h1:B1jQ2IdJlPtBXXdOfZaTqMB0dwi/lqXdKaZIanoHJ6U= github.com/streamnative/kube-instrumentation v0.3.2/go.mod h1:DmbIJSDoHzzQK65IYOD83J/jKBm5c6uOySd9rd/w/j8= -github.com/streamnative/sn-operator/api v0.8.0-rc.27 h1:5YavFDd2QGEKTx7z6n0qExbd91u2v2A8Ce8/mcpnqWI= -github.com/streamnative/sn-operator/api v0.8.0-rc.27/go.mod h1:Dk0zyHDvpkfminFsQfP+n1lj3olVKDJ6fDIjrIpZbEo= -github.com/streamnative/sn-operator/api/commons v0.8.0-rc.27 h1:Y5dTRUGLc+MRZ7HXLkbJTYHF6wDWb7qfwKAY1+7a1I0= -github.com/streamnative/sn-operator/api/commons v0.8.0-rc.27/go.mod h1:patZoVYX263Go+DlEMdB4woRvd7bKGHPmvZfC/Y8MUU= -github.com/streamnative/sn-operator/pkg/commons v0.8.0-rc.27 h1:8XiiEqXgNmzEtT+lEmgIDGttuVK5gApcisMEktOSZGI= -github.com/streamnative/sn-operator/pkg/commons v0.8.0-rc.27/go.mod h1:sBcStB1wSG2j/880Mz/YhRI2a0NoMS0Q09g8Wldb9I0= -github.com/streamnative/unified-rbac/sdk/sdk-go v0.4.1 h1:uTL7J00PLwfLpdQv7F3fC4nZYwNkpSS2b/BLSPUbeR0= -github.com/streamnative/unified-rbac/sdk/sdk-go v0.4.1/go.mod h1:5oitLmfHTyYpiRF811T2nZszCv3k4OnD/ExROFzNno0= +github.com/streamnative/sn-operator/api v0.9.3 h1:7pwjsR3PPatOyoCR3QnZmC7ij17lv5fjLSvebK+zonU= +github.com/streamnative/sn-operator/api v0.9.3/go.mod h1:9V/2cRY832YZuwxf8irXbId9Mr2UXnMd2ViMVa854wk= +github.com/streamnative/sn-operator/api/commons v0.9.3 h1:oo2vmCeMj5zxnXHE5uNJSdRvrmQGMQ0R4LxLfewftUY= +github.com/streamnative/sn-operator/api/commons v0.9.3/go.mod h1:5JHnWtHVsr2tdX0NBpaZ40dQwK8N0et+pblEO5NZMbE= +github.com/streamnative/sn-operator/pkg/commons v0.9.3 h1:lDN/gKe5PPY4tF6HvlCYKNycQURIuQWuYDkV1BWw05s= +github.com/streamnative/sn-operator/pkg/commons v0.9.3/go.mod h1:z+uc+EAyw+C47m/SXWVMVg5erRtlT01th8xoQXuESN0= +github.com/streamnative/unified-rbac/sdk/sdk-go v0.5.0 h1:YyNvZ2FrDpPsMe3JAPbBL4ElgsYu/4e6w2cC6Uz4ETg= +github.com/streamnative/unified-rbac/sdk/sdk-go v0.5.0/go.mod h1:5oitLmfHTyYpiRF811T2nZszCv3k4OnD/ExROFzNno0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= From 23750e1ae3eec2fd63840244b4382f269247c27a Mon Sep 17 00:00:00 2001 From: Guangning E Date: Wed, 26 Mar 2025 16:28:08 +0800 Subject: [PATCH 2/6] Fixed provider --- cloud/provider.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cloud/provider.go b/cloud/provider.go index 64d24bf..462380b 100644 --- a/cloud/provider.go +++ b/cloud/provider.go @@ -182,6 +182,12 @@ func init() { "rolebinding_condition_resource_names_topic_domain": "The conditional role binding resource name - topic domain(persistent/non-persistent)", "rolebinding_condition_resource_names_topic_name": "The conditional role binding resource name - topic name", "rolebinding_condition_resource_names_subscription": "The conditional role binding resource name - subscription", + "valume_name": "The name of the volume", + "bucket": "The bucket name", + "path": "The path of the bucket", + "bucket_region": "The region of the bucket", + "role_arn": "The role arn of the bucket, it is used to access the bucket", + "volume_ready": "Volume is ready, it will be set to 'True' after the volume is ready", } } @@ -217,6 +223,7 @@ func Provider() *schema.Provider { "streamnative_apikey": resourceApiKey(), "streamnative_pulsar_gateway": resourcePulsarGateway(), "streamnative_rolebinding": resourceRoleBinding(), + "streamnative_volume": resourceVolume(), }, DataSourcesMap: map[string]*schema.Resource{ "streamnative_service_account": dataSourceServiceAccount(), @@ -231,6 +238,7 @@ func Provider() *schema.Provider { "streamnative_resources": dataSourceResources(), "streamnative_pulsar_gateway": dataSourcePulsarGateway(), "streamnative_rolebinding": dataSourceRoleBinding(), + "streamnative_volume": dataSourceVolume(), }, } provider.ConfigureContextFunc = func(_ context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { From c0ff24353d27a5f874e1bc68471ffb51366254f6 Mon Sep 17 00:00:00 2001 From: Guangning E Date: Wed, 26 Mar 2025 16:40:47 +0800 Subject: [PATCH 3/6] Update volume ref --- cloud/provider.go | 2 +- cloud/resource_pulsar_cluster.go | 11 +++++++++++ docs/data-sources/volume.md | 2 +- docs/resources/pulsar_cluster.md | 1 + docs/resources/volume.md | 2 +- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/cloud/provider.go b/cloud/provider.go index 462380b..91efe3e 100644 --- a/cloud/provider.go +++ b/cloud/provider.go @@ -182,7 +182,7 @@ func init() { "rolebinding_condition_resource_names_topic_domain": "The conditional role binding resource name - topic domain(persistent/non-persistent)", "rolebinding_condition_resource_names_topic_name": "The conditional role binding resource name - topic name", "rolebinding_condition_resource_names_subscription": "The conditional role binding resource name - subscription", - "valume_name": "The name of the volume", + "volume_name": "The name of the volume", "bucket": "The bucket name", "path": "The path of the bucket", "bucket_region": "The region of the bucket", diff --git a/cloud/resource_pulsar_cluster.go b/cloud/resource_pulsar_cluster.go index c2a7b14..1c6e13f 100644 --- a/cloud/resource_pulsar_cluster.go +++ b/cloud/resource_pulsar_cluster.go @@ -171,6 +171,11 @@ func resourcePulsarCluster() *schema.Resource { return d.Get("type") == string(cloudv1alpha1.PulsarInstanceTypeServerless) }, }, + "volume": { + Type: schema.TypeString, + Optional: true, + Description: descriptions["volume_name"], + }, "config": { Type: schema.TypeList, Optional: true, @@ -462,6 +467,12 @@ func resourcePulsarClusterCreate(ctx context.Context, d *schema.ResourceData, me } else { pulsarCluster.Annotations[UrsaEngineAnnotation] = UrsaEngineValue } + volumeName := d.Get("volume").(string) + if volumeName != "" { + pulsarCluster.Spec.Volume = &cloudv1alpha1.VolumeReference{ + Name: volumeName, + } + } } if !ursaEnabled && !pulsarInstance.IsServerless() { pulsarCluster.Spec.BookKeeper = bookkeeper diff --git a/docs/data-sources/volume.md b/docs/data-sources/volume.md index 631fa30..8be1443 100644 --- a/docs/data-sources/volume.md +++ b/docs/data-sources/volume.md @@ -17,7 +17,7 @@ description: |- ### Required -- `name` (String) +- `name` (String) The name of the volume - `organization` (String) The organization name ### Read-Only diff --git a/docs/resources/pulsar_cluster.md b/docs/resources/pulsar_cluster.md index 8798e07..b4e273d 100644 --- a/docs/resources/pulsar_cluster.md +++ b/docs/resources/pulsar_cluster.md @@ -35,6 +35,7 @@ description: |- - `release_channel` (String) The release channel of the pulsar cluster subscribe to, it must to be lts or rapid, default rapid - `storage_unit` (Number, Deprecated) storage unit per bookie, 1 storage unit is 2 cpu and 8gb memory - `storage_unit_per_bookie` (Number) storage unit per bookie, 1 storage unit is 2 cpu and 8gb memory +- `volume` (String) The name of the volume ### Read-Only diff --git a/docs/resources/volume.md b/docs/resources/volume.md index 8d07fd2..a3bbae9 100644 --- a/docs/resources/volume.md +++ b/docs/resources/volume.md @@ -18,7 +18,7 @@ description: |- ### Required - `bucket` (String) The bucket name -- `name` (String) +- `name` (String) The name of the volume - `organization` (String) The organization name - `path` (String) The path of the bucket - `region` (String) The region of the bucket From 03944a4d04b5f4ced2c40e766e3d75164d21afca Mon Sep 17 00:00:00 2001 From: Guangning E Date: Wed, 26 Mar 2025 16:41:55 +0800 Subject: [PATCH 4/6] Add volume check --- cloud/resource_pulsar_cluster.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cloud/resource_pulsar_cluster.go b/cloud/resource_pulsar_cluster.go index 1c6e13f..57d9bc8 100644 --- a/cloud/resource_pulsar_cluster.go +++ b/cloud/resource_pulsar_cluster.go @@ -469,6 +469,10 @@ func resourcePulsarClusterCreate(ctx context.Context, d *schema.ResourceData, me } volumeName := d.Get("volume").(string) if volumeName != "" { + _, err := clientSet.CloudV1alpha1().Volumes(namespace).Get(ctx, volumeName, metav1.GetOptions{}) + if err != nil { + return diag.FromErr(fmt.Errorf("ERROR_GET_VOLUME_ON_CREATE_PULSAR_CLUSTER: %w", err)) + } pulsarCluster.Spec.Volume = &cloudv1alpha1.VolumeReference{ Name: volumeName, } From 674af796ab25055a2a287673fdb7608a5bc70af9 Mon Sep 17 00:00:00 2001 From: Guangning E Date: Wed, 26 Mar 2025 16:53:33 +0800 Subject: [PATCH 5/6] Fixed volume --- cloud/resource_volume.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cloud/resource_volume.go b/cloud/resource_volume.go index d53f42c..93a9537 100644 --- a/cloud/resource_volume.go +++ b/cloud/resource_volume.go @@ -124,8 +124,7 @@ func resourceVolumeCreate(ctx context.Context, d *schema.ResourceData, meta inte } ready := d.Get("ready").(string) if ready == "False" { - return retry.RetryableError(fmt.Errorf( - "CONTINUE_WAITING_VOLUME_READY: %s", "volume is not ready yet")) + return retry.RetryableError(fmt.Errorf("CONTINUE_WAITING_VOLUME_READY: volume is not ready yet")) } return nil }) @@ -235,12 +234,12 @@ func resourceVolumeUpdate(ctx context.Context, d *schema.ResourceData, meta inte err = retry.RetryContext(ctx, 10*time.Minute, func() *retry.RetryError { dia := resourceVolumeRead(ctx, d, meta) if dia.HasError() { - return retry.RetryableError(fmt.Errorf("ERROR_READ_VOLUME: %w", dia[0].Summary)) + return retry.RetryableError(fmt.Errorf("ERROR_READ_VOLUME")) } ready := d.Get("ready").(string) if ready == "False" { return retry.RetryableError(fmt.Errorf( - "CONTINUE_WAITING_VOLUME_READY: %s", "volume is not ready yet")) + "CONTINUE_WAITING_VOLUME_READY: volume is not ready yet")) } return nil }) From d03a1226f2286d20b1490020c048a7025bdc3003 Mon Sep 17 00:00:00 2001 From: Guangning E Date: Wed, 26 Mar 2025 17:44:50 +0800 Subject: [PATCH 6/6] Fixed fmt --- cloud/resource_volume.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloud/resource_volume.go b/cloud/resource_volume.go index 93a9537..56e9bdb 100644 --- a/cloud/resource_volume.go +++ b/cloud/resource_volume.go @@ -120,7 +120,7 @@ func resourceVolumeCreate(ctx context.Context, d *schema.ResourceData, meta inte err = retry.RetryContext(ctx, 10*time.Minute, func() *retry.RetryError { dia := resourceVolumeRead(ctx, d, meta) if dia.HasError() { - return retry.RetryableError(fmt.Errorf("ERROR_READ_VOLUME: %w", dia[0].Summary)) + return retry.RetryableError(fmt.Errorf("ERROR_READ_VOLUME: %s", dia[0].Summary)) } ready := d.Get("ready").(string) if ready == "False" {