Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions cmd/elasticsearch/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ func runConfigure(appCtx *app.Context) error {

// Configure snapshot repository
repo := appCtx.Config.Elasticsearch.SnapshotRepository

// Always unregister existing repository to ensure clean state
appCtx.Logger.Infof("Unregistering snapshot repository '%s'...", repo.Name)
if err := appCtx.ESClient.DeleteSnapshotRepository(repo.Name); err != nil {
return fmt.Errorf("failed to unregister snapshot repository: %w", err)
}
appCtx.Logger.Successf("Snapshot repository unregistered successfully")

appCtx.Logger.Infof("Configuring snapshot repository '%s' (bucket: %s)...", repo.Name, repo.Bucket)

err = appCtx.ESClient.ConfigureSnapshotRepository(
Expand Down
43 changes: 42 additions & 1 deletion cmd/elasticsearch/configure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,24 @@ import (

// mockESClientForConfigure is a mock for testing configure command
type mockESClientForConfigure struct {
deleteRepoErr error
configureRepoErr error
configureSLMErr error
repoDeleted bool
repoConfigured bool
slmConfigured bool
lastRepoConfig map[string]string
lastSLMConfig map[string]interface{}
}

func (m *mockESClientForConfigure) DeleteSnapshotRepository(_ string) error {
if m.deleteRepoErr != nil {
return m.deleteRepoErr
}
m.repoDeleted = true
return nil
}

func (m *mockESClientForConfigure) ConfigureSnapshotRepository(name, bucket, endpoint, basePath, accessKey, secretKey string) error {
if m.configureRepoErr != nil {
return m.configureRepoErr
Expand Down Expand Up @@ -260,32 +270,51 @@ minio:
}

// TestMockESClientForConfigure demonstrates mock usage for configure
//
//nolint:funlen // Table-driven test
func TestMockESClientForConfigure(t *testing.T) {
tests := []struct {
name string
deleteRepoErr error
configureRepoErr error
configureSLMErr error
expectDeleteOK bool
expectRepoOK bool
expectSLMOK bool
}{
{
name: "successful configuration",
deleteRepoErr: nil,
configureRepoErr: nil,
configureSLMErr: nil,
expectDeleteOK: true,
expectRepoOK: true,
expectSLMOK: true,
},
{
name: "repository deletion fails",
deleteRepoErr: fmt.Errorf("repository deletion failed"),
configureRepoErr: nil,
configureSLMErr: nil,
expectDeleteOK: false,
expectRepoOK: false,
expectSLMOK: false,
},
{
name: "repository configuration fails",
deleteRepoErr: nil,
configureRepoErr: fmt.Errorf("repository creation failed"),
configureSLMErr: nil,
expectDeleteOK: true,
expectRepoOK: false,
expectSLMOK: false,
},
{
name: "SLM configuration fails",
deleteRepoErr: nil,
configureRepoErr: nil,
configureSLMErr: fmt.Errorf("SLM policy creation failed"),
expectDeleteOK: true,
expectRepoOK: true,
expectSLMOK: false,
},
Expand All @@ -294,12 +323,24 @@ func TestMockESClientForConfigure(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := &mockESClientForConfigure{
deleteRepoErr: tt.deleteRepoErr,
configureRepoErr: tt.configureRepoErr,
configureSLMErr: tt.configureSLMErr,
}

// Delete repository
err := mockClient.DeleteSnapshotRepository("backup-repo")

if tt.expectDeleteOK {
assert.NoError(t, err)
assert.True(t, mockClient.repoDeleted)
} else {
assert.Error(t, err)
return // Don't test configure if delete failed
}

// Configure repository
err := mockClient.ConfigureSnapshotRepository(
err = mockClient.ConfigureSnapshotRepository(
"backup-repo",
"backup-bucket",
"minio:9000",
Expand Down
22 changes: 22 additions & 0 deletions internal/clients/elasticsearch/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,28 @@ func (c *Client) RolloverDatastream(datastreamName string) error {
return nil
}

// DeleteSnapshotRepository deletes a snapshot repository
func (c *Client) DeleteSnapshotRepository(name string) error {
res, err := c.es.Snapshot.DeleteRepository(
[]string{name},
c.es.Snapshot.DeleteRepository.WithContext(context.Background()),
)
if err != nil {
return fmt.Errorf("failed to delete snapshot repository: %w", err)
}
defer res.Body.Close()

if res.StatusCode == http.StatusNotFound {
return nil // Repository doesn't exist, which is fine
}

if res.IsError() {
return fmt.Errorf("elasticsearch returned error: %s", res.String())
}

return nil
}

// ConfigureSnapshotRepository configures an S3 snapshot repository
func (c *Client) ConfigureSnapshotRepository(name, bucket, endpoint, basePath, accessKey, secretKey string) error {
body := map[string]interface{}{
Expand Down
1 change: 1 addition & 0 deletions internal/clients/elasticsearch/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Interface interface {
RolloverDatastream(datastreamName string) error

// Repository and SLM operations
DeleteSnapshotRepository(name string) error
ConfigureSnapshotRepository(name, bucket, endpoint, basePath, accessKey, secretKey string) error
ConfigureSLMPolicy(name, schedule, snapshotName, repository, indices, expireAfter string, minCount, maxCount int) error
}
Expand Down