Skip to content

Commit

Permalink
ec_deployment: Add elasticsearch.extension block (#264)
Browse files Browse the repository at this point in the history
Adds an `extension` block to the Elasticsearch resource to be able to
use previously created extensions in the `ec_deployment` resource.

Signed-off-by: Marc Lopez <[email protected]>
  • Loading branch information
marclop authored Mar 23, 2021
1 parent b803984 commit 695a3bd
Show file tree
Hide file tree
Showing 12 changed files with 950 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/264.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
datasource/ec_deployment: Adds a new `elasticsearch.extension` block which can be used to enable custom Elasticsearch bundles or plugins that have previously been uploaded.
```
10 changes: 10 additions & 0 deletions docs/resources/ec_deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ The required `elasticsearch` block supports the following arguments:
* `config` (Optional) Elasticsearch settings applied to all topologies unless overridden in the `topology` element.
* `remote_cluster` (Optional) Elasticsearch remote clusters to configure for the Elasticsearch resource. Can be set multiple times.
* `snapshot_source` (Optional) Restores data from a snapshot of another deployment.
* `extension` (Optional) Custom Elasticsearch bundles or plugins. Can be set multiple times.

##### Topology

Expand Down Expand Up @@ -269,6 +270,15 @@ The optional `elasticsearch.snapshot_source` block, which restores data from a s

~> **Note on behavior** The `snapshot_source` block will not be saved in the Terraform state due to its transient nature. This means that whenever the `snapshot_source` block is set, a snapshot will **always be restored**, unless removed before running `terraform apply`.

##### Extension

The optional `elasticsearch.extension` block, allows custom plugins or bundles to be configured in the Elasticsearch cluster. It supports the following arguments:

* `name` (Required) Extension name.
* `type` (Required) Extension type, only `bundle` or `plugin` are supported.
* `version` (Required) Elasticsearch compatibility version. Bundles should specify major or minor versions with wildcards, such as `7.*` or `*` but **plugins must use full version notation down to the patch level**, such as `7.10.1` and wildcards are not allowed.
* `url` (Required) Bundle or plugin URL, the extension URL can be obtained from the `ec_deployment_extension.<name>.url` attribute or the API and cannot be a random HTTP address that is hosted elsewhere.

#### Kibana

The optional `kibana` block supports the following arguments:
Expand Down
35 changes: 35 additions & 0 deletions docs/resources/ec_deployment_extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,41 @@ resource "ec_deployment_extension" "example_extension" {
}
```

### Using extension in ec_deployment
```hcl
resource "ec_deployment_extension" "example_extension" {
name = "my_extension"
description = "my extension"
version = "*"
extension_type = "bundle"
download_url = "https://example.net"
}
data "ec_stack" "latest" {
version_regex = "latest"
region = "us-east-1"
}
resource "ec_deployment" "with_extension" {
# Optional name.
name = "my_example_deployment"
# Mandatory fields
region = "us-east-1"
version = data.ec_stack.latest.version
deployment_template_id = "aws-io-optimized-v2"
elasticsearch {
extension {
name = ec_deployment_extension.example_extension.name
type = "bundle"
version = data.ec_stack.latest.version
url = ec_deployment_extension.example_extension.url
}
}
}
```

## Argument Reference
The following arguments are supported:

Expand Down
94 changes: 94 additions & 0 deletions ec/acc/deployment_with_extension_bundle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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 acc

import (
"fmt"
"os"
"path/filepath"
"testing"

"github.com/elastic/cloud-sdk-go/pkg/multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccDeployment_withExtension(t *testing.T) {
extResName := "ec_deployment_extension.my_extension"
resName := "ec_deployment.with_extension"
randomName := prefix + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

filePath := filepath.Join(os.TempDir(), "extension.zip")
defer os.Remove(filePath)

cfg := fixtureAccDeploymentWithExtensionBundle(t,
"testdata/deployment_with_extension_bundle_file.tf",
getRegion(), randomName, "desc", filePath,
)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactory,
CheckDestroy: func(s *terraform.State) error {
merr := multierror.NewPrefixed("checking resource with extension")

if err := testAccExtensionDestroy(s); err != nil {
merr = merr.Append(err)
}
if err := testAccDeploymentDestroy(s); err != nil {
merr = merr.Append(err)
}

return merr.ErrorOrNil()
},
Steps: []resource.TestStep{
{
PreConfig: func() { writeFile(t, filePath, "extension.txt", "foo") },
Config: cfg,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(extResName, "name", randomName),
resource.TestCheckResourceAttr(extResName, "version", "*"),
resource.TestCheckResourceAttr(extResName, "description", "desc"),
resource.TestCheckResourceAttr(extResName, "extension_type", "bundle"),
resource.TestCheckResourceAttr(extResName, "file_path", filePath),
resource.TestCheckResourceAttr(resName, "elasticsearch.0.extension.#", "1"),
resource.TestCheckTypeSetElemNestedAttrs(resName, "elasticsearch.0.extension.*", map[string]string{
"type": "bundle",
"name": randomName,
}),
func(s *terraform.State) error {
return checkExtensionFile(t, s, "extension.txt", "foo")
},
),
},
},
})
}

