From 9b5a676481c94ac850d542474625a48929779f13 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sat, 7 Oct 2023 00:09:00 -0400 Subject: [PATCH 1/4] chore: gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7e27e8a..7f3f420 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.terraform +.terraform.lock.hcl .tfvars terraform.tfvars terraform.tfstate @@ -8,4 +10,4 @@ terraform.tfstate.backup dist/* # Ignore go tools artifacts -bin/* \ No newline at end of file +bin/* From 3ab2522f0d06bb7e9d3085871ff3b5ebef853bcb Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sat, 7 Oct 2023 00:21:01 -0400 Subject: [PATCH 2/4] chore: ignore `logs.jsonl` --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7f3f420..04b6637 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ terraform.tfvars terraform.tfstate terraform.tfstate.backup .env +logs.jsonl # Ignore goreleaser artifacts dist/* From 7738250a410eb8f9b6d041e5de7183ea6724d475 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sat, 7 Oct 2023 15:05:31 -0400 Subject: [PATCH 3/4] feat: implement `ImportState` --- internal/resources/index.go | 52 +++++++++++++++++++++++++++++++---- internal/services/pinecone.go | 3 ++ learning/log.md | 8 ++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/internal/resources/index.go b/internal/resources/index.go index b2b9953..66465d2 100644 --- a/internal/resources/index.go +++ b/internal/resources/index.go @@ -49,12 +49,16 @@ type indexResourceModel struct { } // Metadata returns the resource type name. -func (r *indexResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { +func (r *indexResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + tflog.Debug(ctx, "indexResource.Metadata", map[string]any{"req": req, "resp": resp}) + resp.TypeName = req.ProviderTypeName + "_index" } // Schema defines the schema for the resource. -func (r *indexResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *indexResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + tflog.Debug(ctx, "indexResource.Schema", map[string]any{"req": req, "resp": resp}) + resp.Schema = schema.Schema{ Description: "Manages an index.", Attributes: map[string]schema.Attribute{ @@ -100,7 +104,8 @@ func (r *indexResource) Schema(_ context.Context, _ resource.SchemaRequest, resp } // Configure adds the provider configured client to the resource. -func (r *indexResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *indexResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + tflog.Debug(ctx, "indexResource.Configure", map[string]any{"req": req, "resp": resp}) if req.ProviderData == nil { return } @@ -122,6 +127,7 @@ func (r *indexResource) Configure(_ context.Context, req resource.ConfigureReque // Create a new resource. func (r *indexResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + tflog.Debug(ctx, "indexResource.Create", map[string]any{"req": req, "resp": resp}) var plan indexResourceModel // Read Terraform plan data into the model @@ -185,6 +191,8 @@ func (r *indexResource) Create(ctx context.Context, req resource.CreateRequest, // Read resource information. func (r *indexResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + tflog.Debug(ctx, "indexResource.Read", map[string]any{"req": req, "resp": resp}) + // Get current state // Read data from Terraform state var state indexResourceModel @@ -200,7 +208,7 @@ func (r *indexResource) Read(ctx context.Context, req resource.ReadRequest, resp if err != nil { resp.Diagnostics.AddError( "Failed to describe index", - fmt.Sprintf("Failed to describe index: %s", err), + err.Error(), ) return } @@ -220,6 +228,8 @@ func (r *indexResource) Read(ctx context.Context, req resource.ReadRequest, resp // Update resource information. func (r *indexResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + tflog.Debug(ctx, "indexResource.Update", map[string]any{"req": req, "resp": resp}) + var plan indexResourceModel // Read Terraform plan data into the model @@ -249,6 +259,8 @@ func (r *indexResource) Update(ctx context.Context, req resource.UpdateRequest, // Delete resource information. func (r *indexResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + tflog.Debug(ctx, "indexResource.Delete", map[string]any{"req": req, "resp": resp}) + var state indexResourceModel // Read Terraform plan data into the model @@ -274,7 +286,37 @@ func (r *indexResource) Delete(ctx context.Context, req resource.DeleteRequest, } func (r *indexResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - tflog.Info(ctx, "Import state", map[string]any{"ID": req.ID}) + tflog.Debug(ctx, "indexResource.ImportState", map[string]any{"req": req, "resp": resp}) + + // fetch a fresh index from pinecone + // req.ID appears to be our only way to get the index name + indexName := req.ID + // note that what gets fetched from pinecone, based on purely + // the index name, may differ from the rest of whatever is + // specified in the resource stanza in HCL + + // Get fresh state from Pinecone + response, err := r.client.DescribeIndex(indexName) + if err != nil { + resp.Diagnostics.AddError( + "Failed to describe index", + err.Error(), + ) + return + } + + // TODO(kevinwang) wait for the index to be in a ready state + state := indexResourceModel{} + state.Dimension = types.Int64Value(response.Database.Dimension) + state.Metric = types.StringValue(response.Database.Metric) + state.Replicas = types.Int64Value(response.Database.Replicas) + state.Pods = types.Int64Value(response.Database.Pods) + state.Name = types.StringValue(response.Database.Name) + + // Save data into Terraform state + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + // Retrieve import ID and save to id attribute resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } diff --git a/internal/services/pinecone.go b/internal/services/pinecone.go index 9669182..404d440 100644 --- a/internal/services/pinecone.go +++ b/internal/services/pinecone.go @@ -291,6 +291,9 @@ type DescribeIndexResponse struct { // https://controller.{environment}.pinecone.io/databases/{indexName} // Get a description of an index. func (p *Pinecone) DescribeIndex(name string) (*DescribeIndexResponse, error) { + if name == "" { + return nil, fmt.Errorf("DescribeIndex failed: name argument was not specified") + } url := fmt.Sprintf(baseUrl+"/databases/%s", p.Environment, name) // initialize a request diff --git a/learning/log.md b/learning/log.md index 4adfdbf..8d78644 100644 --- a/learning/log.md +++ b/learning/log.md @@ -272,3 +272,11 @@ Login to https://registry.terraform.io/publish/provider/github/thiskevinwang Visit https://developer.hashicorp.com/terraform/registry/providers/publishing 10-05-2023 2:35pm Publishing... + +10-07-2023 12:41am Logging + +Regexp to grok `.jsonl` file + +`"indexResource.[a-z]+".+tf_rpc` + +`TF_LOG=json TF_LOG_PROVIDER=info TF_LOG_PATH=logs.jsonl terraform apply` From 5474d0c29e7968c071bfa968a5f851533603fe43 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sat, 7 Oct 2023 15:30:00 -0400 Subject: [PATCH 4/4] chore: README --- README.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6c65dbb..c7fef3f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ go test -v ./... ## Documenting -This uses `./tools/tools.go` to in stall [`tfplugindocs`](github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs) +This project relies on `./tools/tools.go` to install [`tfplugindocs`](github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs). ```console export GOBIN=$PWD/bin @@ -32,22 +32,27 @@ go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs which tfplugindocs ``` -Generate docs, and preview them at https://registry.terraform.io/tools/doc-preview +Run `tfplugindocs` to generate docs, and preview individual files at https://registry.terraform.io/tools/doc-preview -## Release +## Releasing -https://developer.hashicorp.com/terraform/registry/providers/publishing#publishing-to-the-registry +There are a few one time steps that have been done already and will not be covered in this README. See the following footnote for more information. [^release] -- Sign in to https://registry.terraform.io/publish/provider/github/thiskevinwang/terraform-provider-pinecone -- Ensure repo has `.goreleaser.yml` -- gpg --armor --export "[EMAIL]" - - add this to registry signing keys -- gpg --armor --detach-sign -- git tag v0.1.1 -- GITHUB_TOKEN=$(gh auth token) goreleaser release --clean +[^release]: Docs on publishing: https://developer.hashicorp.com/terraform/registry/providers/publishing#publishing-to-the-registry + +To release a new version of the provider to the registry, a new GitHub release needs to be created. +Use the following steps to proceed. + +1. Ideally you’re on `main`, and have a clean working tree. +2. Ensure the commit (aka HEAD) you're about to release is tagged. + - `git tag v0.1.2` + - `git push origin v0.1.2` +3. Run `goreleaser`: `GITHUB_TOKEN=$(gh auth token) goreleaser release --clean` + - This will create a new GitHub release which should be detected by the Terraform registry shortly after. ## Appendix +- https://thekevinwang.com/2023/10/05/build-publish-terraform-provider - https://docs.pinecone.io/ - https://github.com/hashicorp/terraform-plugin-framework - https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework/providers-plugin-framework-provider