// Package index provides index-level operations for OpenSearch. package index import ( "context" "encoding/json" "errors" ) var ( // ErrInvalidPolicy is returned when the policy configuration is invalid. ErrInvalidPolicy = errors.New("invalid policy configuration") ) // PolicyManager defines the interface for managing index lifecycle policies. // This interface abstracts both AWS OpenSearch ISM (Index State Management) // and Elasticsearch ILM (Index Lifecycle Management) to provide a unified API. type PolicyManager interface { // PutPolicy creates or updates a lifecycle policy. PutPolicy(ctx context.Context, name string, policy *Policy) error // GetPolicy retrieves a lifecycle policy by name. GetPolicy(ctx context.Context, name string) (*Policy, error) // DeletePolicy deletes a lifecycle policy by name. DeletePolicy(ctx context.Context, name string) error // ListPolicies retrieves all lifecycle policies. ListPolicies(ctx context.Context) (map[string]*Policy, error) } // Policy represents a generic index lifecycle policy. // It can be converted to ISM (AWS OpenSearch) or ILM (Elasticsearch) format. type Policy struct { // Description of the policy Description string `json:"description,omitempty"` // DefaultState is the initial state (ISM specific) DefaultState string `json:"default_state,omitempty"` // States defines the lifecycle states (ISM format) States []State `json:"states,omitempty"` // Phases defines the lifecycle phases (ILM format) // This field is used when targeting Elasticsearch Phases map[string]Phase `json:"phases,omitempty"` // ISMTemplate for applying policy to indices (ISM specific) ISMTemplate []ISMTemplate `json:"ism_template,omitempty"` } // State represents a state in ISM (AWS OpenSearch). type State struct { Name string `json:"name"` Actions []Action `json:"actions,omitempty"` Transitions []Transition `json:"transitions,omitempty"` } // Action represents an action within a state. // In ISM, actions are serialized as a single key-value object where // the key is the action type and the value is the configuration. type Action struct { // Config contains the action type as the key and its configuration as the value // Example: {"rollover": {"min_index_age": "1d"}} Config map[string]interface{} } // NewAction creates a new action with the specified type and configuration. func NewAction(actionType string, config map[string]interface{}) Action { return Action{ Config: map[string]interface{}{ actionType: config, }, } } // MarshalJSON implements custom JSON marshaling for Action. // It directly marshals the Config map instead of wrapping it in a "Config" field. func (a Action) MarshalJSON() ([]byte, error) { return json.Marshal(a.Config) } // UnmarshalJSON implements custom JSON unmarshaling for Action. func (a *Action) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &a.Config) } // Transition defines when to move to the next state. type Transition struct { StateName string `json:"state_name"` Conditions *Conditions `json:"conditions,omitempty"` } // Conditions defines the conditions for state transitions. type Conditions struct { MinIndexAge string `json:"min_index_age,omitempty"` MinDocCount *int64 `json:"min_doc_count,omitempty"` MinSize string `json:"min_size,omitempty"` } // Phase represents a phase in ILM (Elasticsearch). // This is for future Elasticsearch compatibility. type Phase struct { MinAge string `json:"min_age,omitempty"` Actions map[string]interface{} `json:"actions,omitempty"` } // ISMTemplate defines index patterns for automatic policy application. type ISMTemplate struct { IndexPatterns []string `json:"index_patterns"` Priority int `json:"priority"` } // Validate checks if the policy configuration is valid. func (p *Policy) Validate() error { if p == nil { return ErrInvalidPolicy } // For ISM: must have at least one state with a default_state if len(p.States) > 0 { if p.DefaultState == "" { return errors.New("default_state is required when states are defined") } // Verify default_state exists in states found := false for _, state := range p.States { if state.Name == p.DefaultState { found = true break } } if !found { return errors.New("default_state must match one of the defined states") } } // For ILM: must have at least one phase if len(p.Phases) > 0 && len(p.States) > 0 { return errors.New("cannot define both ISM states and ILM phases in the same policy") } if len(p.States) == 0 && len(p.Phases) == 0 { return errors.New("policy must define either states (ISM) or phases (ILM)") } return nil }