func fixtureAccDeploymentWithExtensionBundle(t *testing.T, filepath, region, name, desc, file string) string {
t.Helper()

b, err := os.ReadFile(filepath)
if err != nil {
t.Fatal(err)
}
return fmt.Sprintf(string(b), region,
setDefaultTemplate(region, defaultTemplate), name, desc, file,
)
}
39 changes: 39 additions & 0 deletions ec/acc/testdata/deployment_with_extension_bundle_file.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
locals {
region = "%s"
deployment_template = "%s"
name = "%s"
description = "%s"
file_path = "%s"
}


data "ec_stack" "latest" {
version_regex = "latest"
region = local.region
}

resource "ec_deployment" "with_extension" {
name = local.name
region = local.region
version = data.ec_stack.latest.version
deployment_template_id = local.deployment_template

elasticsearch {
extension {
type = "bundle"
name = local.name
version = data.ec_stack.latest.version
url = ec_deployment_extension.my_extension.url
}
}
}

resource "ec_deployment_extension" "my_extension" {
name = local.name
description = local.description
version = "*"
extension_type = "bundle"

file_path = local.file_path
file_hash = filebase64sha256(local.file_path)
}
43 changes: 43 additions & 0 deletions ec/ecresource/deploymentresource/elasticsearch_expanders.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ func expandEsResource(raw interface{}, res *models.ElasticsearchPayload) (*model
expandSnapshotSource(snap, res.Plan.Transient.RestoreSnapshot)
}

if ext, ok := es["extension"]; ok {
if e := ext.(*schema.Set); e.Len() > 0 {
expandEsExtension(e.List(), res.Plan.Elasticsearch)
}
}

return res, nil
}

Expand Down Expand Up @@ -332,3 +338,40 @@ func sizeIsEmpty(size *models.TopologySize) bool {

return false
}

func expandEsExtension(raw []interface{}, es *models.ElasticsearchConfiguration) {
for _, rawExt := range raw {
m := rawExt.(map[string]interface{})

var version string
if v, ok := m["version"]; ok {
version = v.(string)
}

var url string
if u, ok := m["url"]; ok {
url = u.(string)
}

var name string
if n, ok := m["name"]; ok {
name = n.(string)
}

if t, ok := m["type"]; ok && t.(string) == "bundle" {
es.UserBundles = append(es.UserBundles, &models.ElasticsearchUserBundle{
Name: &name,
ElasticsearchVersion: &version,
URL: &url,
})
}

if t, ok := m["type"]; ok && t.(string) == "plugin" {
es.UserPlugins = append(es.UserPlugins, &models.ElasticsearchUserPlugin{
Name: &name,
ElasticsearchVersion: &version,
URL: &url,
})
}
}
}
43 changes: 43 additions & 0 deletions ec/ecresource/deploymentresource/elasticsearch_flatteners.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ func flattenEsResources(in []*models.ElasticsearchResourceInfo, name string, rem
m["remote_cluster"] = r
}

extensions := schema.NewSet(esExtensionHash, nil)
for _, ext := range flattenEsBundles(plan.Elasticsearch.UserBundles) {
extensions.Add(ext)
}

for _, ext := range flattenEsPlugins(plan.Elasticsearch.UserPlugins) {
extensions.Add(ext)
}

if extensions.Len() > 0 {
m["extension"] = extensions
}

result = append(result, m)
}

Expand Down Expand Up @@ -197,3 +210,33 @@ func flattenEsRemotes(in models.RemoteResources) []interface{} {

return res
}

func flattenEsBundles(in []*models.ElasticsearchUserBundle) []interface{} {
result := make([]interface{}, 0, len(in))
for _, bundle := range in {
m := make(map[string]interface{})
m["type"] = "bundle"
m["version"] = *bundle.ElasticsearchVersion
m["url"] = *bundle.URL
m["name"] = *bundle.Name

result = append(result, m)
}

return result
}

func flattenEsPlugins(in []*models.ElasticsearchUserPlugin) []interface{} {
result := make([]interface{}, 0, len(in))
for _, plugin := range in {
m := make(map[string]interface{})
m["type"] = "plugin"
m["version"] = *plugin.ElasticsearchVersion
m["url"] = *plugin.URL
m["name"] = *plugin.Name

result = append(result, m)
}

return result
}
Loading

0 comments on commit 695a3bd

Please sign in to comment.