From d6d50dd92e935827639ca999cd37f9e5f35ef86e Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Tue, 23 Jul 2024 23:50:24 -0400 Subject: [PATCH 1/8] add support for DeletionProtection to Index models and operations --- pinecone/client.go | 104 ++++++++++++++++++++++++++++------------ pinecone/client_test.go | 65 ++++++++++++++----------- pinecone/models.go | 18 ++++--- 3 files changed, 120 insertions(+), 67 deletions(-) diff --git a/pinecone/client.go b/pinecone/client.go index 8ca493a..25e1bbe 100644 --- a/pinecone/client.go +++ b/pinecone/client.go @@ -429,15 +429,16 @@ func (c *Client) ListIndexes(ctx context.Context) ([]*Index, error) { // // [type of pods]: https://docs.pinecone.io/guides/indexes/choose-a-pod-type-and-size type CreatePodIndexRequest struct { - Name string - Dimension int32 - Metric IndexMetric - Environment string - PodType string - Shards int32 - Replicas int32 - SourceCollection *string - MetadataConfig *PodSpecMetadataConfig + Name string + Dimension int32 + Metric IndexMetric + DeletionProtection string + Environment string + PodType string + Shards int32 + Replicas int32 + SourceCollection *string + MetadataConfig *PodSpecMetadataConfig } // ReplicaCount ensures the replica count of a pods-based Index is >1. @@ -501,11 +502,14 @@ func (req CreatePodIndexRequest) TotalCount() int { // fmt.Printf("Successfully created pod index: %s", idx.Name) // } func (c *Client) CreatePodIndex(ctx context.Context, in *CreatePodIndexRequest) (*Index, error) { + deletionProtection := toDeletionProtection(in.DeletionProtection) metric := control.CreateIndexRequestMetric(in.Metric) + req := control.CreateIndexRequest{ - Name: in.Name, - Dimension: in.Dimension, - Metric: &metric, + Name: in.Name, + Dimension: in.Dimension, + Metric: &metric, + DeletionProtection: &deletionProtection, } req.Spec = control.IndexSpec{ @@ -590,11 +594,12 @@ func (c *Client) CreatePodIndex(ctx context.Context, in *CreatePodIndexRequest) // [region]: https://docs.pinecone.io/troubleshooting/available-cloud-regions // [cloud provider]: https://docs.pinecone.io/troubleshooting/available-cloud-regions#regions-available-for-serverless-indexes type CreateServerlessIndexRequest struct { - Name string - Dimension int32 - Metric IndexMetric - Cloud Cloud - Region string + Name string + Dimension int32 + Metric IndexMetric + DeletionProtection string + Cloud Cloud + Region string } // CreateServerlessIndex creates and initializes a new serverless Index via the specified Client. @@ -636,11 +641,18 @@ type CreateServerlessIndexRequest struct { // fmt.Printf("Successfully created serverless index: %s", idx.Name) // } func (c *Client) CreateServerlessIndex(ctx context.Context, in *CreateServerlessIndexRequest) (*Index, error) { + deletionProtection := toDeletionProtection(in.DeletionProtection) + + fmt.Printf("in.Metric: %v\n", in.Metric) + fmt.Printf("control.CreateIndexRequestMetric(in.Metric): %v\n", control.CreateIndexRequestMetric(in.Metric)) + metric := control.CreateIndexRequestMetric(in.Metric) + req := control.CreateIndexRequest{ - Name: in.Name, - Dimension: in.Dimension, - Metric: &metric, + Name: in.Name, + Dimension: in.Dimension, + Metric: &metric, + DeletionProtection: &deletionProtection, Spec: control.IndexSpec{ Serverless: &control.ServerlessSpec{ Cloud: control.ServerlessSpecCloud(in.Cloud), @@ -754,6 +766,12 @@ func (c *Client) DeleteIndex(ctx context.Context, idxName string) error { return nil } +type ConfigureIndexParams struct { + PodType string + Replicas int32 + DeletionProtection control.DeletionProtection +} + // ConfigureIndex is used to [scale a pods-based index] up or down by changing the size of the pods or the number of // replicas. // @@ -794,12 +812,15 @@ func (c *Client) DeleteIndex(ctx context.Context, idxName string) error { // // [scale a pods-based index]: https://docs.pinecone.io/guides/indexes/configure-pod-based-indexes // [app.pinecone.io]: https://app.pinecone.io -func (c *Client) ConfigureIndex(ctx context.Context, name string, podType *string, - replicas *int32) (*Index, error) { +func (c *Client) ConfigureIndex(ctx context.Context, name string, configParams ConfigureIndexParams) (*Index, error) { - if podType == nil && replicas == nil { - return nil, fmt.Errorf("must specify either podType or replicas") - } + // if configParams.PodType == "" && configParams.Replicas == 0 && configParams.DeletionProtection == "" { + // return nil, fmt.Errorf("must specify either PodType, Replicas, or DeletionProtection") + // } + + podType := pointerOrNil(configParams.PodType) + replicas := pointerOrNil(configParams.Replicas) + deletionProtection := pointerOrNil(configParams.DeletionProtection) request := control.ConfigureIndexRequest{ Spec: &struct { @@ -816,6 +837,7 @@ func (c *Client) ConfigureIndex(ctx context.Context, name string, podType *strin Replicas: replicas, }, }, + DeletionProtection: deletionProtection, } res, err := c.restClient.ConfigureIndex(ctx, name, request) @@ -1153,13 +1175,16 @@ func toIndex(idx *control.IndexModel) *Index { Ready: idx.Status.Ready, State: IndexStatusState(idx.Status.State), } + deletionProtecetion := toDeletionProtection(string(derefOrDefault(idx.DeletionProtection, control.Disabled))) + return &Index{ - Name: idx.Name, - Dimension: idx.Dimension, - Host: idx.Host, - Metric: IndexMetric(idx.Metric), - Spec: spec, - Status: status, + Name: idx.Name, + Dimension: idx.Dimension, + Host: idx.Host, + Metric: IndexMetric(idx.Metric), + DeletionProtection: deletionProtecetion, + Spec: spec, + Status: status, } } @@ -1303,6 +1328,15 @@ func buildClientBaseOptions(in NewClientBaseParams) []control.ClientOption { return clientOptions } +func toDeletionProtection(value string) control.DeletionProtection { + switch control.DeletionProtection(value) { + case control.Enabled, control.Disabled: + return control.DeletionProtection(value) + default: + return control.Disabled + } +} + func ensureURLScheme(inputURL string) (string, error) { parsedURL, err := url.Parse(inputURL) if err != nil { @@ -1324,6 +1358,14 @@ func valueOrFallback[T comparable](value, fallback T) T { } } +func pointerOrNil[T comparable](value T) *T { + var zero T // set to zero-value of generic type T + if value == zero { + return nil + } + return &value +} + func derefOrDefault[T any](ptr *T, defaultValue T) T { if ptr == nil { return defaultValue diff --git a/pinecone/client_test.go b/pinecone/client_test.go index 594c075..78855a8 100644 --- a/pinecone/client_test.go +++ b/pinecone/client_test.go @@ -466,9 +466,7 @@ func (ts *IntegrationTests) TestConfigureIndexIllegalScaleDown() { log.Fatalf("Error creating index %s: %v", name, err) } - pods := "p1.x1" // test index originally created with "p1.x2" pods - replicas := int32(1) // could be nil, but do not want to test nil case here - _, err = ts.client.ConfigureIndex(context.Background(), name, &pods, &replicas) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{PodType: "p1.x1", Replicas: 1}) require.ErrorContainsf(ts.T(), err, "Cannot scale down", err.Error()) } @@ -491,8 +489,7 @@ func (ts *IntegrationTests) TestConfigureIndexScaleUpNoPods() { log.Fatalf("Error creating index %s: %v", name, err) } - replicas := int32(2) - _, err = ts.client.ConfigureIndex(context.Background(), name, nil, &replicas) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{Replicas: 2}) require.NoError(ts.T(), err) } @@ -515,12 +512,11 @@ func (ts *IntegrationTests) TestConfigureIndexScaleUpNoReplicas() { log.Fatalf("Error creating index %s: %v", name, err) } - pods := "p1.x4" - _, err = ts.client.ConfigureIndex(context.Background(), name, &pods, nil) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{PodType: "p1.x4"}) require.NoError(ts.T(), err) } -func (ts *IntegrationTests) TestConfigureIndexIllegalNoPodsOrReplicas() { +func (ts *IntegrationTests) TestConfigureIndexIllegalNoPodsOrReplicasOrDeletionProtection() { name := uuid.New().String() defer func(ts *IntegrationTests, name string) { @@ -539,8 +535,8 @@ func (ts *IntegrationTests) TestConfigureIndexIllegalNoPodsOrReplicas() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, nil, nil) - require.ErrorContainsf(ts.T(), err, "must specify either podType or replicas", err.Error()) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{}) + require.ErrorContainsf(ts.T(), err, "must specify either PodType, Replicas, or DeletionProtection", err.Error()) } func (ts *IntegrationTests) TestConfigureIndexHitPodLimit() { @@ -562,8 +558,7 @@ func (ts *IntegrationTests) TestConfigureIndexHitPodLimit() { log.Fatalf("Error creating index %s: %v", name, err) } - replicas := int32(30000) - _, err = ts.client.ConfigureIndex(context.Background(), name, nil, &replicas) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{Replicas: 30000}) require.ErrorContainsf(ts.T(), err, "You've reached the max pods allowed", err.Error()) } @@ -837,6 +832,9 @@ func TestEnsureURLSchemeUnit(t *testing.T) { } func TestToIndexUnit(t *testing.T) { + deletionProtectionEnabled := control.Enabled + deletionProtectionDisabled := control.Disabled + tests := []struct { name string originalInput *control.IndexModel @@ -850,10 +848,11 @@ func TestToIndexUnit(t *testing.T) { { name: "pod index input", originalInput: &control.IndexModel{ - Name: "testIndex", - Dimension: 128, - Host: "test-host", - Metric: "cosine", + Name: "testIndex", + Dimension: 128, + Host: "test-host", + Metric: "cosine", + DeletionProtection: &deletionProtectionDisabled, Spec: struct { Pod *control.PodSpec `json:"pod,omitempty"` Serverless *control.ServerlessSpec `json:"serverless,omitempty"` @@ -878,10 +877,11 @@ func TestToIndexUnit(t *testing.T) { }, }, expectedOutput: &Index{ - Name: "testIndex", - Dimension: 128, - Host: "test-host", - Metric: "cosine", + Name: "testIndex", + Dimension: 128, + Host: "test-host", + Metric: "cosine", + DeletionProtection: "disabled", Spec: &IndexSpec{ Pod: &PodSpec{ Environment: "test-environ", @@ -901,10 +901,11 @@ func TestToIndexUnit(t *testing.T) { { name: "serverless index input", originalInput: &control.IndexModel{ - Name: "testIndex", - Dimension: 128, - Host: "test-host", - Metric: "cosine", + Name: "testIndex", + Dimension: 128, + Host: "test-host", + Metric: "cosine", + DeletionProtection: &deletionProtectionEnabled, Spec: struct { Pod *control.PodSpec `json:"pod,omitempty"` Serverless *control.ServerlessSpec `json:"serverless,omitempty"` @@ -924,10 +925,11 @@ func TestToIndexUnit(t *testing.T) { }, }, expectedOutput: &Index{ - Name: "testIndex", - Dimension: 128, - Host: "test-host", - Metric: "cosine", + Name: "testIndex", + Dimension: 128, + Host: "test-host", + Metric: "cosine", + DeletionProtection: "enabled", Spec: &IndexSpec{ Serverless: &ServerlessSpec{ Cloud: Cloud("test-environ"), @@ -1222,7 +1224,12 @@ func TestBuildClientBaseOptionsUnit(t *testing.T) { } } -// Helper functions: +// Helper functions + +func stringPtr(s string) *string { + return &s +} + func isValidUUID(u string) bool { _, err := uuid.Parse(u) return err == nil diff --git a/pinecone/models.go b/pinecone/models.go index df4570b..8662376 100644 --- a/pinecone/models.go +++ b/pinecone/models.go @@ -1,6 +1,9 @@ package pinecone -import "google.golang.org/protobuf/types/known/structpb" +import ( + "github.com/pinecone-io/go-pinecone/internal/gen/control" + "google.golang.org/protobuf/types/known/structpb" +) // IndexMetric is the [distance metric] to be used by similarity search against a Pinecone Index. // @@ -52,12 +55,13 @@ type IndexSpec struct { // Index is a Pinecone Index object. Can be either a pod-based or a serverless Index, depending on the IndexSpec. type Index struct { - Name string `json:"name"` - Dimension int32 `json:"dimension"` - Host string `json:"host"` - Metric IndexMetric `json:"metric"` - Spec *IndexSpec `json:"spec,omitempty"` - Status *IndexStatus `json:"status,omitempty"` + Name string `json:"name"` + Dimension int32 `json:"dimension"` + Host string `json:"host"` + Metric IndexMetric `json:"metric"` + DeletionProtection control.DeletionProtection `json:"deletion_protection,omitempty"` + Spec *IndexSpec `json:"spec,omitempty"` + Status *IndexStatus `json:"status,omitempty"` } // Collection is a Pinecone [Collection object]. Only available for pod-based Indexes. From 16b1e60a5b4bb3ec5a0ef45f0d920e1f3ce5c9c7 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Wed, 24 Jul 2024 00:34:48 -0400 Subject: [PATCH 2/8] remove print statements, add check for empty ConfigureIndexParams values --- pinecone/client.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/pinecone/client.go b/pinecone/client.go index 25e1bbe..ae65224 100644 --- a/pinecone/client.go +++ b/pinecone/client.go @@ -642,10 +642,6 @@ type CreateServerlessIndexRequest struct { // } func (c *Client) CreateServerlessIndex(ctx context.Context, in *CreateServerlessIndexRequest) (*Index, error) { deletionProtection := toDeletionProtection(in.DeletionProtection) - - fmt.Printf("in.Metric: %v\n", in.Metric) - fmt.Printf("control.CreateIndexRequestMetric(in.Metric): %v\n", control.CreateIndexRequestMetric(in.Metric)) - metric := control.CreateIndexRequestMetric(in.Metric) req := control.CreateIndexRequest{ @@ -814,9 +810,9 @@ type ConfigureIndexParams struct { // [app.pinecone.io]: https://app.pinecone.io func (c *Client) ConfigureIndex(ctx context.Context, name string, configParams ConfigureIndexParams) (*Index, error) { - // if configParams.PodType == "" && configParams.Replicas == 0 && configParams.DeletionProtection == "" { - // return nil, fmt.Errorf("must specify either PodType, Replicas, or DeletionProtection") - // } + if configParams.PodType == "" && configParams.Replicas == 0 && configParams.DeletionProtection == "" { + return nil, fmt.Errorf("must specify either PodType, Replicas, or DeletionProtection") + } podType := pointerOrNil(configParams.PodType) replicas := pointerOrNil(configParams.Replicas) From fc13c64c16867c661ea6826b1aa9b856c0f24068 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Wed, 24 Jul 2024 13:57:48 -0400 Subject: [PATCH 3/8] clean up test file --- pinecone/client_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pinecone/client_test.go b/pinecone/client_test.go index 78855a8..cbeba79 100644 --- a/pinecone/client_test.go +++ b/pinecone/client_test.go @@ -1224,12 +1224,7 @@ func TestBuildClientBaseOptionsUnit(t *testing.T) { } } -// Helper functions - -func stringPtr(s string) *string { - return &s -} - +// Helper functions: func isValidUUID(u string) bool { _, err := uuid.Parse(u) return err == nil From 4af5ef66ba6c317666b8121a4eeeaa2879cbf0f1 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Wed, 24 Jul 2024 14:09:21 -0400 Subject: [PATCH 4/8] print collection errors in CI --- pinecone/client_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pinecone/client_test.go b/pinecone/client_test.go index cbeba79..82ff0e4 100644 --- a/pinecone/client_test.go +++ b/pinecone/client_test.go @@ -428,6 +428,8 @@ func (ts *IntegrationTests) TestCreateCollection() { Name: name, Source: sourceIndex, }) + fmt.Printf("TestCreateCollection err: %+v\n", err) + require.NoError(ts.T(), err) require.Equal(ts.T(), name, collection.Name, "Collection name does not match") } @@ -441,6 +443,8 @@ func (ts *IntegrationTests) TestDeleteCollection() { Name: collectionName, Source: ts.idxName, }) + fmt.Printf("TestDeleteCollection err: %+v\n", err) + require.NoError(ts.T(), err) err = ts.client.DeleteCollection(context.Background(), collectionName) From 803aecd4b476851993d6f85597b5a07ffbda1b22 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Wed, 24 Jul 2024 17:31:17 -0400 Subject: [PATCH 5/8] update the other one this time --- pinecone/client_test.go | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pinecone/client_test.go b/pinecone/client_test.go index 82ff0e4..eac3d07 100644 --- a/pinecone/client_test.go +++ b/pinecone/client_test.go @@ -24,7 +24,50 @@ import ( ) // Integration tests: +<<<<<<< HEAD func (ts *IntegrationTests) TestNewClientParamsSet() { +======= +type ClientTestsIntegration struct { + suite.Suite + client Client + clientSourceTag Client + sourceTag string + podIndex string + serverlessIndex string +} + +func TestIntegrationClient(t *testing.T) { + suite.Run(t, new(ClientTestsIntegration)) +} + +func (ts *ClientTestsIntegration) SetupSuite() { + apiKey := os.Getenv("PINECONE_API_KEY") + require.NotEmpty(ts.T(), apiKey, "PINECONE_API_KEY env variable not set") + + ts.podIndex = os.Getenv("STATIC_TEST_PODS_INDEX_NAME") + require.NotEmpty(ts.T(), ts.podIndex, "STATIC_TEST_PODS_INDEX_NAME env variable not set") + + ts.serverlessIndex = os.Getenv("STATIC_TEST_SERVERLESS_INDEX_NAME") + require.NotEmpty(ts.T(), ts.serverlessIndex, "TEST_SERVERLESS_INDEX_NAME env variable not set") + + client, err := NewClient(NewClientParams{}) + require.NoError(ts.T(), err) + + ts.client = *client + + ts.sourceTag = "test_source_tag" + clientSourceTag, err := NewClient(NewClientParams{ApiKey: apiKey, SourceTag: ts.sourceTag}) + require.NoError(ts.T(), err) + ts.clientSourceTag = *clientSourceTag + + // this will clean up the project deleting all indexes and collections that are + // named a UUID. Generally not needed as all tests are cleaning up after themselves + // Left here as a convenience during active development. + //deleteUUIDNamedResources(context.Background(), &ts.client) +} + +func (ts *ClientTestsIntegration) TestNewClientParamsSet() { +>>>>>>> 2e391eb (update the other one this time) apiKey := "test-api-key" client, err := NewClient(NewClientParams{ApiKey: apiKey}) From d1658bcf7746b11c7f68a8a0d2647d6b8eecd1b2 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Thu, 25 Jul 2024 14:50:00 -0400 Subject: [PATCH 6/8] add custom DeletionProtection type and use that in index operations, update doc comments for updated functions and types --- pinecone/client.go | 133 ++++++++++++++++++++++++---------------- pinecone/client_test.go | 14 ++--- pinecone/models.go | 11 ++++ 3 files changed, 98 insertions(+), 60 deletions(-) diff --git a/pinecone/client.go b/pinecone/client.go index ae65224..7bcbc51 100644 --- a/pinecone/client.go +++ b/pinecone/client.go @@ -432,7 +432,7 @@ type CreatePodIndexRequest struct { Name string Dimension int32 Metric IndexMetric - DeletionProtection string + DeletionProtection DeletionProtection Environment string PodType string Shards int32 @@ -502,14 +502,14 @@ func (req CreatePodIndexRequest) TotalCount() int { // fmt.Printf("Successfully created pod index: %s", idx.Name) // } func (c *Client) CreatePodIndex(ctx context.Context, in *CreatePodIndexRequest) (*Index, error) { - deletionProtection := toDeletionProtection(in.DeletionProtection) - metric := control.CreateIndexRequestMetric(in.Metric) + deletionProtection := pointerOrNil(control.DeletionProtection(in.DeletionProtection)) + metric := pointerOrNil(control.CreateIndexRequestMetric(in.Metric)) req := control.CreateIndexRequest{ Name: in.Name, Dimension: in.Dimension, - Metric: &metric, - DeletionProtection: &deletionProtection, + Metric: metric, + DeletionProtection: deletionProtection, } req.Spec = control.IndexSpec{ @@ -552,6 +552,7 @@ func (c *Client) CreatePodIndex(ctx context.Context, in *CreatePodIndexRequest) // and consist only of lower case alphanumeric characters or '-'. // - Dimension: The [dimensionality] of the vectors to be inserted in the Index. // - Metric: The metric used to measure the [similarity] between vectors ('euclidean', 'cosine', or 'dotproduct'). +// - DeletionProtection: // - Cloud: The public [cloud provider] where you would like your Index hosted. // For serverless Indexes, you define only the cloud and region where the Index should be hosted. // - Region: The [region] where you would like your Index to be created. @@ -597,7 +598,7 @@ type CreateServerlessIndexRequest struct { Name string Dimension int32 Metric IndexMetric - DeletionProtection string + DeletionProtection DeletionProtection Cloud Cloud Region string } @@ -641,14 +642,14 @@ type CreateServerlessIndexRequest struct { // fmt.Printf("Successfully created serverless index: %s", idx.Name) // } func (c *Client) CreateServerlessIndex(ctx context.Context, in *CreateServerlessIndexRequest) (*Index, error) { - deletionProtection := toDeletionProtection(in.DeletionProtection) - metric := control.CreateIndexRequestMetric(in.Metric) + deletionProtection := pointerOrNil(control.DeletionProtection(in.DeletionProtection)) + metric := pointerOrNil(control.CreateIndexRequestMetric(in.Metric)) req := control.CreateIndexRequest{ Name: in.Name, Dimension: in.Dimension, - Metric: &metric, - DeletionProtection: &deletionProtection, + Metric: metric, + DeletionProtection: deletionProtection, Spec: control.IndexSpec{ Serverless: &control.ServerlessSpec{ Cloud: control.ServerlessSpecCloud(in.Cloud), @@ -762,6 +763,42 @@ func (c *Client) DeleteIndex(ctx context.Context, idxName string) error { return nil } +// ConfigureIndexParams contains parameters for configuring an index. For both pod-based +// and serverless indexes you can configure the DeletionProtection status for an index. +// For pod-based indexes you can also configure the number of Replicas and the PodType. +// Each of the fields are optional, but at least one field must be set. +// See [scale a pods-based index] for more information. +// +// Fields: +// - PodType: (Optional) The pod size to scale the index to. For a "p1" pod type, +// you could pass "p1.x2" to scale your index to the "x2" size, or you could pass "p1.x4" +// to scale your index to the "x4" size, and so forth. Only applies to pod-based indexes. +// - Replicas: (Optional) The number of replicas to scale the index to. This is capped by +// the maximum number of replicas allowed in your Pinecone project. To configure this number, +// go to [app.pinecone.io], select your project, and configure the maximum number of pods. +// - DeletionProtection: (Optional) // DeletionProtection determines whether [deletion protection] +// is "enabled" or "disabled" for the index. When "enabled", the index cannot be deleted. Defaults to "disabled". +// +// Example: +// +// ctx := context.Background() +// +// clientParams := pinecone.NewClientParams{ +// ApiKey: "YOUR_API_KEY", +// SourceTag: "your_source_identifier", // optional +// } +// +// pc, err := pinecone.NewClient(clientParams) +// if err != nil { +// log.Fatalf("Failed to create Client: %v", err) +// } else { +// fmt.Println("Successfully created a new Client object!") +// } +// +// idx, err := pc.ConfigureIndex(ctx, "my-index", &ConfigureIndexParams{ DeletionProtection: "enabled", Replicas: 4 }) +// +// [app.pinecone.io]: https://app.pinecone.io +// [scale a pods-based index]: https://docs.pinecone.io/guides/indexes/configure-pod-based-indexes type ConfigureIndexParams struct { PodType string Replicas int32 @@ -769,54 +806,55 @@ type ConfigureIndexParams struct { } // ConfigureIndex is used to [scale a pods-based index] up or down by changing the size of the pods or the number of -// replicas. +// replicas, or to enable and disable deletion protection for an index. // // Parameters: +// - ctx: A context.Context object controls the request's lifetime, allowing for the request +// to be canceled or to timeout according to the context's deadline. // - name: The name of the index to configure. -// - pods: (Optional) The pod size to scale the index to (e.g. for a "p1" pod type, -// you could pass "p1.x2" to scale your index to the "x2" size, -// or you could pass "p1.x4" to scale your index to the "x4" size, and -// so forth. -// - replicas: (Optional) The number of replicas to scale the index to. -// This is capped by the maximum number of replicas allowed in your Pinecone project. To configure this number, -// go to [app.pinecone.io], select your project, and configure the maximum number of pods. +// - in: A pointer to a ConfigureIndexParams object that contains the parameters for configuring the index. // // Note: You can only scale an index up, not down. If you want to scale an index down, // you must create a new index with the desired configuration. // // Returns a pointer to a configured Index object or an error. // -// Example for a pods-based index originally configured with 1 "p1" pod of size "x2" and 1 replica: -// -// // To scale the size of your pods from "x2" to "x4": -// _, err := pc.ConfigureIndex(ctx, "my-index", "p1.x4", nil) -// if err != nil { -// fmt.Printf("Failed to configure index: %v\n", err) -// } -// -// // To scale the number of replicas: -// _, err := pc.ConfigureIndex(ctx, "my-index", nil, 4) -// if err != nil { -// fmt.Printf("Failed to configure index: %v\n", err) -// } +// Example: // -// // To scale both the size of your pods and the number of replicas: -// _, err := pc.ConfigureIndex(ctx, "my-index", "p1.x4", 4) -// if err != nil { -// fmt.Printf("Failed to configure index: %v\n", err) -// } +// // To scale the size of your pods from "x2" to "x4": +// _, err := pc.ConfigureIndex(ctx, "my-pod-index", &ConfigureIndexParams{PodType: "p1.x4"}) +// if err != nil { +// fmt.Printf("Failed to configure index: %v\n", err) +// } +// +// // To scale the number of replicas: +// _, err := pc.ConfigureIndex(ctx, "my-pod-index", &ConfigureIndexParams{Replicas: 4}) +// if err != nil { +// fmt.Printf("Failed to configure index: %v\n", err) +// } +// +// // To scale both the size of your pods and the number of replicas: +// _, err := pc.ConfigureIndex(ctx, "my-pod-index", &ConfigureIndexParams{PodType: "p1.x4", Replicas: 4}) +// if err != nil { +// fmt.Printf("Failed to configure index: %v\n", err) +// } +// +// // To enable deletion protection: +// _, err := pc.ConfigureIndex(ctx, "my-index", nil, nil, &ConfigureIndexParams{DeletionProtection: "enabled"}) +// if err != nil { +// fmt.Printf("Failed to configure index: %v\n", err) +// } // // [scale a pods-based index]: https://docs.pinecone.io/guides/indexes/configure-pod-based-indexes -// [app.pinecone.io]: https://app.pinecone.io -func (c *Client) ConfigureIndex(ctx context.Context, name string, configParams ConfigureIndexParams) (*Index, error) { +func (c *Client) ConfigureIndex(ctx context.Context, name string, in *ConfigureIndexParams) (*Index, error) { - if configParams.PodType == "" && configParams.Replicas == 0 && configParams.DeletionProtection == "" { + if in.PodType == "" && in.Replicas == 0 && in.DeletionProtection == "" { return nil, fmt.Errorf("must specify either PodType, Replicas, or DeletionProtection") } - podType := pointerOrNil(configParams.PodType) - replicas := pointerOrNil(configParams.Replicas) - deletionProtection := pointerOrNil(configParams.DeletionProtection) + podType := pointerOrNil(in.PodType) + replicas := pointerOrNil(in.Replicas) + deletionProtection := pointerOrNil(in.DeletionProtection) request := control.ConfigureIndexRequest{ Spec: &struct { @@ -1171,7 +1209,7 @@ func toIndex(idx *control.IndexModel) *Index { Ready: idx.Status.Ready, State: IndexStatusState(idx.Status.State), } - deletionProtecetion := toDeletionProtection(string(derefOrDefault(idx.DeletionProtection, control.Disabled))) + deletionProtecetion := derefOrDefault(idx.DeletionProtection, control.Disabled) return &Index{ Name: idx.Name, @@ -1324,15 +1362,6 @@ func buildClientBaseOptions(in NewClientBaseParams) []control.ClientOption { return clientOptions } -func toDeletionProtection(value string) control.DeletionProtection { - switch control.DeletionProtection(value) { - case control.Enabled, control.Disabled: - return control.DeletionProtection(value) - default: - return control.Disabled - } -} - func ensureURLScheme(inputURL string) (string, error) { parsedURL, err := url.Parse(inputURL) if err != nil { diff --git a/pinecone/client_test.go b/pinecone/client_test.go index eac3d07..32a8624 100644 --- a/pinecone/client_test.go +++ b/pinecone/client_test.go @@ -48,7 +48,7 @@ func (ts *ClientTestsIntegration) SetupSuite() { require.NotEmpty(ts.T(), ts.podIndex, "STATIC_TEST_PODS_INDEX_NAME env variable not set") ts.serverlessIndex = os.Getenv("STATIC_TEST_SERVERLESS_INDEX_NAME") - require.NotEmpty(ts.T(), ts.serverlessIndex, "TEST_SERVERLESS_INDEX_NAME env variable not set") + require.NotEmpty(ts.T(), ts.serverlessIndex, "STATIC_TEST_SERVERLESS_INDEX_NAME env variable not set") client, err := NewClient(NewClientParams{}) require.NoError(ts.T(), err) @@ -471,7 +471,6 @@ func (ts *IntegrationTests) TestCreateCollection() { Name: name, Source: sourceIndex, }) - fmt.Printf("TestCreateCollection err: %+v\n", err) require.NoError(ts.T(), err) require.Equal(ts.T(), name, collection.Name, "Collection name does not match") @@ -486,7 +485,6 @@ func (ts *IntegrationTests) TestDeleteCollection() { Name: collectionName, Source: ts.idxName, }) - fmt.Printf("TestDeleteCollection err: %+v\n", err) require.NoError(ts.T(), err) @@ -513,7 +511,7 @@ func (ts *IntegrationTests) TestConfigureIndexIllegalScaleDown() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{PodType: "p1.x1", Replicas: 1}) + _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{PodType: "p1.x1", Replicas: 1}) require.ErrorContainsf(ts.T(), err, "Cannot scale down", err.Error()) } @@ -536,7 +534,7 @@ func (ts *IntegrationTests) TestConfigureIndexScaleUpNoPods() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{Replicas: 2}) + _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{Replicas: 2}) require.NoError(ts.T(), err) } @@ -559,7 +557,7 @@ func (ts *IntegrationTests) TestConfigureIndexScaleUpNoReplicas() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{PodType: "p1.x4"}) + _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{PodType: "p1.x4"}) require.NoError(ts.T(), err) } @@ -582,7 +580,7 @@ func (ts *IntegrationTests) TestConfigureIndexIllegalNoPodsOrReplicasOrDeletionP log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{}) + _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{}) require.ErrorContainsf(ts.T(), err, "must specify either PodType, Replicas, or DeletionProtection", err.Error()) } @@ -605,7 +603,7 @@ func (ts *IntegrationTests) TestConfigureIndexHitPodLimit() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{Replicas: 30000}) + _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{Replicas: 30000}) require.ErrorContainsf(ts.T(), err, "You've reached the max pods allowed", err.Error()) } diff --git a/pinecone/models.go b/pinecone/models.go index 8662376..146539f 100644 --- a/pinecone/models.go +++ b/pinecone/models.go @@ -30,6 +30,17 @@ const ( Terminating IndexStatusState = "Terminating" ) +// DeletionProtection determines whether [deletion protection] is "enabled" or "disabled" for the index. +// When "enabled", the index cannot be deleted. Defaults to "disabled". +// +// [deletion protection]: http://docs.pinecone.io/guides/indexes/prevent-index-deletion +type DeletionProtection string + +const ( + DeletionProtectionEnabled DeletionProtection = "Enabled" + DeletionProtectionDisabled DeletionProtection = "Disabled" +) + // Cloud is the [cloud provider] to be used for a Pinecone serverless Index. // // [cloud provider]: https://docs.pinecone.io/troubleshooting/available-cloud-regions From 5c9fc6416077df770ec9b830af0d78934b5d63e6 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Thu, 25 Jul 2024 15:46:11 -0400 Subject: [PATCH 7/8] update Index type to use custom DeletionProtection --- pinecone/client.go | 6 +++--- pinecone/models.go | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pinecone/client.go b/pinecone/client.go index 7bcbc51..c5632b0 100644 --- a/pinecone/client.go +++ b/pinecone/client.go @@ -821,7 +821,7 @@ type ConfigureIndexParams struct { // // Example: // -// // To scale the size of your pods from "x2" to "x4": +// // To scale the size of your pods-based index from "x2" to "x4": // _, err := pc.ConfigureIndex(ctx, "my-pod-index", &ConfigureIndexParams{PodType: "p1.x4"}) // if err != nil { // fmt.Printf("Failed to configure index: %v\n", err) @@ -1209,14 +1209,14 @@ func toIndex(idx *control.IndexModel) *Index { Ready: idx.Status.Ready, State: IndexStatusState(idx.Status.State), } - deletionProtecetion := derefOrDefault(idx.DeletionProtection, control.Disabled) + deletionProtection := derefOrDefault(idx.DeletionProtection, "") return &Index{ Name: idx.Name, Dimension: idx.Dimension, Host: idx.Host, Metric: IndexMetric(idx.Metric), - DeletionProtection: deletionProtecetion, + DeletionProtection: DeletionProtection(deletionProtection), Spec: spec, Status: status, } diff --git a/pinecone/models.go b/pinecone/models.go index 146539f..2b03304 100644 --- a/pinecone/models.go +++ b/pinecone/models.go @@ -1,7 +1,6 @@ package pinecone import ( - "github.com/pinecone-io/go-pinecone/internal/gen/control" "google.golang.org/protobuf/types/known/structpb" ) @@ -66,13 +65,13 @@ type IndexSpec struct { // Index is a Pinecone Index object. Can be either a pod-based or a serverless Index, depending on the IndexSpec. type Index struct { - Name string `json:"name"` - Dimension int32 `json:"dimension"` - Host string `json:"host"` - Metric IndexMetric `json:"metric"` - DeletionProtection control.DeletionProtection `json:"deletion_protection,omitempty"` - Spec *IndexSpec `json:"spec,omitempty"` - Status *IndexStatus `json:"status,omitempty"` + Name string `json:"name"` + Dimension int32 `json:"dimension"` + Host string `json:"host"` + Metric IndexMetric `json:"metric"` + DeletionProtection DeletionProtection `json:"deletion_protection,omitempty"` + Spec *IndexSpec `json:"spec,omitempty"` + Status *IndexStatus `json:"status,omitempty"` } // Collection is a Pinecone [Collection object]. Only available for pod-based Indexes. From a1a119b865270535bfc31c13635130d5ff425de3 Mon Sep 17 00:00:00 2001 From: Austin DeNoble Date: Fri, 26 Jul 2024 19:43:47 -0400 Subject: [PATCH 8/8] review feedback, fix doc comments, add two integration tests for deletion protection --- pinecone/client.go | 59 ++++++++++++-------- pinecone/client_test.go | 120 ++++++++++++++++++++++++---------------- pinecone/models.go | 4 +- 3 files changed, 108 insertions(+), 75 deletions(-) diff --git a/pinecone/client.go b/pinecone/client.go index c5632b0..5788378 100644 --- a/pinecone/client.go +++ b/pinecone/client.go @@ -383,6 +383,8 @@ func (c *Client) ListIndexes(ctx context.Context) ([]*Index, error) { // default, all metadata is indexed; when `metadata_config` is present, // only specified metadata fields are indexed. These configurations are // only valid for use with pod-based Indexes. +// - DeletionProtection: (Optional) determines whether [deletion protection] is "enabled" or "disabled" for the index. +// When "enabled", the index cannot be deleted. Defaults to "disabled". // // To create a new pods-based Index, use the CreatePodIndex method on the Client object. // @@ -426,8 +428,8 @@ func (c *Client) ListIndexes(ctx context.Context) ([]*Index, error) { // [metadata configuration]: https://docs.pinecone.io/guides/indexes/configure-pod-based-indexes#selective-metadata-indexing // [cloud environment]: https://docs.pinecone.io/guides/indexes/understanding-indexes#pod-environments // [replicas]: https://docs.pinecone.io/guides/indexes/configure-pod-based-indexes#add-replicas -// -// [type of pods]: https://docs.pinecone.io/guides/indexes/choose-a-pod-type-and-size +// [type of pod]: https://docs.pinecone.io/guides/indexes/choose-a-pod-type-and-size +// [deletion protection]: https://docs.pinecone.io/guides/indexes/prevent-index-deletion#enable-deletion-protection type CreatePodIndexRequest struct { Name string Dimension int32 @@ -552,10 +554,11 @@ func (c *Client) CreatePodIndex(ctx context.Context, in *CreatePodIndexRequest) // and consist only of lower case alphanumeric characters or '-'. // - Dimension: The [dimensionality] of the vectors to be inserted in the Index. // - Metric: The metric used to measure the [similarity] between vectors ('euclidean', 'cosine', or 'dotproduct'). -// - DeletionProtection: // - Cloud: The public [cloud provider] where you would like your Index hosted. // For serverless Indexes, you define only the cloud and region where the Index should be hosted. // - Region: The [region] where you would like your Index to be created. +// - DeletionProtection: (Optional) determines whether [deletion protection] is "enabled" or "disabled" for the index. +// When "enabled", the index cannot be deleted. Defaults to "disabled". // // To create a new Serverless Index, use the CreateServerlessIndex method on the Client object. // @@ -594,6 +597,7 @@ func (c *Client) CreatePodIndex(ctx context.Context, in *CreatePodIndexRequest) // [similarity]: https://docs.pinecone.io/guides/indexes/understanding-indexes#distance-metrics // [region]: https://docs.pinecone.io/troubleshooting/available-cloud-regions // [cloud provider]: https://docs.pinecone.io/troubleshooting/available-cloud-regions#regions-available-for-serverless-indexes +// [deletion protection]: https://docs.pinecone.io/guides/indexes/prevent-index-deletion#enable-deletion-protection type CreateServerlessIndexRequest struct { Name string Dimension int32 @@ -776,7 +780,7 @@ func (c *Client) DeleteIndex(ctx context.Context, idxName string) error { // - Replicas: (Optional) The number of replicas to scale the index to. This is capped by // the maximum number of replicas allowed in your Pinecone project. To configure this number, // go to [app.pinecone.io], select your project, and configure the maximum number of pods. -// - DeletionProtection: (Optional) // DeletionProtection determines whether [deletion protection] +// - DeletionProtection: (Optional) DeletionProtection determines whether [deletion protection] // is "enabled" or "disabled" for the index. When "enabled", the index cannot be deleted. Defaults to "disabled". // // Example: @@ -799,10 +803,11 @@ func (c *Client) DeleteIndex(ctx context.Context, idxName string) error { // // [app.pinecone.io]: https://app.pinecone.io // [scale a pods-based index]: https://docs.pinecone.io/guides/indexes/configure-pod-based-indexes +// [deletion protection]: https://docs.pinecone.io/guides/indexes/prevent-index-deletion#enable-deletion-protection type ConfigureIndexParams struct { PodType string Replicas int32 - DeletionProtection control.DeletionProtection + DeletionProtection DeletionProtection } // ConfigureIndex is used to [scale a pods-based index] up or down by changing the size of the pods or the number of @@ -846,33 +851,34 @@ type ConfigureIndexParams struct { // } // // [scale a pods-based index]: https://docs.pinecone.io/guides/indexes/configure-pod-based-indexes -func (c *Client) ConfigureIndex(ctx context.Context, name string, in *ConfigureIndexParams) (*Index, error) { - +func (c *Client) ConfigureIndex(ctx context.Context, name string, in ConfigureIndexParams) (*Index, error) { if in.PodType == "" && in.Replicas == 0 && in.DeletionProtection == "" { - return nil, fmt.Errorf("must specify either PodType, Replicas, or DeletionProtection") + return nil, fmt.Errorf("must specify PodType, Replicas, or DeletionProtection when configuring an index") } podType := pointerOrNil(in.PodType) replicas := pointerOrNil(in.Replicas) deletionProtection := pointerOrNil(in.DeletionProtection) - request := control.ConfigureIndexRequest{ - Spec: &struct { - Pod struct { - PodType *string `json:"pod_type,omitempty"` - Replicas *int32 `json:"replicas,omitempty"` - } `json:"pod"` - }{ - Pod: struct { - PodType *string `json:"pod_type,omitempty"` - Replicas *int32 `json:"replicas,omitempty"` + var request control.ConfigureIndexRequest + if podType != nil || replicas != nil { + request.Spec = + &struct { + Pod struct { + PodType *string `json:"pod_type,omitempty"` + Replicas *int32 `json:"replicas,omitempty"` + } `json:"pod"` }{ - PodType: podType, - Replicas: replicas, - }, - }, - DeletionProtection: deletionProtection, + Pod: struct { + PodType *string `json:"pod_type,omitempty"` + Replicas *int32 `json:"replicas,omitempty"` + }{ + PodType: podType, + Replicas: replicas, + }, + } } + request.DeletionProtection = (*control.DeletionProtection)(deletionProtection) res, err := c.restClient.ConfigureIndex(ctx, name, request) if err != nil { @@ -888,6 +894,11 @@ func (c *Client) ConfigureIndex(ctx context.Context, name string, in *ConfigureI return decodeIndex(res.Body) } +func PrettifyStruct(obj interface{}) string { + bytes, _ := json.MarshalIndent(obj, "", " ") + return string(bytes) +} + // ListCollections retrieves a list of all Collections in a Pinecone [project]. See Collection for more information. // // Parameters: @@ -1209,7 +1220,7 @@ func toIndex(idx *control.IndexModel) *Index { Ready: idx.Status.Ready, State: IndexStatusState(idx.Status.State), } - deletionProtection := derefOrDefault(idx.DeletionProtection, "") + deletionProtection := derefOrDefault(idx.DeletionProtection, "disabled") return &Index{ Name: idx.Name, diff --git a/pinecone/client_test.go b/pinecone/client_test.go index 32a8624..d7218df 100644 --- a/pinecone/client_test.go +++ b/pinecone/client_test.go @@ -24,50 +24,7 @@ import ( ) // Integration tests: -<<<<<<< HEAD func (ts *IntegrationTests) TestNewClientParamsSet() { -======= -type ClientTestsIntegration struct { - suite.Suite - client Client - clientSourceTag Client - sourceTag string - podIndex string - serverlessIndex string -} - -func TestIntegrationClient(t *testing.T) { - suite.Run(t, new(ClientTestsIntegration)) -} - -func (ts *ClientTestsIntegration) SetupSuite() { - apiKey := os.Getenv("PINECONE_API_KEY") - require.NotEmpty(ts.T(), apiKey, "PINECONE_API_KEY env variable not set") - - ts.podIndex = os.Getenv("STATIC_TEST_PODS_INDEX_NAME") - require.NotEmpty(ts.T(), ts.podIndex, "STATIC_TEST_PODS_INDEX_NAME env variable not set") - - ts.serverlessIndex = os.Getenv("STATIC_TEST_SERVERLESS_INDEX_NAME") - require.NotEmpty(ts.T(), ts.serverlessIndex, "STATIC_TEST_SERVERLESS_INDEX_NAME env variable not set") - - client, err := NewClient(NewClientParams{}) - require.NoError(ts.T(), err) - - ts.client = *client - - ts.sourceTag = "test_source_tag" - clientSourceTag, err := NewClient(NewClientParams{ApiKey: apiKey, SourceTag: ts.sourceTag}) - require.NoError(ts.T(), err) - ts.clientSourceTag = *clientSourceTag - - // this will clean up the project deleting all indexes and collections that are - // named a UUID. Generally not needed as all tests are cleaning up after themselves - // Left here as a convenience during active development. - //deleteUUIDNamedResources(context.Background(), &ts.client) -} - -func (ts *ClientTestsIntegration) TestNewClientParamsSet() { ->>>>>>> 2e391eb (update the other one this time) apiKey := "test-api-key" client, err := NewClient(NewClientParams{ApiKey: apiKey}) @@ -492,6 +449,71 @@ func (ts *IntegrationTests) TestDeleteCollection() { require.NoError(ts.T(), err) } +func (ts *IntegrationTests) TestDeletionProtectionPodIntegration() { + name := uuid.New().String() + + defer func(ts *IntegrationTests, name string) { + err := ts.deleteIndex(name) + require.NoError(ts.T(), err) + }(ts, name) + + _, err := ts.client.CreatePodIndex(context.Background(), &CreatePodIndexRequest{ + Name: name, + Dimension: 10, + Metric: Cosine, + Environment: "us-east1-gcp", + PodType: "p1.x1", + DeletionProtection: "enabled", + Shards: 1, + Replicas: 1, + }) + require.NoError(ts.T(), err) + + // validate we cannot delete the index + err = ts.client.DeleteIndex(context.Background(), name) + require.ErrorContainsf(ts.T(), err, "failed to delete index: Deletion protection is enabled for this index", err.Error()) + + // make sure configuring the index without specifying DeletionProtection maintains "enabled" state + index, err := ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{PodType: "p1.x2"}) + require.NoError(ts.T(), err) + require.Equal(ts.T(), DeletionProtectionEnabled, index.DeletionProtection, "Expected deletion protection to be 'enabled'") + + // validate we cannot delete the index + err = ts.client.DeleteIndex(context.Background(), name) + require.ErrorContainsf(ts.T(), err, "failed to delete index: Deletion protection is enabled for this index", err.Error()) + + // disable deletion protection so we can clean up + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{DeletionProtection: "disabled"}) + require.NoError(ts.T(), err) +} + +func (ts *IntegrationTests) TestDeletionProtectionServerlessIntegration() { + name := uuid.New().String() + + defer func(ts *IntegrationTests, name string) { + err := ts.deleteIndex(name) + require.NoError(ts.T(), err) + }(ts, name) + + _, err := ts.client.CreateServerlessIndex(context.Background(), &CreateServerlessIndexRequest{ + Name: name, + Dimension: 10, + Metric: Cosine, + Cloud: "aws", + Region: "us-west-2", + DeletionProtection: "enabled", + }) + require.NoError(ts.T(), err) + + // validate we cannot delete the index + err = ts.client.DeleteIndex(context.Background(), name) + require.ErrorContainsf(ts.T(), err, "failed to delete index: Deletion protection is enabled for this index", err.Error()) + + // disable deletion protection so we can clean up + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{DeletionProtection: "disabled"}) + require.NoError(ts.T(), err) +} + func (ts *IntegrationTests) TestConfigureIndexIllegalScaleDown() { name := uuid.New().String() @@ -511,7 +533,7 @@ func (ts *IntegrationTests) TestConfigureIndexIllegalScaleDown() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{PodType: "p1.x1", Replicas: 1}) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{PodType: "p1.x1"}) require.ErrorContainsf(ts.T(), err, "Cannot scale down", err.Error()) } @@ -534,7 +556,7 @@ func (ts *IntegrationTests) TestConfigureIndexScaleUpNoPods() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{Replicas: 2}) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{Replicas: 2}) require.NoError(ts.T(), err) } @@ -557,7 +579,7 @@ func (ts *IntegrationTests) TestConfigureIndexScaleUpNoReplicas() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{PodType: "p1.x4"}) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{PodType: "p1.x4"}) require.NoError(ts.T(), err) } @@ -580,8 +602,8 @@ func (ts *IntegrationTests) TestConfigureIndexIllegalNoPodsOrReplicasOrDeletionP log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{}) - require.ErrorContainsf(ts.T(), err, "must specify either PodType, Replicas, or DeletionProtection", err.Error()) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{}) + require.ErrorContainsf(ts.T(), err, "must specify PodType, Replicas, or DeletionProtection", err.Error()) } func (ts *IntegrationTests) TestConfigureIndexHitPodLimit() { @@ -603,7 +625,7 @@ func (ts *IntegrationTests) TestConfigureIndexHitPodLimit() { log.Fatalf("Error creating index %s: %v", name, err) } - _, err = ts.client.ConfigureIndex(context.Background(), name, &ConfigureIndexParams{Replicas: 30000}) + _, err = ts.client.ConfigureIndex(context.Background(), name, ConfigureIndexParams{Replicas: 30000}) require.ErrorContainsf(ts.T(), err, "You've reached the max pods allowed", err.Error()) } diff --git a/pinecone/models.go b/pinecone/models.go index 2b03304..661adf5 100644 --- a/pinecone/models.go +++ b/pinecone/models.go @@ -36,8 +36,8 @@ const ( type DeletionProtection string const ( - DeletionProtectionEnabled DeletionProtection = "Enabled" - DeletionProtectionDisabled DeletionProtection = "Disabled" + DeletionProtectionEnabled DeletionProtection = "enabled" + DeletionProtectionDisabled DeletionProtection = "disabled" ) // Cloud is the [cloud provider] to be used for a Pinecone serverless Index.