Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/resources/elasticsearch_alias.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "elasticstack_elasticsearch_alias Resource - terraform-provider-elasticstack"
subcategory: "Elasticsearch"
description: |-
Manages an Elasticsearch alias. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html
---

# elasticstack_elasticsearch_alias (Resource)

Manages an Elasticsearch alias. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) The alias name.

### Optional

- `read_indices` (Attributes Set) Set of read indices for the alias. (see [below for nested schema](#nestedatt--read_indices))
- `write_index` (Attributes) The write index for the alias. Only one write index is allowed per alias. (see [below for nested schema](#nestedatt--write_index))

### Read-Only

- `id` (String) Generated ID of the alias resource.

<a id="nestedatt--read_indices"></a>
### Nested Schema for `read_indices`

Required:

- `name` (String) Name of the read index.

Optional:

- `filter` (String) Query used to limit documents the alias can access.
- `index_routing` (String) Value used to route indexing operations to a specific shard.
- `is_hidden` (Boolean) If true, the alias is hidden.
- `routing` (String) Value used to route indexing and search operations to a specific shard.
- `search_routing` (String) Value used to route search operations to a specific shard.


<a id="nestedatt--write_index"></a>
### Nested Schema for `write_index`

Required:

- `name` (String) Name of the write index.

Optional:

- `filter` (String) Query used to limit documents the alias can access.
- `index_routing` (String) Value used to route indexing operations to a specific shard.
- `is_hidden` (Boolean) If true, the alias is hidden.
- `routing` (String) Value used to route indexing and search operations to a specific shard.
- `search_routing` (String) Value used to route search operations to a specific shard.
240 changes: 240 additions & 0 deletions internal/clients/elasticsearch/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,246 @@ func DeleteDataStreamLifecycle(ctx context.Context, apiClient *clients.ApiClient
return nil
}

func GetAlias(ctx context.Context, apiClient *clients.ApiClient, aliasName string) (map[string]models.Index, fwdiags.Diagnostics) {
esClient, err := apiClient.GetESClient()
if err != nil {
return nil, fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}

res, err := esClient.Indices.GetAlias(
esClient.Indices.GetAlias.WithName(aliasName),
esClient.Indices.GetAlias.WithContext(ctx),
)
if err != nil {
return nil, fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}
defer res.Body.Close()

if res.StatusCode == http.StatusNotFound {
return nil, nil
}

diags := diagutil.CheckError(res, fmt.Sprintf("Unable to get alias '%s'", aliasName))
if diagutil.FrameworkDiagsFromSDK(diags).HasError() {
return nil, diagutil.FrameworkDiagsFromSDK(diags)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CheckErrorFromFW will return the right type directly.

Suggested change
diags := diagutil.CheckError(res, fmt.Sprintf("Unable to get alias '%s'", aliasName))
if diagutil.FrameworkDiagsFromSDK(diags).HasError() {
return nil, diagutil.FrameworkDiagsFromSDK(diags)
}
diags := diagutil.CheckErrorFromFW(res, fmt.Sprintf("Unable to get alias '%s'", aliasName))
if diags.HasError() {
return nil, diags
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 3c8a8b2. Updated to use CheckErrorFromFW directly as suggested.


indices := make(map[string]models.Index)
if err := json.NewDecoder(res.Body).Decode(&indices); err != nil {
return nil, fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}

return indices, nil
}

func PutAlias(ctx context.Context, apiClient *clients.ApiClient, aliasName string, indices []string, alias *models.IndexAlias) fwdiags.Diagnostics {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is now unused and can be deleted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed unused PutAlias function in commit 3c8a8b2.

esClient, err := apiClient.GetESClient()
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}

// Build the request body for index aliases API
var actions []map[string]interface{}

for _, index := range indices {
addAction := map[string]interface{}{
"add": map[string]interface{}{
"index": index,
"alias": aliasName,
},
}

// Only include non-empty optional fields in the add action
addActionDetails := addAction["add"].(map[string]interface{})

if alias.Filter != nil {
addActionDetails["filter"] = alias.Filter
}
if alias.IndexRouting != "" {
addActionDetails["index_routing"] = alias.IndexRouting
}
if alias.SearchRouting != "" {
addActionDetails["search_routing"] = alias.SearchRouting
}
if alias.Routing != "" {
addActionDetails["routing"] = alias.Routing
}
if alias.IsHidden {
addActionDetails["is_hidden"] = alias.IsHidden
}
if alias.IsWriteIndex {
addActionDetails["is_write_index"] = alias.IsWriteIndex
}

actions = append(actions, addAction)
}

aliasActions := map[string]interface{}{
"actions": actions,
}

aliasBytes, err := json.Marshal(aliasActions)
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}

res, err := esClient.Indices.UpdateAliases(
bytes.NewReader(aliasBytes),
esClient.Indices.UpdateAliases.WithContext(ctx),
)
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}
defer res.Body.Close()

diags := diagutil.CheckError(res, fmt.Sprintf("Unable to create/update alias '%s'", aliasName))
return diagutil.FrameworkDiagsFromSDK(diags)
}

func DeleteAlias(ctx context.Context, apiClient *clients.ApiClient, aliasName string, indices []string) fwdiags.Diagnostics {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is now unused and can be deleted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed unused DeleteAlias function in commit 3c8a8b2.

esClient, err := apiClient.GetESClient()
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}

// Use UpdateAliases API for deletion to handle multiple indices
aliasActions := map[string]interface{}{
"actions": []map[string]interface{}{
{
"remove": map[string]interface{}{
"indices": indices,
"alias": aliasName,
},
},
},
}

aliasBytes, err := json.Marshal(aliasActions)
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}

res, err := esClient.Indices.UpdateAliases(
bytes.NewReader(aliasBytes),
esClient.Indices.UpdateAliases.WithContext(ctx),
)
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}
defer res.Body.Close()

diags := diagutil.CheckError(res, fmt.Sprintf("Unable to delete alias '%s'", aliasName))
return diagutil.FrameworkDiagsFromSDK(diags)
}

