Skip to content

Commit 1131f2a

Browse files
author
Kanji Yomoda
authored
Add script resource (#173)
* Implement script resource * Generate docs for script resource * Update CHANGELOG * Fixed reviewed ones * Fix test * Fix subcategory for elasticstack_elasticsearch_script * Fix elasticstack_elasticsearch_script docs * Format code * Regenerate docs * Remove new resource check
1 parent 99e7990 commit 1131f2a

File tree

10 files changed

+490
-0
lines changed

10 files changed

+490
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### Added
44
- New resource `elasticstack_elasticsearch_logstash_pipeline` to manage Logstash pipelines ([Centralized Pipeline Management](https://www.elastic.co/guide/en/logstash/current/logstash-centralized-pipeline-management.html)) ([#151](https://github.com/elastic/terraform-provider-elasticstack/pull/151))
5+
- Add `elasticstack_elasticsearch_script` resource ([#173](https://github.com/elastic/terraform-provider-elasticstack/pull/173))
56
- Add `elasticstack_elasticsearch_security_role` data source ([#177](https://github.com/elastic/terraform-provider-elasticstack/pull/177))
67
- Add `elasticstack_elasticsearch_security_role_mapping` data source ([#178](https://github.com/elastic/terraform-provider-elasticstack/pull/178))
78

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
subcategory: "Cluster"
3+
layout: ""
4+
page_title: "Elasticstack: elasticstack_elasticsearch_script Resource"
5+
description: |-
6+
Creates or updates a stored script or search template.
7+
---
8+
9+
# Resource: elasticstack_elasticsearch_script
10+
11+
Creates or updates a stored script or search template. See https://www.elastic.co/guide/en/elasticsearch/reference/current/create-stored-script-api.html
12+
13+
## Example Usage
14+
15+
```terraform
16+
provider "elasticstack" {
17+
elasticsearch {}
18+
}
19+
20+
resource "elasticstack_elasticsearch_script" "my_script" {
21+
script_id = "my_script"
22+
lang = "painless"
23+
source = "Math.log(_score * 2) + params['my_modifier']"
24+
context = "score"
25+
}
26+
27+
resource "elasticstack_elasticsearch_script" "my_search_template" {
28+
script_id = "my_search_template"
29+
lang = "mustache"
30+
source = jsonencode({
31+
query = {
32+
match = {
33+
message = "{{query_string}}"
34+
}
35+
}
36+
from = "{{from}}"
37+
size = "{{size}}"
38+
})
39+
params = jsonencode({
40+
query_string = "My query string"
41+
})
42+
}
43+
```
44+
45+
<!-- schema generated by tfplugindocs -->
46+
## Schema
47+
48+
### Required
49+
50+
- `lang` (String) Script language. For search templates, use `mustache`.
51+
- `script_id` (String) Identifier for the stored script. Must be unique within the cluster.
52+
- `source` (String) For scripts, a string containing the script. For search templates, an object containing the search template.
53+
54+
### Optional
55+
56+
- `context` (String) Context in which the script or search template should run.
57+
- `elasticsearch_connection` (Block List, Max: 1) Used to establish connection to Elasticsearch server. Overrides environment variables if present. (see [below for nested schema](#nestedblock--elasticsearch_connection))
58+
- `params` (String) Parameters for the script or search template.
59+
60+
### Read-Only
61+
62+
- `id` (String) The ID of this resource.
63+
64+
<a id="nestedblock--elasticsearch_connection"></a>
65+
### Nested Schema for `elasticsearch_connection`
66+
67+
Optional:
68+
69+
- `api_key` (String, Sensitive) API Key to use for authentication to Elasticsearch
70+
- `ca_data` (String) PEM-encoded custom Certificate Authority certificate
71+
- `ca_file` (String) Path to a custom Certificate Authority certificate
72+
- `endpoints` (List of String, Sensitive) A list of endpoints the Terraform provider will point to. They must include the http(s) schema and port number.
73+
- `insecure` (Boolean) Disable TLS certificate validation
74+
- `password` (String, Sensitive) A password to use for API authentication to Elasticsearch.
75+
- `username` (String) A username to use for API authentication to Elasticsearch.
76+
77+
## Import
78+
79+
Import is supported using the following syntax:
80+
81+
```shell
82+
terraform import elasticstack_elasticsearch_script.my_script <cluster_uuid>/<script id>
83+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
terraform import elasticstack_elasticsearch_script.my_script <cluster_uuid>/<script id>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
provider "elasticstack" {
2+
elasticsearch {}
3+
}
4+
5+
resource "elasticstack_elasticsearch_script" "my_script" {
6+
script_id = "my_script"
7+
lang = "painless"
8+
source = "Math.log(_score * 2) + params['my_modifier']"
9+
context = "score"
10+
}
11+
12+
resource "elasticstack_elasticsearch_script" "my_search_template" {
13+
script_id = "my_search_template"
14+
lang = "mustache"
15+
source = jsonencode({
16+
query = {
17+
match = {
18+
message = "{{query_string}}"
19+
}
20+
}
21+
from = "{{from}}"
22+
size = "{{size}}"
23+
})
24+
params = jsonencode({
25+
query_string = "My query string"
26+
})
27+
}

internal/clients/cluster.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,58 @@ func (a *ApiClient) GetElasticsearchSettings(ctx context.Context) (map[string]in
175175
}
176176
return clusterSettings, diags
177177
}
178+
179+
func (a *ApiClient) GetElasticsearchScript(ctx context.Context, id string) (*models.Script, diag.Diagnostics) {
180+
res, err := a.es.GetScript(id, a.es.GetScript.WithContext(ctx))
181+
if err != nil {
182+
return nil, diag.FromErr(err)
183+
}
184+
defer res.Body.Close()
185+
if res.StatusCode == http.StatusNotFound {
186+
return nil, nil
187+
}
188+
if diags := utils.CheckError(res, fmt.Sprintf("Unable to get stored script: %s", id)); diags.HasError() {
189+
return nil, diags
190+
}
191+
var scriptResponse struct {
192+
Script *models.Script `json:"script"`
193+
}
194+
if err := json.NewDecoder(res.Body).Decode(&scriptResponse); err != nil {
195+
return nil, diag.FromErr(err)
196+
}
197+
198+
return scriptResponse.Script, nil
199+
}
200+
201+
func (a *ApiClient) PutElasticsearchScript(ctx context.Context, script *models.Script) diag.Diagnostics {
202+
req := struct {
203+
Script *models.Script `json:"script"`
204+
}{
205+
script,
206+
}
207+
scriptBytes, err := json.Marshal(req)
208+
if err != nil {
209+
return diag.FromErr(err)
210+
}
211+
res, err := a.es.PutScript(script.ID, bytes.NewReader(scriptBytes), a.es.PutScript.WithContext(ctx), a.es.PutScript.WithScriptContext(script.Context))
212+
if err != nil {
213+
return diag.FromErr(err)
214+
}
215+
defer res.Body.Close()
216+
if diags := utils.CheckError(res, "Unable to put stored script"); diags.HasError() {
217+
return diags
218+
}
219+
return nil
220+
}
221+
222+
func (a *ApiClient) DeleteElasticsearchScript(ctx context.Context, id string) diag.Diagnostics {
223+
res, err := a.es.DeleteScript(id, a.es.DeleteScript.WithContext(ctx))
224+
if err != nil {
225+
return diag.FromErr(err)
226+
}
227+
defer res.Body.Close()
228+
if diags := utils.CheckError(res, fmt.Sprintf("Unable to delete script: %s", id)); diags.HasError() {
229+
return diags
230+
}
231+
return nil
232+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package cluster
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
9+
"github.com/elastic/terraform-provider-elasticstack/internal/models"
10+
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
11+
"github.com/hashicorp/terraform-plugin-log/tflog"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
14+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
15+
)
16+
17+
func ResourceScript() *schema.Resource {
18+
scriptSchema := map[string]*schema.Schema{
19+
"script_id": {
20+
Description: "Identifier for the stored script. Must be unique within the cluster.",
21+
Type: schema.TypeString,
22+
Required: true,
23+
ForceNew: true,
24+
},
25+
"lang": {
26+
Description: "Script language. For search templates, use `mustache`.",
27+
Type: schema.TypeString,
28+
Required: true,
29+
ValidateFunc: validation.StringInSlice([]string{"painless", "expression", "mustache", "java"}, false),
30+
},
31+
"source": {
32+
Description: "For scripts, a string containing the script. For search templates, an object containing the search template.",
33+
Type: schema.TypeString,
34+
Required: true,
35+
},
36+
"params": {
37+
Description: "Parameters for the script or search template.",
38+
Type: schema.TypeString,
39+
Optional: true,
40+
DiffSuppressFunc: utils.DiffJsonSuppress,
41+
ValidateFunc: validation.StringIsJSON,
42+
},
43+
"context": {
44+
Description: "Context in which the script or search template should run.",
45+
Type: schema.TypeString,
46+
Optional: true,
47+
},
48+
}
49+
utils.AddConnectionSchema(scriptSchema)
50+
51+
return &schema.Resource{
52+
Description: "Creates or updates a stored script or search template. See https://www.elastic.co/guide/en/elasticsearch/reference/current/create-stored-script-api.html",
53+
54+
CreateContext: resourceScriptPut,
55+
UpdateContext: resourceScriptPut,
56+
ReadContext: resourceScriptRead,
57+
DeleteContext: resourceScriptDelete,
58+
59+
Importer: &schema.ResourceImporter{
60+
StateContext: schema.ImportStatePassthroughContext,
61+
},
62+
63+
Schema: scriptSchema,
64+
}
65+
}
66+
67+
func resourceScriptRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
68+
var diags diag.Diagnostics
69+
client, err := clients.NewApiClient(d, meta)
70+
if err != nil {
71+
return diag.FromErr(err)
72+
}
73+
74+
id := d.Id()
75+
compId, diags := clients.CompositeIdFromStr(id)
76+
if diags.HasError() {
77+
return diags
78+
}
79+
80+
script, diags := client.GetElasticsearchScript(ctx, compId.ResourceId)
81+
if script == nil && diags == nil {
82+
tflog.Warn(ctx, fmt.Sprintf(`Script "%s" not found, removing from state`, compId.ResourceId))
83+
d.SetId("")
84+
}
85+
if diags.HasError() {
86+
return diags
87+
}
88+
89+
if err := d.Set("script_id", compId.ResourceId); err != nil {
90+
return diag.FromErr(err)
91+
}
92+
if err := d.Set("lang", script.Language); err != nil {
93+
return diag.FromErr(err)
94+
}
95+
if err := d.Set("source", script.Source); err != nil {
96+
return diag.FromErr(err)
97+
}
98+
99+
return diags
100+
}
101+
102+
func resourceScriptPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
103+
client, err := clients.NewApiClient(d, meta)
104+
if err != nil {
105+
return diag.FromErr(err)
106+
}
107+
108+
scriptID := d.Get("script_id").(string)
109+
id, diags := client.ID(ctx, scriptID)
110+
if diags.HasError() {
111+
return diags
112+
}
113+
114+
script := models.Script{
115+
ID: scriptID,
116+
Language: d.Get("lang").(string),
117+
Source: d.Get("source").(string),
118+
}
119+
if paramsJSON, ok := d.GetOk("params"); ok {
120+
var params map[string]interface{}
121+
bytes := []byte(paramsJSON.(string))
122+
err = json.Unmarshal(bytes, &params)
123+
if err != nil {
124+
return diag.FromErr(err)
125+
}
126+
script.Params = params
127+
}
128+
if scriptContext, ok := d.GetOk("context"); ok {
129+
script.Context = scriptContext.(string)
130+
}
131+
if diags := client.PutElasticsearchScript(ctx, &script); diags.HasError() {
132+
return diags
133+
}
134+
135+
d.SetId(id.String())
136+
return resourceScriptRead(ctx, d, meta)
137+
}
138+
139+
func resourceScriptDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
140+
client, err := clients.NewApiClient(d, meta)
141+
if err != nil {
142+
return diag.FromErr(err)
143+
}
144+
145+
compId, diags := clients.CompositeIdFromStr(d.Id())
146+
if diags.HasError() {
147+
return diags
148+
}
149+
return client.DeleteElasticsearchScript(ctx, compId.ResourceId)
150+
}

0 commit comments

Comments
 (0)