diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 3c9bff54..3bbe3e07 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -22,17 +22,24 @@ jobs: - name: Setup Go uses: actions/setup-go@v2 with: - go-version: "1.17" + go-version: "1.18" - name: Setup Elasticsearch run: | set -e docker-compose up & until $(curl --output /dev/null --silent --head --fail -u elastic:changeme http://localhost:9200); do sleep 5; done curl -XPOST -u elastic:changeme http://localhost:9200/_license/start_trial?acknowledge=true + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest + args: --timeout 600s - name: Run build run: make build - name: Run acceptance tests - run: ELASTICSEARCH_URLS="http://localhost:9200" ELASTICSEARCH_USERNAME="elastic" ELASTICSEARCH_PASSWORD="changeme" make testacc + run: | + mv es/resource_elasticsearch_license_test.go es/resource_elasticsearch_license_test.go.disable + ELASTICSEARCH_URLS="http://localhost:9200" ELASTICSEARCH_USERNAME="elastic" ELASTICSEARCH_PASSWORD="changeme" make testacc - name: Run acceptance test on license resource run: | set -e @@ -58,7 +65,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18 - name: Import GPG key id: import_gpg uses: paultyng/ghaction-import-gpg@v2.1.0 diff --git a/Makefile b/Makefile index 96d151bc..053677ae 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ gen: rm -f aws/internal/keyvaluetags/*_gen.go go generate ./... -test: fmtcheck +test: fmt fmtcheck go test $(TEST) -timeout=30s -parallel=4 testacc: fmt fmtcheck @@ -93,7 +93,7 @@ trial-license: curl -XPOST -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} ${ELASTICSEARCH_URLS}/_license/start_trial?acknowledge=true start-pods: clean-pods - kubectl run elasticsearch --image docker.elastic.co/elasticsearch/elasticsearch:8.0.1 --port "9200" --expose --env "cluster.name=test" --env "discovery.type=single-node" --env "ELASTIC_PASSWORD=changeme" --env "xpack.security.enabled=true" --env "ES_JAVA_OPTS=-Xms512m -Xmx512m" --env "path.repo=/tmp" --limits "cpu=500m,memory=1024Mi" + kubectl run elasticsearch --image docker.elastic.co/elasticsearch/elasticsearch:8.4.1 --port "9200" --expose --env "cluster.name=test" --env "discovery.type=single-node" --env "ELASTIC_PASSWORD=changeme" --env "xpack.security.enabled=true" --env "ES_JAVA_OPTS=-Xms512m -Xmx512m" --env "path.repo=/tmp" --limits "cpu=500m,memory=1024Mi" echo "Waiting for Elasticsearch availability" until curl -s http://elasticsearch:9200 | grep -q 'missing authentication credentials'; do sleep 30; done; echo "Enable trial license" diff --git a/docker-compose.yml b/docker-compose.yml index 2a76820b..dbbc2bba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3" services: elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:8.0.1 + image: docker.elastic.co/elasticsearch/elasticsearch:8.4.1 hostname: elasticsearch environment: cluster.name: test diff --git a/docs/index.md b/docs/index.md index 6186ad1a..fa54cd7c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -51,3 +51,6 @@ provider "elasticsearch" { - [elasticsearch_snapshot_repository](resources/elasticsearch_snapshot_repository.md) - [elasticsearch_snapshot_lifecycle_policy](resources/elasticsearch_snapshot_lifecycle_policy.md) - [elasticsearch_watcher](resources/elasticsearch_watcher.md) +- [elasticsearch_data_stream](resources/elasticsearch_data_stream.md) +- [elasticsearch_ingest_pipeline](resources/elasticsearch_ingest_pipeline.md) +- [elasticsearch_transform](resources/elasticsearch_transform.md) diff --git a/docs/resources/elasticsearch_data_stream.md b/docs/resources/elasticsearch_data_stream.md new file mode 100644 index 00000000..c2f05be2 --- /dev/null +++ b/docs/resources/elasticsearch_data_stream.md @@ -0,0 +1,28 @@ +# elasticsearch_data_stream + +This resource permit to manage the index data stream in Elasticsearch. +You can see the API documentation: https://www.elastic.co/guide/en/elasticsearch/reference/current/data-stream-apis.html + +***Supported Elasticsearch version:*** + - v8 + +## Example Usage + +It will create data stream index. + +> You need to have index template with `data_sream` that match your index before to create data stream index. + +```tf +resource elasticsearch_data_stream "test" { + name = "terraform-test" +} +``` + +## Argument Reference + +***The following arguments are supported:*** + - **name**: (required) The data stream index name. + +## Attribute Reference + +NA \ No newline at end of file diff --git a/docs/resources/elasticsearch_index_component_template.md b/docs/resources/elasticsearch_index_component_template.md index cb2dfef2..e17b9de0 100644 --- a/docs/resources/elasticsearch_index_component_template.md +++ b/docs/resources/elasticsearch_index_component_template.md @@ -5,6 +5,7 @@ You can see the API documentation: https://www.elastic.co/guide/en/elasticsearch ***Supported Elasticsearch version:*** - v7 + - v8 ## Example Usage diff --git a/docs/resources/elasticsearch_index_lifecycle_policy.md b/docs/resources/elasticsearch_index_lifecycle_policy.md index 28734eae..dc6f2378 100644 --- a/docs/resources/elasticsearch_index_lifecycle_policy.md +++ b/docs/resources/elasticsearch_index_lifecycle_policy.md @@ -6,6 +6,7 @@ You can see the API documentation: https://www.elastic.co/guide/en/elasticsearch ***Supported Elasticsearch version:*** - v6 - v7 + - v8 ## Example Usage diff --git a/docs/resources/elasticsearch_index_template.md b/docs/resources/elasticsearch_index_template.md index aab5f48c..4f484c0b 100644 --- a/docs/resources/elasticsearch_index_template.md +++ b/docs/resources/elasticsearch_index_template.md @@ -5,6 +5,7 @@ You can see the API documentation: https://www.elastic.co/guide/en/elasticsearch ***Supported Elasticsearch version:*** - v7 + - v8 ## Example Usage diff --git a/docs/resources/elasticsearch_index_template_legacy.md b/docs/resources/elasticsearch_index_template_legacy.md index db6b349b..71dbf406 100644 --- a/docs/resources/elasticsearch_index_template_legacy.md +++ b/docs/resources/elasticsearch_index_template_legacy.md @@ -6,6 +6,7 @@ You can see the API documentation: https://www.elastic.co/guide/en/elasticsearch ***Supported Elasticsearch version:*** - v6 - v7 + - v8 ## Example Usage diff --git a/docs/resources/elasticsearch_ingest_pipeline.md b/docs/resources/elasticsearch_ingest_pipeline.md new file mode 100644 index 00000000..70eacb98 --- /dev/null +++ b/docs/resources/elasticsearch_ingest_pipeline.md @@ -0,0 +1,41 @@ +# elasticsearch_ingest_pipeline Resource Source + +This resource permit to manage the ingest pipeline in Elasticsearch. +You can see the API documentation: https://www.elastic.co/guide/en/elasticsearch/reference/current/ingest-apis.html + +***Supported Elasticsearch version:*** + - v8 + +## Example Usage + +It will create ingest pipeline. + +```tf +resource elasticsearch_ingest_pipeline "test" { + name = "terraform-test" + pipeline = < 0 { + globalB, err := json.Marshal(role.Global) + if err != nil { + return err + } + global = string(globalB) + } + if err = d.Set("global", global); err != nil { + return err + } + if err = d.Set("run_as", role.RunAs); err != nil { + return err + } + + flattenMetdata, err := convertInterfaceToJsonString(role.Metadata) if err != nil { return err } - d.Set("metadata", flattenMetdata) + if err = d.Set("metadata", flattenMetdata); err != nil { + return err + } log.Infof("Read role %s successfully", id) @@ -233,8 +202,8 @@ func resourceElasticsearchSecurityRoleRead(d *schema.ResourceData, meta interfac } // resourceElasticsearchSecurityRoleUpdate update existing role in Elasticsearch -func resourceElasticsearchSecurityRoleUpdate(d *schema.ResourceData, meta interface{}) error { - err := createRole(d, meta) +func resourceElasticsearchSecurityRoleUpdate(d *schema.ResourceData, meta interface{}) (err error) { + err = createRole(d, meta) if err != nil { return err } @@ -245,35 +214,16 @@ func resourceElasticsearchSecurityRoleUpdate(d *schema.ResourceData, meta interf } // resourceElasticsearchSecurityRoleDelete delete existing role in Elasticsearch -func resourceElasticsearchSecurityRoleDelete(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSecurityRoleDelete(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() log.Debugf("Role id: %s", id) - client := meta.(*elastic.Client) - res, err := client.API.Security.DeleteRole( - id, - client.API.Security.DeleteRole.WithContext(context.Background()), - client.API.Security.DeleteRole.WithPretty(), - ) - - if err != nil { + client := meta.(eshandler.ElasticsearchHandler) + if err = client.RoleDelete(id); err != nil { return err } - defer res.Body.Close() - - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Role %s not found - removing from state", id) - log.Warnf("Role %s not found - removing from state", id) - d.SetId("") - return nil - - } - return errors.Errorf("Error when delete role %s: %s", id, res.String()) - } - d.SetId("") log.Infof("Deleted role %s successfully", id) @@ -281,14 +231,8 @@ func resourceElasticsearchSecurityRoleDelete(d *schema.ResourceData, meta interf } -// Print Role object as Json string -func (r *RoleSpec) String() string { - json, _ := json.Marshal(r) - return string(json) -} - // createRole create or update role in Elasticsearch -func createRole(d *schema.ResourceData, meta interface{}) error { +func createRole(d *schema.ResourceData, meta interface{}) (err error) { name := d.Get("name").(string) indices := buildRolesIndicesPermissions(d.Get("indices").(*schema.Set).List()) applications := buildRolesApplicationPrivileges(d.Get("applications").(*schema.Set).List()) @@ -297,47 +241,33 @@ func createRole(d *schema.ResourceData, meta interface{}) error { runAs := convertArrayInterfaceToArrayString(d.Get("run_as").(*schema.Set).List()) metadata := optionalInterfaceJSON(d.Get("metadata").(string)) - role := &RoleSpec{ + client := meta.(eshandler.ElasticsearchHandler) + + data := &eshandler.XPackSecurityRole{ Cluster: cluster, Applications: applications, Indices: indices, RunAs: runAs, - Global: global, - Metadata: metadata, } - log.Debug("Name: ", name) - log.Debug("Role: ", role) - data, err := json.Marshal(role) - if err != nil { - return err + if global != nil { + data.Global = global.(map[string]interface{}) } - - client := meta.(*elastic.Client) - res, err := client.API.Security.PutRole( - name, - bytes.NewReader(data), - client.API.Security.PutRole.WithContext(context.Background()), - client.API.Security.PutRole.WithPretty(), - ) - - if err != nil { - return err + if metadata != nil { + data.Metadata = metadata.(map[string]interface{}) } - defer res.Body.Close() - - if res.IsError() { - return errors.Errorf("Error when add role %s: %s\ndata: %s", name, res.String(), string(data)) + if err = client.RoleUpdate(name, data); err != nil { + return err } return nil } // buildRolesIndicesPermissions convert list to list of RoleIndicesPermissions objects -func buildRolesIndicesPermissions(raws []interface{}) []RoleIndicesPermissions { +func buildRolesIndicesPermissions(raws []interface{}) []eshandler.XPackSecurityIndicesPermissions { - rolesIndicesPermissions := make([]RoleIndicesPermissions, 0, len(raws)) + rolesIndicesPermissions := make([]eshandler.XPackSecurityIndicesPermissions, 0, len(raws)) for _, raw := range raws { m := raw.(map[string]interface{}) @@ -345,10 +275,10 @@ func buildRolesIndicesPermissions(raws []interface{}) []RoleIndicesPermissions { if len(m["names"].(*schema.Set).List()) == 0 { continue } - roleIndicesPermisions := RoleIndicesPermissions{ + roleIndicesPermisions := eshandler.XPackSecurityIndicesPermissions{ Names: convertArrayInterfaceToArrayString(m["names"].(*schema.Set).List()), Privileges: convertArrayInterfaceToArrayString(m["privileges"].(*schema.Set).List()), - Query: optionalInterfaceJSON(m["query"].(string)), + Query: m["query"].(string), FieldSecurity: optionalInterfaceJSON(m["field_security"].(string)), } @@ -360,8 +290,8 @@ func buildRolesIndicesPermissions(raws []interface{}) []RoleIndicesPermissions { } // buildRolesApplicationPrivileges convert list to list of RoleApplicationPrivileges objects -func buildRolesApplicationPrivileges(raws []interface{}) []RoleApplicationPrivileges { - rolesApplicationPrivileges := make([]RoleApplicationPrivileges, 0, len(raws)) +func buildRolesApplicationPrivileges(raws []interface{}) []eshandler.XPackSecurityApplicationPrivileges { + rolesApplicationPrivileges := make([]eshandler.XPackSecurityApplicationPrivileges, 0, len(raws)) for _, raw := range raws { m := raw.(map[string]interface{}) @@ -370,7 +300,7 @@ func buildRolesApplicationPrivileges(raws []interface{}) []RoleApplicationPrivil if m["application"].(string) == "" { continue } - roleApplicationPrivileges := RoleApplicationPrivileges{ + roleApplicationPrivileges := eshandler.XPackSecurityApplicationPrivileges{ Application: m["application"].(string), Privileges: convertArrayInterfaceToArrayString(m["privileges"].(*schema.Set).List()), Resources: convertArrayInterfaceToArrayString(m["resources"].(*schema.Set).List()), @@ -383,7 +313,7 @@ func buildRolesApplicationPrivileges(raws []interface{}) []RoleApplicationPrivil return rolesApplicationPrivileges } -func flattenIndiceMapping(indice RoleIndicesPermissions) (map[string]interface{}, error) { +func flattenIndiceMapping(indice eshandler.XPackSecurityIndicesPermissions) (map[string]interface{}, error) { if reflect.ValueOf(indice).IsZero() { return nil, nil } @@ -392,7 +322,7 @@ func flattenIndiceMapping(indice RoleIndicesPermissions) (map[string]interface{} tfMap["names"] = indice.Names tfMap["privileges"] = indice.Privileges - if indice.Query != nil { + if indice.Query != "" { queryB, err := json.Marshal(indice.Query) if err != nil { return nil, err @@ -411,7 +341,7 @@ func flattenIndiceMapping(indice RoleIndicesPermissions) (map[string]interface{} return tfMap, nil } -func flattenIndicesMapping(indices []RoleIndicesPermissions) ([]interface{}, error) { +func flattenIndicesMapping(indices []eshandler.XPackSecurityIndicesPermissions) ([]interface{}, error) { if indices == nil { return nil, nil } @@ -430,7 +360,7 @@ func flattenIndicesMapping(indices []RoleIndicesPermissions) ([]interface{}, err } -func flattenApplicationMapping(application RoleApplicationPrivileges) map[string]interface{} { +func flattenApplicationMapping(application eshandler.XPackSecurityApplicationPrivileges) map[string]interface{} { if reflect.ValueOf(application).IsZero() { return nil } @@ -443,7 +373,7 @@ func flattenApplicationMapping(application RoleApplicationPrivileges) map[string return tfMap } -func flattenApplicationsMapping(applications []RoleApplicationPrivileges) []interface{} { +func flattenApplicationsMapping(applications []eshandler.XPackSecurityApplicationPrivileges) []interface{} { if applications == nil { return nil } diff --git a/es/resource_elasticsearch_security_role_mapping.go b/es/resource_elasticsearch_security_role_mapping.go index 6858c706..549b2393 100644 --- a/es/resource_elasticsearch_security_role_mapping.go +++ b/es/resource_elasticsearch_security_role_mapping.go @@ -7,29 +7,14 @@ package es import ( - "bytes" - "context" - "encoding/json" "fmt" - "io/ioutil" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/pkg/errors" + olivere "github.com/olivere/elastic/v7" log "github.com/sirupsen/logrus" ) -// RoleMapping is role mapping object returned by API -type RoleMapping map[string]*RoleMappingSpec - -// RoleMappingSpec is role mapping object -type RoleMappingSpec struct { - Roles []string `json:"roles"` - Enabled bool `json:"enabled"` - Rules interface{} `json:"rules,omitempty"` - Metadata interface{} `json:"metadata,omitempty"` -} - // resourceElasticsearchSecurityRoleMapping handle role mapping API call func resourceElasticsearchSecurityRoleMapping() *schema.Resource { return &schema.Resource{ @@ -39,7 +24,7 @@ func resourceElasticsearchSecurityRoleMapping() *schema.Resource { Delete: resourceElasticsearchSecurityRoleMappingDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ @@ -75,10 +60,10 @@ func resourceElasticsearchSecurityRoleMapping() *schema.Resource { } // resourceElasticsearchSecurityRoleMappingCreate create new role mapping in Elasticsearch -func resourceElasticsearchSecurityRoleMappingCreate(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSecurityRoleMappingCreate(d *schema.ResourceData, meta interface{}) (err error) { name := d.Get("name").(string) - err := createRoleMapping(d, meta) + err = createRoleMapping(d, meta) if err != nil { return err } @@ -89,67 +74,56 @@ func resourceElasticsearchSecurityRoleMappingCreate(d *schema.ResourceData, meta } // resourceElasticsearchSecurityRoleMappingRead read existing role mapping in Elasticsearch -func resourceElasticsearchSecurityRoleMappingRead(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSecurityRoleMappingRead(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() log.Debugf("Role mapping id: %s", id) - client := meta.(*elastic.Client) - res, err := client.API.Security.GetRoleMapping( - client.API.Security.GetRoleMapping.WithContext(context.Background()), - client.API.Security.GetRoleMapping.WithPretty(), - client.API.Security.GetRoleMapping.WithName(id), - ) + client := meta.(eshandler.ElasticsearchHandler) + + rm, err := client.RoleMappingGet(id) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Role mapping %s not found. Removing from state\n", id) - log.Warnf("Role mapping %s not found. Removing from state\n", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when get role mapping %s: %s", id, res.String()) + if rm == nil { + fmt.Printf("[WARN] Role mapping %s not found. Removing from state\n", id) + log.Warnf("Role mapping %s not found. Removing from state\n", id) + d.SetId("") + return nil + } + if err = d.Set("name", id); err != nil { + return err } - b, err := ioutil.ReadAll(res.Body) - if err != nil { + if err = d.Set("enabled", rm.Enabled); err != nil { return err } - - log.Debugf("Get role mapping %s successfully:\n%s", id, string(b)) - roleMapping := make(RoleMapping) - err = json.Unmarshal(b, &roleMapping) - if err != nil { + if err = d.Set("roles", rm.Roles); err != nil { return err } - - log.Debugf("Role mapping %+v", roleMapping) - - d.Set("name", id) - d.Set("enabled", roleMapping[id].Enabled) - d.Set("roles", roleMapping[id].Roles) - flattenRules, err := convertInterfaceToJsonString(roleMapping[id].Rules) + flattenRules, err := convertInterfaceToJsonString(rm.Rules) if err != nil { return err } - d.Set("rules", flattenRules) - flattenMetadata, err := convertInterfaceToJsonString(roleMapping[id].Metadata) + if err = d.Set("rules", flattenRules); err != nil { + return err + } + flattenMetadata, err := convertInterfaceToJsonString(rm.Metadata) if err != nil { return err } - d.Set("metadata", flattenMetadata) + if err = d.Set("metadata", flattenMetadata); err != nil { + return err + } log.Infof("Read role mapping %s successfully", id) return nil } // resourceElasticsearchSecurityRoleMappingUpdate update existing role mapping in Elasticsearch -func resourceElasticsearchSecurityRoleMappingUpdate(d *schema.ResourceData, meta interface{}) error { - err := createRoleMapping(d, meta) +func resourceElasticsearchSecurityRoleMappingUpdate(d *schema.ResourceData, meta interface{}) (err error) { + err = createRoleMapping(d, meta) if err != nil { return err } @@ -160,35 +134,16 @@ func resourceElasticsearchSecurityRoleMappingUpdate(d *schema.ResourceData, meta } // resourceElasticsearchSecurityRoleMappingDelete delete existing role mapping in Elasticsearch -func resourceElasticsearchSecurityRoleMappingDelete(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSecurityRoleMappingDelete(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() log.Debugf("Role mapping id: %s", id) - client := meta.(*elastic.Client) - res, err := client.API.Security.DeleteRoleMapping( - id, - client.API.Security.DeleteRoleMapping.WithContext(context.Background()), - client.API.Security.DeleteRoleMapping.WithPretty(), - ) - - if err != nil { + client := meta.(eshandler.ElasticsearchHandler) + if err = client.RoleMappingDelete(id); err != nil { return err } - defer res.Body.Close() - - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Role mapping %s not found - removing from state", id) - log.Warnf("Role mapping %s not found - removing from state", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when delete role mapping %s: %s", id, res.String()) - - } - d.SetId("") log.Infof("Deleted role mapping %s successfully", id) @@ -197,50 +152,34 @@ func resourceElasticsearchSecurityRoleMappingDelete(d *schema.ResourceData, meta } // createRoleMapping create or update role mapping -func createRoleMapping(d *schema.ResourceData, meta interface{}) error { +func createRoleMapping(d *schema.ResourceData, meta interface{}) (err error) { name := d.Get("name").(string) enabled := d.Get("enabled").(bool) roles := convertArrayInterfaceToArrayString(d.Get("roles").(*schema.Set).List()) - rules := optionalInterfaceJSON(d.Get("rules").(string)) - metadata := optionalInterfaceJSON(d.Get("metadata").(string)) + rulesStr := d.Get("rules").(string) + metadataStr := d.Get("metadata").(string) - roleMapping := &RoleMappingSpec{ - Enabled: enabled, - Roles: roles, - Rules: rules, - Metadata: metadata, - } - log.Debug("Name: ", name) - log.Debug("RoleMapping: ", roleMapping) + client := meta.(eshandler.ElasticsearchHandler) - data, err := json.Marshal(roleMapping) + rules, err := convertRawJsonTopMapString(rulesStr) if err != nil { return err } - - client := meta.(*elastic.Client) - res, err := client.API.Security.PutRoleMapping( - name, - bytes.NewReader(data), - client.API.Security.PutRoleMapping.WithContext(context.Background()), - client.API.Security.PutRoleMapping.WithPretty(), - ) - + metadata, err := convertRawJsonTopMapString(metadataStr) if err != nil { return err } - defer res.Body.Close() + data := &olivere.XPackSecurityRoleMapping{ + Enabled: enabled, + Roles: roles, + Rules: rules, + Metadata: metadata, + } - if res.IsError() { - return errors.Errorf("Error when add role mapping %s: %s", name, res.String()) + if err = client.RoleMappingUpdate(name, data); err != nil { + return err } return nil } - -// Print role mapping as Json string -func (r *RoleMappingSpec) String() string { - json, _ := json.Marshal(r) - return string(json) -} diff --git a/es/resource_elasticsearch_security_role_mapping_test.go b/es/resource_elasticsearch_security_role_mapping_test.go index 99ea8055..019392b1 100644 --- a/es/resource_elasticsearch_security_role_mapping_test.go +++ b/es/resource_elasticsearch_security_role_mapping_test.go @@ -1,11 +1,10 @@ package es import ( - "context" "fmt" "testing" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/pkg/errors" @@ -53,18 +52,13 @@ func testCheckElasticsearchSecurityRoleMappingExists(name string) resource.TestC meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.Security.GetRoleMapping( - client.API.Security.GetRoleMapping.WithContext(context.Background()), - client.API.Security.GetRoleMapping.WithPretty(), - client.API.Security.GetRoleMapping.WithName(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + rm, err := client.RoleMappingGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - return errors.Errorf("Error when get role mapping %s: %s", rs.Primary.ID, res.String()) + if rm == nil { + return errors.Errorf("Role mapping %s not found", rs.Primary.ID) } return nil @@ -76,26 +70,23 @@ func testCheckElasticsearchSecurityRoleMappingDestroy(s *terraform.State) error if rs.Type != "elasticsearch_role_mapping" { continue } + if rs.Primary.ID != "test" { + continue + } meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.Security.GetRoleMapping( - client.API.Security.GetRoleMapping.WithContext(context.Background()), - client.API.Security.GetRoleMapping.WithPretty(), - client.API.Security.GetRoleMapping.WithName(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + rm, err := client.RoleMappingGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - return nil - } + if rm != nil { + return fmt.Errorf("Role mapping %q still exists", rs.Primary.ID) } - return fmt.Errorf("Role mapping %q still exists", rs.Primary.ID) + return nil + } return nil diff --git a/es/resource_elasticsearch_security_role_test.go b/es/resource_elasticsearch_security_role_test.go index 9a6f9fc5..46fd879c 100644 --- a/es/resource_elasticsearch_security_role_test.go +++ b/es/resource_elasticsearch_security_role_test.go @@ -1,11 +1,10 @@ package es import ( - "context" "fmt" "testing" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/pkg/errors" @@ -53,18 +52,13 @@ func testCheckElasticsearchSecurityRoleExists(name string) resource.TestCheckFun meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.Security.GetRole( - client.API.Security.GetRole.WithContext(context.Background()), - client.API.Security.GetRole.WithPretty(), - client.API.Security.GetRole.WithName(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + role, err := client.RoleGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - return errors.Errorf("Error when get security role %s: %s", rs.Primary.ID, res.String()) + if role == nil { + return errors.Errorf("Role %s not found", rs.Primary.ID) } return nil @@ -76,26 +70,23 @@ func testCheckElasticsearchSecurityRoleDestroy(s *terraform.State) error { if rs.Type != "elasticsearch_role" { continue } + if rs.Primary.ID != "test" { + continue + } meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.Security.GetRole( - client.API.Security.GetRole.WithContext(context.Background()), - client.API.Security.GetRole.WithPretty(), - client.API.Security.GetRole.WithName(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + role, err := client.RoleGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - return nil - } + if role != nil { + return fmt.Errorf("Security role %q still exists", rs.Primary.ID) } - return fmt.Errorf("Security role %q still exists", rs.Primary.ID) + return nil + } return nil diff --git a/es/resource_elasticsearch_security_user.go b/es/resource_elasticsearch_security_user.go index acb6a760..af239de9 100644 --- a/es/resource_elasticsearch_security_user.go +++ b/es/resource_elasticsearch_security_user.go @@ -7,32 +7,14 @@ package es import ( - "bytes" - "context" - "encoding/json" "fmt" - "io/ioutil" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/pkg/errors" + olivere "github.com/olivere/elastic/v7" log "github.com/sirupsen/logrus" ) -// User Json object returned by API -type User map[string]*UserSpec - -// UserSpec is the user object -type UserSpec struct { - Enabled bool `json:"enabled"` - Email string `json:"email"` - FullName string `json:"full_name"` - Password string `json:"password,omitempty"` - PasswordHash string `json:"password_hash,omitempty"` - Roles []string `json:"roles"` - Metadata interface{} `json:"metadata,omitempty"` -} - // resourceElasticsearchSecurityUser handle the user API call func resourceElasticsearchSecurityUser() *schema.Resource { return &schema.Resource{ @@ -42,7 +24,7 @@ func resourceElasticsearchSecurityUser() *schema.Resource { Delete: resourceElasticsearchSecurityUserDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ @@ -89,10 +71,10 @@ func resourceElasticsearchSecurityUser() *schema.Resource { } // resourceElasticsearchSecurityUserCreate create new user in Elasticsearch -func resourceElasticsearchSecurityUserCreate(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSecurityUserCreate(d *schema.ResourceData, meta interface{}) (err error) { username := d.Get("username").(string) - err := createUser(d, meta, false) + err = createUser(d, meta) if err != nil { return err } @@ -104,57 +86,48 @@ func resourceElasticsearchSecurityUserCreate(d *schema.ResourceData, meta interf } // resourceElasticsearchSecurityUserRead read existing user in Elasticsearch -func resourceElasticsearchSecurityUserRead(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSecurityUserRead(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() log.Debugf("User id: %s", id) - client := meta.(*elastic.Client) - res, err := client.API.Security.GetUser( - client.API.Security.GetUser.WithContext(context.Background()), - client.API.Security.GetUser.WithPretty(), - client.API.Security.GetUser.WithUsername(id), - ) + client := meta.(eshandler.ElasticsearchHandler) + + user, err := client.UserGet(id) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] User %s not found - removing from state", id) - log.Warnf("User %s not found - removing from state", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when get user %s: %s", id, res.String()) + if user == nil { + fmt.Printf("[WARN] User %s not found - removing from state", id) + log.Warnf("User %s not found - removing from state", id) + d.SetId("") + return nil + } + if err = d.Set("username", id); err != nil { + return err } - b, err := ioutil.ReadAll(res.Body) - if err != nil { + if err = d.Set("enabled", user.Enabled); err != nil { return err } - - log.Debugf("Get user %s successfully:\n%s", id, string(b)) - user := make(User) - err = json.Unmarshal(b, &user) - if err != nil { + if err = d.Set("email", user.Email); err != nil { + return err + } + if err = d.Set("full_name", user.Fullname); err != nil { + return err + } + if err = d.Set("roles", user.Roles); err != nil { return err } - log.Debugf("User %+v", user) - - d.Set("username", id) - d.Set("enabled", user[id].Enabled) - d.Set("email", user[id].Email) - d.Set("full_name", user[id].FullName) - d.Set("roles", user[id].Roles) - - flattenMetadata, err := convertInterfaceToJsonString(user[id].Metadata) + flattenMetadata, err := convertInterfaceToJsonString(user.Metadata) if err != nil { return err } - d.Set("metadata", flattenMetadata) + if err = d.Set("metadata", flattenMetadata); err != nil { + return err + } log.Infof("Read user %s successfully", id) @@ -162,91 +135,53 @@ func resourceElasticsearchSecurityUserRead(d *schema.ResourceData, meta interfac } // resourceElasticsearchSecurityUserUpdate update existing user in Elasticsearch -func resourceElasticsearchSecurityUserUpdate(d *schema.ResourceData, meta interface{}) error { - +func resourceElasticsearchSecurityUserUpdate(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() + enabled := d.Get("enabled").(bool) + email := d.Get("email").(string) + fullName := d.Get("full_name").(string) + password := d.Get("password").(string) + passwordHash := d.Get("password_hash").(string) + roles := convertArrayInterfaceToArrayString(d.Get("roles").(*schema.Set).List()) + metadata := optionalInterfaceJSON(d.Get("metadata").(string)) - // Use change password API if needed - if d.HasChange("password") || d.HasChange("password_hash") { - - payload := make(map[string]string) - if d.HasChange("password") { - payload["password"] = d.Get("password").(string) - } else { - payload["password_hash"] = d.Get("password_hash").(string) - } - - data, err := json.Marshal(payload) - if err != nil { - return err - } - - client := meta.(*elastic.Client) - res, err := client.API.Security.ChangePassword( - bytes.NewReader(data), - client.API.Security.ChangePassword.WithUsername(id), - client.API.Security.ChangePassword.WithContext(context.Background()), - client.API.Security.ChangePassword.WithPretty(), - ) - - if err != nil { - return err - } - - defer res.Body.Close() - - if res.IsError() { - return errors.Errorf("Error when change password for user %s: %s", id, res.String()) - } - - log.Infof("Updated user password %s successfully", d.Id()) + client := meta.(eshandler.ElasticsearchHandler) + data := &olivere.XPackSecurityPutUserRequest{ + Enabled: enabled, + Email: email, + FullName: fullName, + Roles: roles, + } + if metadata != nil { + data.Metadata = metadata.(map[string]interface{}) } - // Use user API for other fiedls - if d.HasChange("enabled") || d.HasChange("email") || d.HasChange("full_name") || d.HasChange("roles") || d.HasChange("metadata") { - err := createUser(d, meta, true) - if err != nil { - return err - } - - log.Infof("Updated user %s successfully", d.Id()) + // Provide password only if it change + if d.HasChange("password") || d.HasChange("password_hash") { + data.Password = password + data.PasswordHash = passwordHash + } + if err = client.UserUpdate(id, data); err != nil { + return err } return resourceElasticsearchSecurityUserRead(d, meta) } // resourceElasticsearchSecurityUserDelete delete existing user in Elasticsearch -func resourceElasticsearchSecurityUserDelete(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSecurityUserDelete(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() log.Debugf("User id: %s", id) - client := meta.(*elastic.Client) - res, err := client.API.Security.DeleteUser( - id, - client.API.Security.DeleteUser.WithContext(context.Background()), - client.API.Security.DeleteUser.WithPretty(), - ) + client := meta.(eshandler.ElasticsearchHandler) - if err != nil { + if err = client.UserDelete(id); err != nil { return err } - defer res.Body.Close() - - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] User %s not found - removing from state", id) - log.Warnf("User %s not found - removing from state", id) - d.SetId("") - return nil - - } - return errors.Errorf("Error when delete user %s: %s", id, res.String()) - } - d.SetId("") log.Infof("Deleted user %s successfully", id) @@ -254,14 +189,8 @@ func resourceElasticsearchSecurityUserDelete(d *schema.ResourceData, meta interf } -// Print user object as Json string -func (r *UserSpec) String() string { - json, _ := json.Marshal(r) - return string(json) -} - // createUser create or update user in Elasticsearch -func createUser(d *schema.ResourceData, meta interface{}, isUpdate bool) error { +func createUser(d *schema.ResourceData, meta interface{}) (err error) { username := d.Get("username").(string) enabled := d.Get("enabled").(bool) email := d.Get("email").(string) @@ -271,44 +200,23 @@ func createUser(d *schema.ResourceData, meta interface{}, isUpdate bool) error { roles := convertArrayInterfaceToArrayString(d.Get("roles").(*schema.Set).List()) metadata := optionalInterfaceJSON(d.Get("metadata").(string)) - user := &UserSpec{ - Enabled: enabled, - Email: email, - FullName: fullName, - Roles: roles, - Metadata: metadata, - } + client := meta.(eshandler.ElasticsearchHandler) - if isUpdate == false { - user.Password = password - user.PasswordHash = passwordHash + data := &olivere.XPackSecurityPutUserRequest{ + Enabled: enabled, + Email: email, + FullName: fullName, + Roles: roles, + Password: password, + PasswordHash: passwordHash, } - - log.Debug("Username: ", username) - log.Debug("User: ", user) - - data, err := json.Marshal(user) - if err != nil { - return err + if metadata != nil { + data.Metadata = metadata.(map[string]interface{}) } - client := meta.(*elastic.Client) - res, err := client.API.Security.PutUser( - username, - bytes.NewReader(data), - client.API.Security.PutUser.WithContext(context.Background()), - client.API.Security.PutUser.WithPretty(), - ) - - if err != nil { + if err = client.UserCreate(username, data); err != nil { return err } - defer res.Body.Close() - - if res.IsError() { - return errors.Errorf("Error when add user %s: %s", username, res.String()) - } - return nil } diff --git a/es/resource_elasticsearch_security_user_test.go b/es/resource_elasticsearch_security_user_test.go index a828c78c..86b0048e 100644 --- a/es/resource_elasticsearch_security_user_test.go +++ b/es/resource_elasticsearch_security_user_test.go @@ -1,11 +1,10 @@ package es import ( - "context" "fmt" "testing" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/pkg/errors" @@ -54,18 +53,13 @@ func testCheckElasticsearchSecurityUserExists(name string) resource.TestCheckFun meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.Security.GetUser( - client.API.Security.GetUser.WithContext(context.Background()), - client.API.Security.GetUser.WithPretty(), - client.API.Security.GetUser.WithUsername(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + user, err := client.UserGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - return errors.Errorf("Error when get user %s: %s", rs.Primary.ID, res.String()) + if user == nil { + return errors.Errorf("User %s not found", rs.Primary.ID) } return nil @@ -77,26 +71,22 @@ func testCheckElasticsearchSecurityUserDestroy(s *terraform.State) error { if rs.Type != "elasticsearch_user" { continue } + if rs.Primary.ID != "test" { + continue + } meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.Security.GetUser( - client.API.Security.GetUser.WithContext(context.Background()), - client.API.Security.GetUser.WithPretty(), - client.API.Security.GetUser.WithUsername(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + user, err := client.UserGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - return nil - } + if user != nil { + return fmt.Errorf("User %q still exists", rs.Primary.ID) } - return fmt.Errorf("User %q still exists", rs.Primary.ID) + return nil } return nil diff --git a/es/resource_elasticsearch_snapshot_lifecycle_policy.go b/es/resource_elasticsearch_snapshot_lifecycle_policy.go index 44f8d676..0453aff7 100644 --- a/es/resource_elasticsearch_snapshot_lifecycle_policy.go +++ b/es/resource_elasticsearch_snapshot_lifecycle_policy.go @@ -6,35 +6,14 @@ package es import ( - "bytes" - "context" "encoding/json" "fmt" - "io/ioutil" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) -// SnapshotLifecyclePolicy object returned by API -type SnapshotLifecyclePolicy map[string]*SnapshotLifecyclePolicyGet - -// SnapshotLifecyclePolicySpec is the snapshot lifecycle policy object -type SnapshotLifecyclePolicySpec struct { - Schedule string `json:"schedule"` - Name string `json:"name"` - Repository string `json:"repository"` - Configs interface{} `json:"config,omitempty"` - Retention interface{} `json:"retention,omitempty"` -} - -// SnapshotLifecyclePolicyGet is the policy -type SnapshotLifecyclePolicyGet struct { - Policy *SnapshotLifecyclePolicySpec `json:"policy"` -} - // resourceElasticsearchSnapshotLifecyclePolicy handle the snapshot lifecycle policy API call func resourceElasticsearchSnapshotLifecyclePolicy() *schema.Resource { return &schema.Resource{ @@ -44,7 +23,7 @@ func resourceElasticsearchSnapshotLifecyclePolicy() *schema.Resource { Delete: resourceElasticsearchSnapshotLifecyclePolicyDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ @@ -66,9 +45,14 @@ func resourceElasticsearchSnapshotLifecyclePolicy() *schema.Resource { Required: true, }, "configs": { - Type: schema.TypeString, - Optional: true, - DiffSuppressFunc: suppressEquivalentJSON, + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { + return suppressEquivalentJSONWithExclude(k, oldValue, newValue, d, map[string]any{ + "ignore_unavailable": false, + "include_global_state": false, + }) + }, }, "retention": { Type: schema.TypeString, @@ -80,11 +64,11 @@ func resourceElasticsearchSnapshotLifecyclePolicy() *schema.Resource { } // resourceElasticsearchSnapshotLifecyclePolicyCreate create snapshot lifecycle policy -func resourceElasticsearchSnapshotLifecyclePolicyCreate(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSnapshotLifecyclePolicyCreate(d *schema.ResourceData, meta interface{}) (err error) { name := d.Get("name").(string) - err := createSnapshotLifecyclePolicy(d, meta) + err = createSnapshotLifecyclePolicy(d, meta) if err != nil { return err } @@ -93,8 +77,8 @@ func resourceElasticsearchSnapshotLifecyclePolicyCreate(d *schema.ResourceData, } // resourceElasticsearchSnapshotLifecyclePolicyUpdate update snapshot lifecycle policy -func resourceElasticsearchSnapshotLifecyclePolicyUpdate(d *schema.ResourceData, meta interface{}) error { - err := createSnapshotLifecyclePolicy(d, meta) +func resourceElasticsearchSnapshotLifecyclePolicyUpdate(d *schema.ResourceData, meta interface{}) (err error) { + err = createSnapshotLifecyclePolicy(d, meta) if err != nil { return err } @@ -102,152 +86,101 @@ func resourceElasticsearchSnapshotLifecyclePolicyUpdate(d *schema.ResourceData, } // resourceElasticsearchSnapshotLifecyclePolicyRead read snapshot lifecycle policy -func resourceElasticsearchSnapshotLifecyclePolicyRead(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSnapshotLifecyclePolicyRead(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() - client := meta.(*elastic.Client) - res, err := client.API.SlmGetLifecycle( - client.API.SlmGetLifecycle.WithContext(context.Background()), - client.API.SlmGetLifecycle.WithPretty(), - client.API.SlmGetLifecycle.WithPolicyID(id), - ) - if err != nil { - return err - } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Snapshot lifecycle policy %s not found - removing from state", id) - log.Warnf("Snapshot lifecycle policy %s not found - removing from state", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when get snapshot lifecycle policy %s: %s", id, res.String()) - - } - b, err := ioutil.ReadAll(res.Body) - if err != nil { - return err - } - - log.Debugf("Get snapshot lifecycle policy successfully:\n%s", string(b)) - - snapshotLifecyclePolicy := make(SnapshotLifecyclePolicy) - err = json.Unmarshal(b, &snapshotLifecyclePolicy) + client := meta.(eshandler.ElasticsearchHandler) + policy, err := client.SLMGet(id) if err != nil { return err } - log.Debugf("SnapshotLifecyclePolicy object %+v", snapshotLifecyclePolicy) - - // Manage bug https://github.com/elastic/elasticsearch/issues/47664 - if len(snapshotLifecyclePolicy) == 0 { + if policy == nil { fmt.Printf("[WARN] Snapshot lifecycle policy %s not found - removing from state", id) log.Warnf("Snapshot lifecycle policy %s not found - removing from state", id) d.SetId("") return nil } - d.Set("name", id) - d.Set("snapshot_name", snapshotLifecyclePolicy[id].Policy.Name) - d.Set("schedule", snapshotLifecyclePolicy[id].Policy.Schedule) - d.Set("repository", snapshotLifecyclePolicy[id].Policy.Repository) + if err = d.Set("name", id); err != nil { + return err + } + if err = d.Set("snapshot_name", policy.Name); err != nil { + return err + } + if err = d.Set("schedule", policy.Schedule); err != nil { + return err + } + if err = d.Set("repository", policy.Repository); err != nil { + return err + } - flattenConfigs, err := convertInterfaceToJsonString(snapshotLifecyclePolicy[id].Policy.Configs) + flattenConfigs, err := convertInterfaceToJsonString(policy.Config) if err != nil { return err } - d.Set("configs", flattenConfigs) + if err = d.Set("configs", flattenConfigs); err != nil { + return err + } - flattenRetention, err := convertInterfaceToJsonString(snapshotLifecyclePolicy[id].Policy.Retention) + flattenRetention, err := convertInterfaceToJsonString(policy.Retention) if err != nil { return err } - d.Set("retention", flattenRetention) + if err = d.Set("retention", flattenRetention); err != nil { + return err + } return nil } // resourceElasticsearchSnapshotLifecyclePolicyDelete delete snapshot lifecycle policy -func resourceElasticsearchSnapshotLifecyclePolicyDelete(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSnapshotLifecyclePolicyDelete(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() - client := meta.(*elastic.Client) - res, err := client.API.SlmDeleteLifecycle( - id, - client.API.SlmDeleteLifecycle.WithContext(context.Background()), - client.API.SlmDeleteLifecycle.WithPretty(), - ) - - if err != nil { + client := meta.(eshandler.ElasticsearchHandler) + if err = client.SLMDelete(id); err != nil { return err } - defer res.Body.Close() - - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Snapshot lifecycle policy %s not found - removing from state", id) - log.Warnf("Snapshot lifecycle policy %s not found - removing from state", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when delete snapshot lifecycle policy %s: %s", id, res.String()) - - } - d.SetId("") return nil } // createSnapshotLifecyclePolicy permit to create or update snapshot lifecycle policy -func createSnapshotLifecyclePolicy(d *schema.ResourceData, meta interface{}) error { +func createSnapshotLifecyclePolicy(d *schema.ResourceData, meta interface{}) (err error) { name := d.Get("name").(string) snapshotName := d.Get("snapshot_name").(string) schedule := d.Get("schedule").(string) repository := d.Get("repository").(string) - configs := optionalInterfaceJSON(d.Get("configs").(string)) - retention := optionalInterfaceJSON(d.Get("retention").(string)) + configStr := d.Get("configs").(string) + retentionStr := d.Get("retention").(string) - snapshotLifecyclePolicy := &SnapshotLifecyclePolicySpec{ - Name: snapshotName, - Schedule: schedule, - Repository: repository, - Configs: configs, - Retention: retention, - } + client := meta.(eshandler.ElasticsearchHandler) - b, err := json.Marshal(snapshotLifecyclePolicy) - if err != nil { + config := &eshandler.ElasticsearchSLMConfig{} + if err = json.Unmarshal([]byte(configStr), config); err != nil { return err } - client := meta.(*elastic.Client) - - res, err := client.API.SlmPutLifecycle( - name, - client.API.SlmPutLifecycle.WithBody(bytes.NewReader(b)), - client.API.SlmPutLifecycle.WithContext(context.Background()), - client.API.SlmPutLifecycle.WithPretty(), - ) - - if err != nil { + retention := &eshandler.ElasticsearchSLMRetention{} + if err = json.Unmarshal([]byte(retentionStr), retention); err != nil { return err } - defer res.Body.Close() + data := &eshandler.SnapshotLifecyclePolicySpec{ + Name: snapshotName, + Schedule: schedule, + Repository: repository, + Config: *config, + Retention: retention, + } - if res.IsError() { - return errors.Errorf("Error when add snapshot lifecycle policy %s: %s", name, res.String()) + if err = client.SLMUpdate(name, data); err != nil { + return err } return nil } - -// Print snapshot lifecycle policy object as Json string -func (r *SnapshotLifecyclePolicySpec) String() string { - json, _ := json.Marshal(r) - return string(json) -} diff --git a/es/resource_elasticsearch_snapshot_lifecycle_policy_test.go b/es/resource_elasticsearch_snapshot_lifecycle_policy_test.go index 40328f49..8b9ede2b 100644 --- a/es/resource_elasticsearch_snapshot_lifecycle_policy_test.go +++ b/es/resource_elasticsearch_snapshot_lifecycle_policy_test.go @@ -1,13 +1,10 @@ package es import ( - "context" - "encoding/json" "fmt" - "io/ioutil" "testing" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -56,33 +53,16 @@ func testCheckElasticsearchSnapshotLifecyclePolicyExists(name string) resource.T meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.SlmGetLifecycle( - client.API.SlmGetLifecycle.WithContext(context.Background()), - client.API.SlmGetLifecycle.WithPretty(), - client.API.SlmGetLifecycle.WithPolicyID(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + policy, err := client.SLMGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - return errors.Errorf("Error when get snapshot lifecycle policy %s: %s", rs.Primary.ID, res.String()) + if policy == nil { + return errors.Errorf("SLM policy %s not found", rs.Primary.ID) } // Manage Bug https://github.com/elastic/elasticsearch/issues/47664 - b, err := ioutil.ReadAll(res.Body) - if err != nil { - return err - } - snapshotLifecyclePolicy := make(SnapshotLifecyclePolicy) - err = json.Unmarshal(b, &snapshotLifecyclePolicy) - if err != nil { - return err - } - if len(snapshotLifecyclePolicy) == 0 { - return errors.Errorf("Error when get snapshot lifecycle policy %s: Policy not found", rs.Primary.ID) - } return nil } @@ -93,39 +73,23 @@ func testCheckElasticsearchSnapshotLifecyclePolicyDestroy(s *terraform.State) er if rs.Type != "elasticsearch_snapshot_lifecycle_policy" { continue } + if rs.Primary.ID != "test" { + continue + } meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.SlmGetLifecycle( - client.API.SlmGetLifecycle.WithContext(context.Background()), - client.API.SlmGetLifecycle.WithPretty(), - client.API.SlmGetLifecycle.WithPolicyID(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + policy, err := client.SLMGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - return nil - } - } - // Manage Bug https://github.com/elastic/elasticsearch/issues/47664 - b, err := ioutil.ReadAll(res.Body) - if err != nil { - return err - } - snapshotLifecyclePolicy := make(SnapshotLifecyclePolicy) - err = json.Unmarshal(b, &snapshotLifecyclePolicy) - if err != nil { - return err - } - if len(snapshotLifecyclePolicy) == 0 { - return nil + if policy != nil { + return fmt.Errorf("Snapshot lifecycle policy %q still exists", rs.Primary.ID) } - return fmt.Errorf("Snapshot lifecycle policy %q still exists", rs.Primary.ID) + return nil + } return nil diff --git a/es/resource_elasticsearch_snapshot_repository.go b/es/resource_elasticsearch_snapshot_repository.go index f0be873a..4b0ca4b5 100644 --- a/es/resource_elasticsearch_snapshot_repository.go +++ b/es/resource_elasticsearch_snapshot_repository.go @@ -7,27 +7,14 @@ package es import ( - "bytes" - "context" - "encoding/json" "fmt" - "io/ioutil" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/pkg/errors" + olivere "github.com/olivere/elastic/v7" log "github.com/sirupsen/logrus" ) -// SnapshotRepository object returned by API -type SnapshotRepository map[string]*SnapshotRepositorySpec - -// SnapshotRepositorySpec is the repository object -type SnapshotRepositorySpec struct { - Type string `json:"type"` - Settings map[string]string `json:"settings"` -} - // resourceElasticsearchSnapshotRepository handle the snapshot repository API call func resourceElasticsearchSnapshotRepository() *schema.Resource { return &schema.Resource{ @@ -37,7 +24,7 @@ func resourceElasticsearchSnapshotRepository() *schema.Resource { Delete: resourceElasticsearchSnapshotRepositoryDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ @@ -62,11 +49,11 @@ func resourceElasticsearchSnapshotRepository() *schema.Resource { } // resourceElasticsearchSnapshotRepositoryCreate create snapshot repository -func resourceElasticsearchSnapshotRepositoryCreate(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSnapshotRepositoryCreate(d *schema.ResourceData, meta interface{}) (err error) { name := d.Get("name").(string) - err := createSnapshotRepository(d, meta) + err = createSnapshotRepository(d, meta) if err != nil { return err } @@ -75,8 +62,8 @@ func resourceElasticsearchSnapshotRepositoryCreate(d *schema.ResourceData, meta } // resourceElasticsearchSnapshotRepositoryUpdate update the snapshot repository -func resourceElasticsearchSnapshotRepositoryUpdate(d *schema.ResourceData, meta interface{}) error { - err := createSnapshotRepository(d, meta) +func resourceElasticsearchSnapshotRepositoryUpdate(d *schema.ResourceData, meta interface{}) (err error) { + err = createSnapshotRepository(d, meta) if err != nil { return err } @@ -84,123 +71,67 @@ func resourceElasticsearchSnapshotRepositoryUpdate(d *schema.ResourceData, meta } // resourceElasticsearchSnapshotRepositoryRead read the sanpshot repository -func resourceElasticsearchSnapshotRepositoryRead(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSnapshotRepositoryRead(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() - client := meta.(*elastic.Client) - res, err := client.API.Snapshot.GetRepository( - client.API.Snapshot.GetRepository.WithContext(context.Background()), - client.API.Snapshot.GetRepository.WithPretty(), - client.API.Snapshot.GetRepository.WithRepository(id), - ) + client := meta.(eshandler.ElasticsearchHandler) + + repo, err := client.SnapshotRepositoryGet(id) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Snapshot repository %s not found - removing from state", id) - log.Warnf("Snapshot repository %s not found - removing from state", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when get snapshot repository %s: %s", id, res.String()) + if repo == nil { + fmt.Printf("[WARN] Snapshot repository %s not found - removing from state", id) + log.Warnf("Snapshot repository %s not found - removing from state", id) + d.SetId("") + return nil } - b, err := ioutil.ReadAll(res.Body) - if err != nil { + + if err = d.Set("name", id); err != nil { return err } - - log.Debugf("Get Snapshot repository successfully:\n%s", string(b)) - - snapshotRepository := make(SnapshotRepository) - err = json.Unmarshal(b, &snapshotRepository) - if err != nil { + if err = d.Set("type", repo.Type); err != nil { + return err + } + if err = d.Set("settings", repo.Settings); err != nil { return err } - - d.Set("name", id) - d.Set("type", snapshotRepository[id].Type) - d.Set("settings", snapshotRepository[id].Settings) return nil } // resourceElasticsearchSnapshotRepositoryDelete delete the snapshot repository -func resourceElasticsearchSnapshotRepositoryDelete(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchSnapshotRepositoryDelete(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() - client := meta.(*elastic.Client) - res, err := client.API.Snapshot.DeleteRepository( - []string{id}, - client.API.Snapshot.DeleteRepository.WithContext(context.Background()), - client.API.Snapshot.DeleteRepository.WithPretty(), - ) - - if err != nil { + client := meta.(eshandler.ElasticsearchHandler) + if err = client.SnapshotRepositoryDelete(id); err != nil { return err } - defer res.Body.Close() - - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Snapshot repository %s not found - removing from state", id) - log.Warnf("Snapshot repository %s not found - removing from state", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when delete snapshot repository %s: %s", id, res.String()) - - } - d.SetId("") return nil } // createSnapshotRepository create or update snapshot repository -func createSnapshotRepository(d *schema.ResourceData, meta interface{}) error { +func createSnapshotRepository(d *schema.ResourceData, meta interface{}) (err error) { name := d.Get("name").(string) snapshotType := d.Get("type").(string) - settings := convertMapInterfaceToMapString(d.Get("settings").(map[string]interface{})) + settings := d.Get("settings").(map[string]interface{}) - snapshotRepository := &SnapshotRepositorySpec{ + client := meta.(eshandler.ElasticsearchHandler) + + data := &olivere.SnapshotRepositoryMetaData{ Type: snapshotType, Settings: settings, } - b, err := json.Marshal(snapshotRepository) - if err != nil { + if err = client.SnapshotRepositoryUpdate(name, data); err != nil { return err } - client := meta.(*elastic.Client) - - res, err := client.API.Snapshot.CreateRepository( - name, - bytes.NewReader(b), - client.API.Snapshot.CreateRepository.WithContext(context.Background()), - client.API.Snapshot.CreateRepository.WithPretty(), - ) - - if err != nil { - return err - } - - defer res.Body.Close() - - if res.IsError() { - return errors.Errorf("Error when add snapshot repository %s: %s", name, res.String()) - } - return nil } - -// Print snapshot repository object as Json string -func (r *SnapshotRepositorySpec) String() string { - json, _ := json.Marshal(r) - return string(json) -} diff --git a/es/resource_elasticsearch_snapshot_repository_test.go b/es/resource_elasticsearch_snapshot_repository_test.go index 82cb5159..726025ad 100644 --- a/es/resource_elasticsearch_snapshot_repository_test.go +++ b/es/resource_elasticsearch_snapshot_repository_test.go @@ -1,11 +1,10 @@ package es import ( - "context" "fmt" "testing" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/pkg/errors" @@ -53,18 +52,13 @@ func testCheckElasticsearchSnapshotRepositoryExists(name string) resource.TestCh meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.Snapshot.GetRepository( - client.API.Snapshot.GetRepository.WithContext(context.Background()), - client.API.Snapshot.GetRepository.WithPretty(), - client.API.Snapshot.GetRepository.WithRepository(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + repo, err := client.SnapshotRepositoryGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - return errors.Errorf("Error when get snapshot repository %s: %s", rs.Primary.ID, res.String()) + if repo == nil { + return errors.Errorf("Repository %s nout found", rs.Primary.ID) } return nil @@ -76,26 +70,22 @@ func testCheckElasticsearchSnapshotRepositoryDestroy(s *terraform.State) error { if rs.Type != "elasticsearch_snapshot_repository" { continue } + if rs.Primary.ID != "test" { + continue + } meta := testAccProvider.Meta() - client := meta.(*elastic.Client) - res, err := client.API.Snapshot.GetRepository( - client.API.Snapshot.GetRepository.WithContext(context.Background()), - client.API.Snapshot.GetRepository.WithPretty(), - client.API.Snapshot.GetRepository.WithRepository(rs.Primary.ID), - ) + client := meta.(eshandler.ElasticsearchHandler) + repo, err := client.SnapshotRepositoryGet(rs.Primary.ID) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - return nil - } + if repo != nil { + return fmt.Errorf("Snapshot repository %q still exists", rs.Primary.ID) } - return fmt.Errorf("Snapshot repository %q still exists", rs.Primary.ID) + return nil } return nil diff --git a/es/resource_elasticsearch_transform.go b/es/resource_elasticsearch_transform.go index 97b00d1d..e544fc59 100644 --- a/es/resource_elasticsearch_transform.go +++ b/es/resource_elasticsearch_transform.go @@ -6,15 +6,11 @@ package es import ( - "context" "encoding/json" "fmt" - "io/ioutil" - "strings" - elastic "github.com/elastic/go-elasticsearch/v8" + eshandler "github.com/disaster37/es-handler/v8" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -69,9 +65,10 @@ func resourceElasticsearchTransform() *schema.Resource { Create: resourceElasticsearchTransformCreate, Read: resourceElasticsearchTransformRead, Delete: resourceElasticsearchTransformDelete, + Update: resourceElasticsearchTransformUpdate, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ @@ -82,7 +79,6 @@ func resourceElasticsearchTransform() *schema.Resource { }, "transform": { Type: schema.TypeString, - ForceNew: true, Required: true, DiffSuppressFunc: diffSuppressTransform, }, @@ -91,9 +87,9 @@ func resourceElasticsearchTransform() *schema.Resource { } // resourceElasticsearchTransformCreate create transform -func resourceElasticsearchTransformCreate(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchTransformCreate(d *schema.ResourceData, meta interface{}) (err error) { - err := createTransform(d, meta) + err = createTransform(d, meta) if err != nil { return err } @@ -102,8 +98,8 @@ func resourceElasticsearchTransformCreate(d *schema.ResourceData, meta interface } // resourceElasticsearchTransformUpdate update transform -func resourceElasticsearchTransformUpdate(d *schema.ResourceData, meta interface{}) error { - err := createTransform(d, meta) +func resourceElasticsearchTransformUpdate(d *schema.ResourceData, meta interface{}) (err error) { + err = createTransform(d, meta) if err != nil { return err } @@ -111,110 +107,65 @@ func resourceElasticsearchTransformUpdate(d *schema.ResourceData, meta interface } // resourceElasticsearchTransformRead read transform -func resourceElasticsearchTransformRead(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchTransformRead(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() - client := meta.(*elastic.Client) - res, err := client.API.TransformGetTransform( - client.API.TransformGetTransform.WithTransformID(id), - client.API.TransformGetTransform.WithContext(context.Background()), - client.API.TransformGetTransform.WithPretty(), - ) + client := meta.(eshandler.ElasticsearchHandler) + transform, err := client.TransformGet(id) if err != nil { return err } - defer res.Body.Close() - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Transform %s not found - removing from state", id) - log.Warnf("Transform %s not found - removing from state", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when get transform %s: %s", id, res.String()) - } - b, err := ioutil.ReadAll(res.Body) - if err != nil { - return err - } - transform := TransformGetResponse{} - if err := json.Unmarshal(b, &transform); err != nil { - return err - } - - if len(transform.Transforms) == 0 { + if transform == nil { fmt.Printf("[WARN] Transform %s not found - removing from state", id) log.Warnf("Transform %s not found - removing from state", id) d.SetId("") return nil } - transformJSON, err := json.Marshal(transform.Transforms[0]) + transformJSON, err := json.Marshal(transform) if err != nil { return err } - log.Debugf("Get transform %s successfully:%+v", id, transformJSON) - d.Set("name", d.Id()) - d.Set("transform", string(transformJSON)) + log.Debugf("Get transform %s successfully:%+v", id, string(transformJSON)) + if err = d.Set("name", d.Id()); err != nil { + return err + } + if err = d.Set("transform", string(transformJSON)); err != nil { + return err + } return nil } // resourceElasticsearchTransformDelete delete transform -func resourceElasticsearchTransformDelete(d *schema.ResourceData, meta interface{}) error { +func resourceElasticsearchTransformDelete(d *schema.ResourceData, meta interface{}) (err error) { id := d.Id() - client := meta.(*elastic.Client) - res, err := client.API.TransformDeleteTransform( - id, - client.API.TransformDeleteTransform.WithContext(context.Background()), - client.API.TransformDeleteTransform.WithPretty(), - ) + client := meta.(eshandler.ElasticsearchHandler) - if err != nil { + if err = client.TransformDelete(id); err != nil { return err } - defer res.Body.Close() - - if res.IsError() { - if res.StatusCode == 404 { - fmt.Printf("[WARN] Transform %s not found - removing from state", id) - log.Warnf("Transform %s not found - removing from state", id) - d.SetId("") - return nil - } - return errors.Errorf("Error when delete transform %s: %s", id, res.String()) - - } - d.SetId("") return nil } // createTransform create or update transform -func createTransform(d *schema.ResourceData, meta interface{}) error { +func createTransform(d *schema.ResourceData, meta interface{}) (err error) { name := d.Get("name").(string) transform := d.Get("transform").(string) - client := meta.(*elastic.Client) - res, err := client.API.TransformPutTransform( - strings.NewReader(transform), - name, - client.API.TransformPutTransform.WithContext(context.Background()), - client.API.TransformPutTransform.WithPretty(), - ) + client := meta.(eshandler.ElasticsearchHandler) - if err != nil { + data := &eshandler.Transform{} + if err = json.Unmarshal([]byte(transform), data); err != nil { return err } - - defer res.Body.Close() - - if res.IsError() { - return errors.Errorf("Error when add transform %s: %s", name, res.String()) + if err = client.TransformUpdate(name, data); err != nil { + return err } return nil diff --git a/es/resource_elasticsearch_transform_test.go b/es/resource_elasticsearch_transform_test.go new file mode 100644 index 00000000..aff41ae0 --- /dev/null +++ b/es/resource_elasticsearch_transform_test.go @@ -0,0 +1,199 @@ +package es + +import ( + "fmt" + "testing" + + eshandler "github.com/disaster37/es-handler/v8" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/pkg/errors" +) + +func TestAccElasticsearchTransform(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testCheckElasticsearchTransformDestroy, + Steps: []resource.TestStep{ + { + Config: testElasticsearchTransform, + Check: resource.ComposeTestCheckFunc( + testCheckElasticsearchTransformExists("elasticsearch_transform.test"), + ), + }, + { + Config: testElasticsearchTransformUpdate, + Check: resource.ComposeTestCheckFunc( + testCheckElasticsearchTransformExists("elasticsearch_transform.test"), + ), + }, + { + ResourceName: "elasticsearch_transform.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckElasticsearchTransformExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No index ID is set") + } + + meta := testAccProvider.Meta() + + client := meta.(eshandler.ElasticsearchHandler) + transform, err := client.TransformGet(rs.Primary.ID) + if err != nil { + return err + } + if transform == nil { + return errors.Errorf("Transform %s not found", rs.Primary.ID) + } + + return nil + } +} + +func testCheckElasticsearchTransformDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "elasticsearch_transform" { + continue + } + if rs.Primary.ID != "test" { + continue + } + + meta := testAccProvider.Meta() + + client := meta.(eshandler.ElasticsearchHandler) + transform, err := client.TransformGet(rs.Primary.ID) + if err != nil { + return err + } + if transform != nil { + return fmt.Errorf("Transform %q still exists", rs.Primary.ID) + } + + return nil + } + + return nil +} + +var testElasticsearchTransform = ` +resource "elasticsearch_transform" "test" { + name = "terraform-test-transform" + transform = <