// AliasAction represents a single action in an atomic alias update operation
type AliasAction struct {
Type string // "add" or "remove"
Index string
Alias string
IsWriteIndex bool
Filter map[string]interface{}
IndexRouting string
IsHidden bool
Routing string
SearchRouting string
}

// UpdateAliasesAtomic performs atomic alias updates using multiple actions
func UpdateAliasesAtomic(ctx context.Context, apiClient *clients.ApiClient, actions []AliasAction) fwdiags.Diagnostics {
esClient, err := apiClient.GetESClient()
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}

var aliasActions []map[string]interface{}

for _, action := range actions {
switch action.Type {
case "remove":
aliasActions = append(aliasActions, map[string]interface{}{
"remove": map[string]interface{}{
"index": action.Index,
"alias": action.Alias,
},
})
case "add":
addDetails := map[string]interface{}{
"index": action.Index,
"alias": action.Alias,
}

if action.IsWriteIndex {
addDetails["is_write_index"] = true
}
if action.Filter != nil {
addDetails["filter"] = action.Filter
}
if action.IndexRouting != "" {
addDetails["index_routing"] = action.IndexRouting
}
if action.SearchRouting != "" {
addDetails["search_routing"] = action.SearchRouting
}
if action.Routing != "" {
addDetails["routing"] = action.Routing
}
if action.IsHidden {
addDetails["is_hidden"] = action.IsHidden
}

aliasActions = append(aliasActions, map[string]interface{}{
"add": addDetails,
})
}
}

requestBody := map[string]interface{}{
"actions": aliasActions,
}

aliasBytes, err := json.Marshal(requestBody)
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}

res, err := esClient.Indices.UpdateAliases(
bytes.NewReader(aliasBytes),
esClient.Indices.UpdateAliases.WithContext(ctx),
)
if err != nil {
return fwdiags.Diagnostics{
fwdiags.NewErrorDiagnostic(err.Error(), err.Error()),
}
}
defer res.Body.Close()

diags := diagutil.CheckError(res, "Unable to update aliases atomically")
return diagutil.FrameworkDiagsFromSDK(diags)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CheckErrorFromFW will return the correct type.

Suggested change
diags := diagutil.CheckError(res, "Unable to update aliases atomically")
return diagutil.FrameworkDiagsFromSDK(diags)
return diagutil.CheckErrorFromFW(res, "Unable to update aliases atomically")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 3c8a8b2. Now using CheckErrorFromFW directly as suggested.

}

func PutIngestPipeline(ctx context.Context, apiClient *clients.ApiClient, pipeline *models.IngestPipeline) diag.Diagnostics {
var diags diag.Diagnostics
pipelineBytes, err := json.Marshal(pipeline)
Expand Down
Loading
Loading