diff --git a/ec/ecresource/extensionresource/create.go b/ec/ecresource/extensionresource/create.go new file mode 100644 index 000000000..74f88463b --- /dev/null +++ b/ec/ecresource/extensionresource/create.go @@ -0,0 +1,46 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "context" + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// Create will createResource a new deployment traffic filter ruleset +func createResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var client = meta.(*api.API) + + res, err := createRequest(client, d) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(*res.Payload.ID) + + if d.Get("file_path") != nil { + _, err = uploadRequest(client, d) + if err != nil { + return diag.FromErr(multierror.NewPrefixed("failed to upload file", err)) + } + } + return readResource(ctx, d, meta) +} diff --git a/ec/ecresource/extensionresource/create_test.go b/ec/ecresource/extensionresource/create_test.go new file mode 100644 index 000000000..2c33b134f --- /dev/null +++ b/ec/ecresource/extensionresource/create_test.go @@ -0,0 +1,92 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "context" + "testing" + + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/mock" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/stretchr/testify/assert" + + "github.com/elastic/terraform-provider-ec/ec/internal/util" +) + +func Test_createResource(t *testing.T) { + tc500Err := util.NewResourceData(t, util.ResDataParams{ + ID: "12345678", + State: newExtension(), + Schema: newSchema(), + }) + wantTC500 := util.NewResourceData(t, util.ResDataParams{ + ID: "12345678", + State: newExtension(), + Schema: newSchema(), + }) + + type args struct { + ctx context.Context + d *schema.ResourceData + meta interface{} + } + tests := []struct { + name string + args args + want diag.Diagnostics + wantRD *schema.ResourceData + }{ + { + name: "returns an error when it receives a 500", + args: args{ + d: tc500Err, + meta: api.NewMock(mock.NewErrorResponse(500, mock.APIError{ + Code: "some", Message: "message", + })), + }, + want: diag.Diagnostics{ + { + Severity: diag.Error, + Summary: "api error: 1 error occurred:\n\t* some: message\n\n", + }, + }, + wantRD: wantTC500, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := createResource(tt.args.ctx, tt.args.d, tt.args.meta) + assert.Equal(t, tt.want, got) + var want interface{} + if tt.wantRD != nil { + if s := tt.wantRD.State(); s != nil { + want = s.Attributes + } + } + + var gotState interface{} + if s := tt.args.d.State(); s != nil { + gotState = s.Attributes + } + + assert.Equal(t, want, gotState) + }) + } +} diff --git a/ec/ecresource/extensionresource/delete.go b/ec/ecresource/extensionresource/delete.go new file mode 100644 index 000000000..706fedb3f --- /dev/null +++ b/ec/ecresource/extensionresource/delete.go @@ -0,0 +1,48 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "context" + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/apierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func deleteResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var client = meta.(*api.API) + + _, err := deleteRequest(client, d) + + if err != nil { + if alreadyDestroyed(err) { + d.SetId("") + return nil + } + + return diag.FromErr(err) + } + + return nil +} + +func alreadyDestroyed(err error) bool { + // If the extension is already destroyed, API return 403. + return apierror.IsRuntimeStatusCode(err, 403) +} diff --git a/ec/ecresource/extensionresource/delete_test.go b/ec/ecresource/extensionresource/delete_test.go new file mode 100644 index 000000000..ebe6c7954 --- /dev/null +++ b/ec/ecresource/extensionresource/delete_test.go @@ -0,0 +1,115 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "context" + "testing" + + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/mock" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/stretchr/testify/assert" + + "github.com/elastic/terraform-provider-ec/ec/internal/util" +) + +func Test_deleteResource(t *testing.T) { + tc500Err := util.NewResourceData(t, util.ResDataParams{ + ID: "12345678", + State: newExtension(), + Schema: newSchema(), + }) + wantTC500 := util.NewResourceData(t, util.ResDataParams{ + ID: "12345678", + State: newExtension(), + Schema: newSchema(), + }) + + tc403Err := util.NewResourceData(t, util.ResDataParams{ + ID: "12345678", + State: newExtension(), + Schema: newSchema(), + }) + wantTC403 := util.NewResourceData(t, util.ResDataParams{ + ID: "12345678", + State: newExtension(), + Schema: newSchema(), + }) + wantTC403.SetId("") + + type args struct { + ctx context.Context + d *schema.ResourceData + meta interface{} + } + tests := []struct { + name string + args args + want diag.Diagnostics + wantRD *schema.ResourceData + }{ + { + name: "returns an error when it receives a 500", + args: args{ + d: tc500Err, + meta: api.NewMock(mock.NewErrorResponse(500, mock.APIError{ + Code: "some", Message: "message", + })), + }, + want: diag.Diagnostics{ + { + Severity: diag.Error, + Summary: "api error: 1 error occurred:\n\t* some: message\n\n", + }, + }, + wantRD: wantTC500, + }, + { + name: "returns nil and unsets the state when the error is known", + args: args{ + d: tc403Err, + meta: api.NewMock(mock.NewErrorResponse(403, mock.APIError{ + Code: "some", Message: "message", + })), + }, + want: nil, + wantRD: wantTC403, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := deleteResource(tt.args.ctx, tt.args.d, tt.args.meta) + assert.Equal(t, tt.want, got) + var want interface{} + if tt.wantRD != nil { + if s := tt.wantRD.State(); s != nil { + want = s.Attributes + } + } + + var gotState interface{} + if s := tt.args.d.State(); s != nil { + gotState = s.Attributes + } + + assert.Equal(t, want, gotState) + }) + } +} diff --git a/ec/ecresource/extensionresource/read.go b/ec/ecresource/extensionresource/read.go new file mode 100644 index 000000000..2cf7ac9f5 --- /dev/null +++ b/ec/ecresource/extensionresource/read.go @@ -0,0 +1,83 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "context" + "errors" + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/apierror" + "github.com/elastic/cloud-sdk-go/pkg/client/extensions" + "github.com/elastic/cloud-sdk-go/pkg/models" + "github.com/elastic/cloud-sdk-go/pkg/multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func readResource(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var client = meta.(*api.API) + + res, err := readRequest(d, client) + + if err != nil { + if extensionNotFound(err) { + d.SetId("") + return nil + } + + return diag.FromErr(multierror.NewPrefixed("failed reading extension", err)) + } + + if err := modelToState(d, res.Payload); err != nil { + return diag.FromErr(err) + } + + return nil +} + +func extensionNotFound(err error) bool { + // We're using the As() call since we do not care about the error value + // but do care about the error's contents type since it's an implicit 404. + var extensionNotFound *extensions.GetExtensionNotFound + if errors.As(err, &extensionNotFound) { + return true + } + + // We also check for the case where a 403 is thrown for ESS. + return apierror.IsRuntimeStatusCode(err, 403) +} + +func modelToState(d *schema.ResourceData, model *models.Extension) error { + if err := d.Set("name", model.Name); err != nil { + return err + } + + if err := d.Set("version", model.Version); err != nil { + return err + } + + if err := d.Set("extension_type", model.ExtensionType); err != nil { + return err + } + + if err := d.Set("description", model.Description); err != nil { + return err + } + + return nil +} diff --git a/ec/ecresource/extensionresource/read_test.go b/ec/ecresource/extensionresource/read_test.go new file mode 100644 index 000000000..beb87d328 --- /dev/null +++ b/ec/ecresource/extensionresource/read_test.go @@ -0,0 +1,115 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "context" + "testing" + + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/mock" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/stretchr/testify/assert" + + "github.com/elastic/terraform-provider-ec/ec/internal/util" +) + +func Test_readResource(t *testing.T) { + tc500Err := util.NewResourceData(t, util.ResDataParams{ + ID: mock.ValidClusterID, + State: newExtension(), + Schema: newSchema(), + }) + wantTC500 := util.NewResourceData(t, util.ResDataParams{ + ID: mock.ValidClusterID, + State: newExtension(), + Schema: newSchema(), + }) + + tc404Err := util.NewResourceData(t, util.ResDataParams{ + ID: mock.ValidClusterID, + State: newExtension(), + Schema: newSchema(), + }) + wantTC404 := util.NewResourceData(t, util.ResDataParams{ + ID: mock.ValidClusterID, + State: newExtension(), + Schema: newSchema(), + }) + wantTC404.SetId("") + + type args struct { + ctx context.Context + d *schema.ResourceData + meta interface{} + } + tests := []struct { + name string + args args + want diag.Diagnostics + wantRD *schema.ResourceData + }{ + { + name: "returns an error when it receives a 500", + args: args{ + d: tc500Err, + meta: api.NewMock(mock.NewErrorResponse(500, mock.APIError{ + Code: "some", Message: "message", + })), + }, + want: diag.Diagnostics{ + { + Severity: diag.Error, + Summary: "failed reading extension: 1 error occurred:\n\t* api error: some: message\n\n", + }, + }, + wantRD: wantTC500, + }, + { + name: "returns nil and unsets the state when the error is known", + args: args{ + d: tc404Err, + meta: api.NewMock(mock.NewErrorResponse(404, mock.APIError{ + Code: "some", Message: "message", + })), + }, + want: nil, + wantRD: wantTC404, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := readResource(tt.args.ctx, tt.args.d, tt.args.meta) + assert.Equal(t, tt.want, got) + var want interface{} + if tt.wantRD != nil { + if s := tt.wantRD.State(); s != nil { + want = s.Attributes + } + } + + var gotState interface{} + if s := tt.args.d.State(); s != nil { + gotState = s.Attributes + } + + assert.Equal(t, want, gotState) + }) + } +} diff --git a/ec/ecresource/extensionresource/request.go b/ec/ecresource/extensionresource/request.go new file mode 100644 index 000000000..e3fe3d408 --- /dev/null +++ b/ec/ecresource/extensionresource/request.go @@ -0,0 +1,115 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "os" + + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/apierror" + "github.com/elastic/cloud-sdk-go/pkg/client/extensions" + "github.com/elastic/cloud-sdk-go/pkg/models" + "github.com/elastic/cloud-sdk-go/pkg/multierror" + "github.com/go-openapi/runtime" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func readRequest(d *schema.ResourceData, client *api.API) (*extensions.GetExtensionOK, error) { + res, err := client.V1API.Extensions.GetExtension( + extensions.NewGetExtensionParams().WithExtensionID(d.Id()), + client.AuthWriter) + + if err != nil { + return nil, apierror.Wrap(err) + } + return res, nil +} + +func createRequest(client *api.API, d *schema.ResourceData) (*extensions.CreateExtensionCreated, error) { + name := d.Get("name").(string) + version := d.Get("version").(string) + extensionsType := d.Get("extension_type").(string) + description := d.Get("description").(string) + + body := &models.CreateExtensionRequest{ + Name: &name, + Version: &version, + ExtensionType: &extensionsType, + Description: description, + } + + res, err := client.V1API.Extensions.CreateExtension( + extensions.NewCreateExtensionParams().WithBody(body), + client.AuthWriter) + + if err != nil { + return nil, apierror.Wrap(err) + } + return res, nil +} + +func updateRequest(client *api.API, d *schema.ResourceData) (*extensions.UpdateExtensionOK, error) { + name := d.Get("name").(string) + version := d.Get("version").(string) + extensionsType := d.Get("extension_type").(string) + description := d.Get("description").(string) + + body := &models.UpdateExtensionRequest{ + Name: &name, + Version: &version, + ExtensionType: &extensionsType, + Description: description, + } + + res, err := client.V1API.Extensions.UpdateExtension( + extensions.NewUpdateExtensionParams().WithBody(body).WithExtensionID(d.Id()), + client.AuthWriter) + if err != nil { + return nil, apierror.Wrap(err) + } + + return res, nil +} + +func deleteRequest(client *api.API, d *schema.ResourceData) (*extensions.DeleteExtensionOK, error) { + res, err := client.V1API.Extensions.DeleteExtension( + extensions.NewDeleteExtensionParams().WithExtensionID(d.Id()), + client.AuthWriter) + if err != nil { + return nil, apierror.Wrap(err) + } + + return res, nil +} +func uploadRequest(client *api.API, d *schema.ResourceData) (*extensions.UploadExtensionOK, error) { + + reader, err := os.Open(d.Get("file_path").(string)) + if err != nil { + return nil, multierror.NewPrefixed("failed open file", err) + } + + res, err := client.V1API.Extensions.UploadExtension( + extensions.NewUploadExtensionParams().WithExtensionID(d.Id()). + WithFile(runtime.NamedReader(d.Get("file_path").(string), reader)), + client.AuthWriter) + if err != nil { + return nil, apierror.Wrap(err) + } + + return res, nil +} diff --git a/ec/ecresource/extensionresource/resource.go b/ec/ecresource/extensionresource/resource.go new file mode 100644 index 000000000..3dcdee81f --- /dev/null +++ b/ec/ecresource/extensionresource/resource.go @@ -0,0 +1,41 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// Resource returns the ec_extension resource schema. +func Resource() *schema.Resource { + return &schema.Resource{ + Description: "Elastic Cloud deployment traffic filtering rules", + Schema: newSchema(), + + CreateContext: createResource, + ReadContext: readResource, + UpdateContext: updateResource, + DeleteContext: deleteResource, + + Timeouts: &schema.ResourceTimeout{ + Default: schema.DefaultTimeout(10 * time.Minute), + }, + } +} diff --git a/ec/ecresource/extensionresource/schema.go b/ec/ecresource/extensionresource/schema.go new file mode 100644 index 000000000..952d84213 --- /dev/null +++ b/ec/ecresource/extensionresource/schema.go @@ -0,0 +1,64 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func newSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "Required name of the ruleset", + Required: true, + }, + "description": { + Type: schema.TypeString, + Description: "Description for extension", + Required: true, + }, + "extension_type": { + Type: schema.TypeString, + Description: "Extension type. bundle or plugin", + Required: true, + }, + "version": { + Type: schema.TypeString, + Description: "Eleasticsearch version", + Required: true, + }, + "download_url": { + Type: schema.TypeString, + Description: "download url", + Optional: true, + }, + + // Uploading file bia API + "file_path": { + Type: schema.TypeString, + Description: "file path", + Optional: true, + }, + "file_hash": { + Type: schema.TypeString, + Description: "file hash", + Optional: true, + }, + } +} diff --git a/ec/ecresource/extensionresource/testutil_datastruct.go b/ec/ecresource/extensionresource/testutil_datastruct.go new file mode 100644 index 000000000..f7b69a20c --- /dev/null +++ b/ec/ecresource/extensionresource/testutil_datastruct.go @@ -0,0 +1,27 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +func newExtension() map[string]interface{} { + return map[string]interface{}{ + "name": "my_extension", + "extension": "bundle", + "description": "my description", + "version": "*", + } +} diff --git a/ec/ecresource/extensionresource/update.go b/ec/ecresource/extensionresource/update.go new file mode 100644 index 000000000..2d000e164 --- /dev/null +++ b/ec/ecresource/extensionresource/update.go @@ -0,0 +1,45 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "context" + + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func updateResource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var client = meta.(*api.API) + + _, err := updateRequest(client, d) + if err != nil { + return diag.FromErr(err) + } + + if d.Get("file_path") != nil { + _, err = uploadRequest(client, d) + if err != nil { + return diag.FromErr(multierror.NewPrefixed("failed to upload file", err)) + } + } + + return readResource(ctx, d, meta) +} diff --git a/ec/ecresource/extensionresource/update_test.go b/ec/ecresource/extensionresource/update_test.go new file mode 100644 index 000000000..99f0bcece --- /dev/null +++ b/ec/ecresource/extensionresource/update_test.go @@ -0,0 +1,92 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. 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. + +package extensionresource + +import ( + "context" + "testing" + + "github.com/elastic/cloud-sdk-go/pkg/api" + "github.com/elastic/cloud-sdk-go/pkg/api/mock" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/stretchr/testify/assert" + + "github.com/elastic/terraform-provider-ec/ec/internal/util" +) + +func Test_updateResource(t *testing.T) { + tc500Err := util.NewResourceData(t, util.ResDataParams{ + ID: "12345678", + State: newExtension(), + Schema: newSchema(), + }) + wantTC500 := util.NewResourceData(t, util.ResDataParams{ + ID: "12345678", + State: newExtension(), + Schema: newSchema(), + }) + + type args struct { + ctx context.Context + d *schema.ResourceData + meta interface{} + } + tests := []struct { + name string + args args + want diag.Diagnostics + wantRD *schema.ResourceData + }{ + { + name: "returns an error when it receives a 500", + args: args{ + d: tc500Err, + meta: api.NewMock(mock.NewErrorResponse(500, mock.APIError{ + Code: "some", Message: "message", + })), + }, + want: diag.Diagnostics{ + { + Severity: diag.Error, + Summary: "api error: 1 error occurred:\n\t* some: message\n\n", + }, + }, + wantRD: wantTC500, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := updateResource(tt.args.ctx, tt.args.d, tt.args.meta) + assert.Equal(t, tt.want, got) + var want interface{} + if tt.wantRD != nil { + if s := tt.wantRD.State(); s != nil { + want = s.Attributes + } + } + + var gotState interface{} + if s := tt.args.d.State(); s != nil { + gotState = s.Attributes + } + + assert.Equal(t, want, gotState) + }) + } +} diff --git a/ec/provider.go b/ec/provider.go index 0d17a4990..43710aa72 100644 --- a/ec/provider.go +++ b/ec/provider.go @@ -29,6 +29,7 @@ import ( "github.com/elastic/terraform-provider-ec/ec/ecdatasource/deploymentsdatasource" "github.com/elastic/terraform-provider-ec/ec/ecdatasource/stackdatasource" "github.com/elastic/terraform-provider-ec/ec/ecresource/deploymentresource" + "github.com/elastic/terraform-provider-ec/ec/ecresource/extensionresource" "github.com/elastic/terraform-provider-ec/ec/ecresource/trafficfilterassocresource" "github.com/elastic/terraform-provider-ec/ec/ecresource/trafficfilterresource" ) @@ -71,6 +72,7 @@ func Provider() *schema.Provider { "ec_deployment": deploymentresource.Resource(), "ec_deployment_traffic_filter": trafficfilterresource.Resource(), "ec_deployment_traffic_filter_association": trafficfilterassocresource.Resource(), + "ec_extension": extensionresource.Resource(), }, } } diff --git a/examples/extension/README.md b/examples/extension/README.md new file mode 100644 index 000000000..38cf630b4 --- /dev/null +++ b/examples/extension/README.md @@ -0,0 +1,12 @@ +# Extension example + +This example shows how to deploy an Elastic Cloud extension using Terraform. +You can create the extension by `files/content.json` + +## Running the example + +To run the example, follow these steps: + +1. Build the provider by running `make install` from the main folder. +2. Run `terrafrom init` to initialize your Terraform CLI. +3. Run `terraform apply` to see how it works. diff --git a/examples/extension/extension.tf b/examples/extension/extension.tf new file mode 100644 index 000000000..16373ce11 --- /dev/null +++ b/examples/extension/extension.tf @@ -0,0 +1,27 @@ +terraform { + required_version = ">= 0.12.29" + + required_providers { + ec = { + source = "elastic/ec" + version = "0.1.0-beta" + } + } +} + +provider "ec" {} + +locals { + file_path = "./files/content.json.zip" +} + +# Create an Elastic Cloud Extension +resource "ec_extension" "example_extension" { + name = "my_extension" + description = "my extension" + version = "*" + extension_type = "bundle" + + file_path = local.file_path + file_hash = filebase64sha256(local.file_path) +} diff --git a/examples/extension/files/content.json b/examples/extension/files/content.json new file mode 100644 index 000000000..b18dda311 --- /dev/null +++ b/examples/extension/files/content.json @@ -0,0 +1 @@ +{"foo": "value", "bar": 3} \ No newline at end of file diff --git a/examples/extension/files/content.json.zip b/examples/extension/files/content.json.zip new file mode 100644 index 000000000..41b9d74f4 Binary files /dev/null and b/examples/extension/files/content.json.zip differ diff --git a/examples/extension/outputs.tf b/examples/extension/outputs.tf new file mode 100644 index 000000000..497f99678 --- /dev/null +++ b/examples/extension/outputs.tf @@ -0,0 +1,3 @@ +output "extension_id" { + value = ec_extension.example_extension.id +}