feat: opensearch客户端初始化 feat: 索引模板接口 ai: 开发准则 chore: TDD流水线脚本
This commit is contained in:
217
operations/index/template.go
Normal file
217
operations/index/template.go
Normal file
@@ -0,0 +1,217 @@
|
||||
// Package index provides index-level operations for OpenSearch.
|
||||
package index
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"es-demo/client"
|
||||
|
||||
"github.com/opensearch-project/opensearch-go/v2/opensearchapi"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidTemplate is returned when the template configuration is invalid.
|
||||
ErrInvalidTemplate = errors.New("invalid template configuration")
|
||||
|
||||
// ErrTemplateNotFound is returned when the specified template does not exist.
|
||||
ErrTemplateNotFound = errors.New("template not found")
|
||||
)
|
||||
|
||||
// Template represents an OpenSearch index template.
|
||||
type Template struct {
|
||||
// IndexPatterns defines the index patterns this template applies to.
|
||||
IndexPatterns []string `json:"index_patterns"`
|
||||
|
||||
// Settings contains index settings like number of shards and replicas.
|
||||
Settings map[string]any `json:"settings,omitempty"`
|
||||
|
||||
// Mappings defines the field mappings for the index.
|
||||
Mappings map[string]any `json:"mappings,omitempty"`
|
||||
|
||||
// Aliases defines index aliases.
|
||||
Aliases map[string]any `json:"aliases,omitempty"`
|
||||
|
||||
// Priority determines template precedence when multiple templates match.
|
||||
Priority int `json:"priority,omitempty"`
|
||||
|
||||
// Version is used for external version management.
|
||||
Version int `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// TemplateResponse represents the response when getting a template.
|
||||
type TemplateResponse struct {
|
||||
IndexTemplates []struct {
|
||||
Name string `json:"name"`
|
||||
IndexTemplate Template `json:"index_template"`
|
||||
} `json:"index_templates"`
|
||||
}
|
||||
|
||||
// PutTemplate creates or updates an index template.
|
||||
func PutTemplate(ctx context.Context, c *client.Client, name string, template *Template) error {
|
||||
if err := validateTemplate(template); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return fmt.Errorf("%w: template name is required", ErrInvalidTemplate)
|
||||
}
|
||||
|
||||
body, err := json.Marshal(template)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal template: %w", err)
|
||||
}
|
||||
|
||||
req := opensearchapi.IndicesPutIndexTemplateRequest{
|
||||
Name: name,
|
||||
Body: bytes.NewReader(body),
|
||||
}
|
||||
|
||||
res, err := req.Do(ctx, c.GetClient())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute put template request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := res.Body.Close(); closeErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: failed to close response body: %v\n", closeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if res.IsError() {
|
||||
bodyBytes, _ := io.ReadAll(res.Body)
|
||||
return fmt.Errorf("put template failed with status %s: %s", res.Status(), string(bodyBytes))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTemplate retrieves an index template by name.
|
||||
func GetTemplate(ctx context.Context, c *client.Client, name string) (*Template, error) {
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("%w: template name is required", ErrInvalidTemplate)
|
||||
}
|
||||
|
||||
req := opensearchapi.IndicesGetIndexTemplateRequest{
|
||||
Name: []string{name},
|
||||
}
|
||||
|
||||
res, err := req.Do(ctx, c.GetClient())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute get template request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := res.Body.Close(); closeErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: failed to close response body: %v\n", closeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if res.IsError() {
|
||||
if res.StatusCode == 404 {
|
||||
return nil, ErrTemplateNotFound
|
||||
}
|
||||
bodyBytes, _ := io.ReadAll(res.Body)
|
||||
return nil, fmt.Errorf("get template failed with status %s: %s", res.Status(), string(bodyBytes))
|
||||
}
|
||||
|
||||
var response TemplateResponse
|
||||
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode template response: %w", err)
|
||||
}
|
||||
|
||||
if len(response.IndexTemplates) == 0 {
|
||||
return nil, ErrTemplateNotFound
|
||||
}
|
||||
|
||||
return &response.IndexTemplates[0].IndexTemplate, nil
|
||||
}
|
||||
|
||||
// DeleteTemplate deletes an index template.
|
||||
func DeleteTemplate(ctx context.Context, c *client.Client, name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("%w: template name is required", ErrInvalidTemplate)
|
||||
}
|
||||
|
||||
req := opensearchapi.IndicesDeleteIndexTemplateRequest{
|
||||
Name: name,
|
||||
}
|
||||
|
||||
res, err := req.Do(ctx, c.GetClient())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute delete template request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := res.Body.Close(); closeErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: failed to close response body: %v\n", closeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if res.IsError() {
|
||||
if res.StatusCode == 404 {
|
||||
return ErrTemplateNotFound
|
||||
}
|
||||
bodyBytes, _ := io.ReadAll(res.Body)
|
||||
return fmt.Errorf("delete template failed with status %s: %s", res.Status(), string(bodyBytes))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListTemplates retrieves all index templates or templates matching a pattern.
|
||||
func ListTemplates(ctx context.Context, c *client.Client, names ...string) (map[string]*Template, error) {
|
||||
var nameList []string
|
||||
if len(names) > 0 {
|
||||
nameList = names
|
||||
}
|
||||
|
||||
req := opensearchapi.IndicesGetIndexTemplateRequest{
|
||||
Name: nameList,
|
||||
}
|
||||
|
||||
res, err := req.Do(ctx, c.GetClient())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute list templates request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := res.Body.Close(); closeErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: failed to close response body: %v\n", closeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if res.IsError() {
|
||||
if res.StatusCode == 404 {
|
||||
return make(map[string]*Template), nil
|
||||
}
|
||||
bodyBytes, _ := io.ReadAll(res.Body)
|
||||
return nil, fmt.Errorf("list templates failed with status %s: %s", res.Status(), string(bodyBytes))
|
||||
}
|
||||
|
||||
var response TemplateResponse
|
||||
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode templates response: %w", err)
|
||||
}
|
||||
|
||||
templates := make(map[string]*Template)
|
||||
for _, item := range response.IndexTemplates {
|
||||
templates[item.Name] = &item.IndexTemplate
|
||||
}
|
||||
|
||||
return templates, nil
|
||||
}
|
||||
|
||||
// validateTemplate validates the template configuration.
|
||||
func validateTemplate(template *Template) error {
|
||||
if template == nil {
|
||||
return fmt.Errorf("%w: template cannot be nil", ErrInvalidTemplate)
|
||||
}
|
||||
|
||||
if len(template.IndexPatterns) == 0 {
|
||||
return fmt.Errorf("%w: index patterns are required", ErrInvalidTemplate)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user