Skip to content

Commit 1df7326

Browse files
committed
Implement script resource
1 parent b357bbf commit 1df7326

File tree

5 files changed

+288
-0
lines changed

5 files changed

+288
-0
lines changed

internal/clients/cluster.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,59 @@ func (a *ApiClient) GetElasticsearchSettings(ctx context.Context) (map[string]in
180180
}
181181
return clusterSettings, diags
182182
}
183+
184+
func (a *ApiClient) GetElasticsearchScript(ctx context.Context, id string) (*models.Script, diag.Diagnostics) {
185+
res, err := a.es.GetScript(id, a.es.GetScript.WithContext(ctx))
186+
if err != nil {
187+
return nil, diag.FromErr(err)
188+
}
189+
defer res.Body.Close()
190+
if res.StatusCode == http.StatusNotFound {
191+
return nil, nil
192+
}
193+
if diags := utils.CheckError(res, fmt.Sprintf("Unable to get the script: %s", id)); diags.HasError() {
194+
return nil, diags
195+
}
196+
type getScriptResponse = struct {
197+
Script *models.Script `json:"script"`
198+
}
199+
var scriptResponse getScriptResponse
200+
if err := json.NewDecoder(res.Body).Decode(&scriptResponse); err != nil {
201+
return nil, diag.FromErr(err)
202+
}
203+
204+
return scriptResponse.Script, nil
205+
}
206+
207+
func (a *ApiClient) PutElasticsearchScript(ctx context.Context, script *models.Script) diag.Diagnostics {
208+
req := struct {
209+
Script *models.Script `json:"script"`
210+
}{
211+
Script: script,
212+
}
213+
scriptBytes, err := json.Marshal(req)
214+
if err != nil {
215+
return diag.FromErr(err)
216+
}
217+
res, err := a.es.PutScript(script.ID, bytes.NewReader(scriptBytes), a.es.PutScript.WithContext(ctx), a.es.PutScript.WithScriptContext(script.Context))
218+
if err != nil {
219+
return diag.FromErr(err)
220+
}
221+
defer res.Body.Close()
222+
if diags := utils.CheckError(res, "Unable to put the script"); diags.HasError() {
223+
return diags
224+
}
225+
return nil
226+
}
227+
228+
func (a *ApiClient) DeleteElasticsearchScript(ctx context.Context, id string) diag.Diagnostics {
229+
res, err := a.es.DeleteScript(id, a.es.DeleteScript.WithContext(ctx))
230+
if err != nil {
231+
return diag.FromErr(err)
232+
}
233+
defer res.Body.Close()
234+
if diags := utils.CheckError(res, fmt.Sprintf("Unable to delete script: %s", id)); diags.HasError() {
235+
return diags
236+
}
237+
return nil
238+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package cluster
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
8+
"github.com/elastic/terraform-provider-elasticstack/internal/models"
9+
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
10+
"github.com/hashicorp/terraform-plugin-log/tflog"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
14+
)
15+
16+
func ResourceScript() *schema.Resource {
17+
scriptSchema := map[string]*schema.Schema{
18+
"script_id": {
19+
Description: "Identifier for the stored script. Must be unique within the cluster.",
20+
Type: schema.TypeString,
21+
Required: true,
22+
ForceNew: true,
23+
},
24+
"lang": {
25+
Description: "Script language. For search templates, use `mustache`. Defaults to `painless`.",
26+
Type: schema.TypeString,
27+
Optional: true,
28+
Default: "painless",
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+
"context": {
37+
Description: "Context in which the script or search template should run.",
38+
Type: schema.TypeString,
39+
Optional: true,
40+
},
41+
}
42+
utils.AddConnectionSchema(scriptSchema)
43+
44+
return &schema.Resource{
45+
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",
46+
47+
CreateContext: resourceScriptPut,
48+
UpdateContext: resourceScriptPut,
49+
ReadContext: resourceScriptRead,
50+
DeleteContext: resourceScriptDelete,
51+
52+
Importer: &schema.ResourceImporter{
53+
StateContext: schema.ImportStatePassthroughContext,
54+
},
55+
56+
Schema: scriptSchema,
57+
}
58+
}
59+
60+
func resourceScriptRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
61+
var diags diag.Diagnostics
62+
client, err := clients.NewApiClient(d, meta)
63+
if err != nil {
64+
return diag.FromErr(err)
65+
}
66+
67+
id := d.Id()
68+
compId, diags := clients.CompositeIdFromStr(id)
69+
if diags.HasError() {
70+
return diags
71+
}
72+
73+
script, diags := client.GetElasticsearchScript(ctx, compId.ResourceId)
74+
if !d.IsNewResource() && script == nil && diags == nil {
75+
tflog.Warn(ctx, fmt.Sprintf(`Script "%s" not found, removing from state`, compId.ResourceId))
76+
d.SetId("")
77+
}
78+
if diags.HasError() {
79+
return diags
80+
}
81+
82+
if err := d.Set("script_id", compId.ResourceId); err != nil {
83+
return diag.FromErr(err)
84+
}
85+
if err := d.Set("lang", script.Language); err != nil {
86+
return diag.FromErr(err)
87+
}
88+
if err := d.Set("source", script.Source); err != nil {
89+
return diag.FromErr(err)
90+
}
91+
92+
return diags
93+
}
94+
95+
func resourceScriptPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
96+
client, err := clients.NewApiClient(d, meta)
97+
if err != nil {
98+
return diag.FromErr(err)
99+
}
100+
101+
scriptID := d.Get("script_id").(string)
102+
id, diags := client.ID(ctx, scriptID)
103+
if diags.HasError() {
104+
return diags
105+
}
106+
107+
script := models.Script{
108+
ID: scriptID,
109+
Language: d.Get("lang").(string),
110+
Source: d.Get("source").(string),
111+
}
112+
if scriptContext, ok := d.GetOk("context"); ok {
113+
script.Context = scriptContext.(string)
114+
}
115+
if diags := client.PutElasticsearchScript(ctx, &script); diags.HasError() {
116+
return diags
117+
}
118+
119+
d.SetId(id.String())
120+
return resourceScriptRead(ctx, d, meta)
121+
}
122+
123+
func resourceScriptDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
124+
client, err := clients.NewApiClient(d, meta)
125+
if err != nil {
126+
return diag.FromErr(err)
127+
}
128+
129+
compId, diags := clients.CompositeIdFromStr(d.Id())
130+
if diags.HasError() {
131+
return diags
132+
}
133+
return client.DeleteElasticsearchScript(ctx, compId.ResourceId)
134+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package cluster_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/elastic/terraform-provider-elasticstack/internal/acctest"
8+
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
9+
sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
12+
)
13+
14+
func TestAccResourceScript(t *testing.T) {
15+
scriptID := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
16+
17+
resource.UnitTest(t, resource.TestCase{
18+
PreCheck: func() { acctest.PreCheck(t) },
19+
CheckDestroy: checkScriptDestroy,
20+
ProviderFactories: acctest.Providers,
21+
Steps: []resource.TestStep{
22+
{
23+
Config: testAccScriptCreate(scriptID),
24+
Check: resource.ComposeTestCheckFunc(
25+
resource.TestCheckResourceAttr("elasticstack_elasticsearch_script.test", "script_id", scriptID),
26+
resource.TestCheckResourceAttr("elasticstack_elasticsearch_script.test", "lang", "painless"),
27+
resource.TestCheckResourceAttr("elasticstack_elasticsearch_script.test", "source", "Math.log(_score * 2) + params['my_modifier']"),
28+
resource.TestCheckResourceAttr("elasticstack_elasticsearch_script.test", "context", "score"),
29+
),
30+
},
31+
{
32+
Config: testAccScriptUpdate(scriptID),
33+
Check: resource.ComposeTestCheckFunc(
34+
resource.TestCheckResourceAttr("elasticstack_elasticsearch_script.test", "script_id", scriptID),
35+
resource.TestCheckResourceAttr("elasticstack_elasticsearch_script.test", "lang", "painless"),
36+
resource.TestCheckResourceAttr("elasticstack_elasticsearch_script.test", "source", "Math.log(_score * 4) + params['my_modifier2']"),
37+
resource.TestCheckNoResourceAttr("elasticstack_elasticsearch_script.test", "context"),
38+
),
39+
},
40+
},
41+
})
42+
}
43+
44+
func testAccScriptCreate(id string) string {
45+
return fmt.Sprintf(`
46+
provider "elasticstack" {
47+
elasticsearch {}
48+
}
49+
50+
resource "elasticstack_elasticsearch_script" "test" {
51+
script_id = "%s"
52+
source = "Math.log(_score * 2) + params['my_modifier']"
53+
context = "score"
54+
}
55+
`, id)
56+
}
57+
58+
func testAccScriptUpdate(id string) string {
59+
return fmt.Sprintf(`
60+
provider "elasticstack" {
61+
elasticsearch {}
62+
}
63+
64+
resource "elasticstack_elasticsearch_script" "test" {
65+
script_id = "%s"
66+
source = "Math.log(_score * 4) + params['my_modifier2']"
67+
}
68+
`, id)
69+
}
70+
71+
func checkScriptDestroy(s *terraform.State) error {
72+
client := acctest.Provider.Meta().(*clients.ApiClient)
73+
74+
for _, rs := range s.RootModule().Resources {
75+
if rs.Type != "elasticstack_elasticsearch_script" {
76+
continue
77+
}
78+
79+
compId, _ := clients.CompositeIdFromStr(rs.Primary.ID)
80+
res, err := client.GetESClient().GetScript(compId.ResourceId)
81+
if err != nil {
82+
return err
83+
}
84+
85+
if res.StatusCode != 404 {
86+
return fmt.Errorf("script (%s) still exists", compId.ResourceId)
87+
}
88+
}
89+
return nil
90+
}

internal/models/models.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,10 @@ type LogstashPipeline struct {
197197
PipelineSettings map[string]interface{} `json:"pipeline_settings"`
198198
Username string `json:"username"`
199199
}
200+
201+
type Script struct {
202+
ID string `json:"-"`
203+
Language string `json:"lang"`
204+
Source string `json:"source"`
205+
Context string `json:"-"`
206+
}

provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ func New(version string) func() *schema.Provider {
135135
"elasticstack_elasticsearch_security_user": security.ResourceUser(),
136136
"elasticstack_elasticsearch_snapshot_lifecycle": cluster.ResourceSlm(),
137137
"elasticstack_elasticsearch_snapshot_repository": cluster.ResourceSnapshotRepository(),
138+
"elasticstack_elasticsearch_script": cluster.ResourceScript(),
138139
},
139140
}
140141

0 commit comments

Comments
 (0)