Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Snapshot Create API #533

Merged
merged 2 commits into from
May 29, 2017
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
6 changes: 5 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1629,12 +1629,16 @@ func (c *Client) TasksList() *TasksListService {

// -- Snapshot and Restore --

// TODO Snapshot Create
// TODO Snapshot Delete
// TODO Snapshot Get
// TODO Snapshot Restore
// TODO Snapshot Status

// SnapshotCreate creates a snapshot.
func (c *Client) SnapshotCreate(repository string, snapshot string) *SnapshotCreateService {
return NewSnapshotCreateService(c).Repository(repository).Snapshot(snapshot)
}

// SnapshotCreateRepository creates or updates a snapshot repository.
func (c *Client) SnapshotCreateRepository(repository string) *SnapshotCreateRepositoryService {
return NewSnapshotCreateRepositoryService(c).Repository(repository)
Expand Down
186 changes: 186 additions & 0 deletions snapshot_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright 2012-present Oliver Eilhard. All rights reserved.
// Use of this source code is governed by a MIT-license.
// See http://olivere.mit-license.org/license.txt for details.

package elastic

import (
"context"
"encoding/json"
"fmt"
"net/url"
"time"

"gopkg.in/olivere/elastic.v5/uritemplates"
)

// SnapshotCreateService is documented at https://www.elastic.co/guide/en/elasticsearch/reference/5.x/modules-snapshots.html.
type SnapshotCreateService struct {
client *Client
pretty bool
repository string
snapshot string
masterTimeout string
waitForCompletion *bool
bodyJson interface{}
bodyString string
}

// NewSnapshotCreateService creates a new SnapshotCreateService.
func NewSnapshotCreateService(client *Client) *SnapshotCreateService {
return &SnapshotCreateService{
client: client,
}
}

// Repository is the repository name.
func (s *SnapshotCreateService) Repository(repository string) *SnapshotCreateService {
s.repository = repository
return s
}

// Snapshot is the snapshot name.
func (s *SnapshotCreateService) Snapshot(snapshot string) *SnapshotCreateService {
s.snapshot = snapshot
return s
}

// MasterTimeout is documented as: Explicit operation timeout for connection to master node.
func (s *SnapshotCreateService) MasterTimeout(masterTimeout string) *SnapshotCreateService {
s.masterTimeout = masterTimeout
return s
}

// WaitForCompletion is documented as: Should this request wait until the operation has completed before returning.
func (s *SnapshotCreateService) WaitForCompletion(waitForCompletion bool) *SnapshotCreateService {
s.waitForCompletion = &waitForCompletion
return s
}

// Pretty indicates that the JSON response be indented and human readable.
func (s *SnapshotCreateService) Pretty(pretty bool) *SnapshotCreateService {
s.pretty = pretty
return s
}

// BodyJson is documented as: The snapshot definition.
func (s *SnapshotCreateService) BodyJson(body interface{}) *SnapshotCreateService {
s.bodyJson = body
return s
}

// BodyString is documented as: The snapshot definition.
func (s *SnapshotCreateService) BodyString(body string) *SnapshotCreateService {
s.bodyString = body
return s
}

// buildURL builds the URL for the operation.
func (s *SnapshotCreateService) buildURL() (string, url.Values, error) {
// Build URL
path, err := uritemplates.Expand("/_snapshot/{repository}/{snapshot}", map[string]string{
"snapshot": s.snapshot,
"repository": s.repository,
})
if err != nil {
return "", url.Values{}, err
}

// Add query string parameters
params := url.Values{}
if s.pretty {
params.Set("pretty", "1")
}
if s.masterTimeout != "" {
params.Set("master_timeout", s.masterTimeout)
}
if s.waitForCompletion != nil {
params.Set("wait_for_completion", fmt.Sprintf("%v", *s.waitForCompletion))
}
return path, params, nil
}

// Validate checks if the operation is valid.
func (s *SnapshotCreateService) Validate() error {
var invalid []string
if s.repository == "" {
invalid = append(invalid, "Repository")
}
if s.snapshot == "" {
invalid = append(invalid, "Snapshot")
}
if len(invalid) > 0 {
return fmt.Errorf("missing required fields: %v", invalid)
}
return nil
}

// Do executes the operation.
func (s *SnapshotCreateService) Do(ctx context.Context) (*SnapshotCreateResponse, error) {
// Check pre-conditions
if err := s.Validate(); err != nil {
return nil, err
}

// Get URL for request
path, params, err := s.buildURL()
if err != nil {
return nil, err
}

// Setup HTTP request body
var body interface{}
if s.bodyJson != nil {
body = s.bodyJson
} else {
body = s.bodyString
}

// Get HTTP response
res, err := s.client.PerformRequest(ctx, "PUT", path, params, body)
if err != nil {
return nil, err
}

// Return operation response
ret := new(SnapshotCreateResponse)
if err := json.Unmarshal(res.Body, ret); err != nil {
return nil, err
}
return ret, nil
}

// SnapshotShardFailure stores information about failures that occurred during shard snapshotting process.
type SnapshotShardFailure struct {
Index string `json:"index"`
IndexUUID string `json:"index_uuid"`
ShardID int `json:"shard_id"`
Reason string `json:"reason"`
NodeID string `json:"node_id"`
Status string `json:"status"`
}

// SnapshotCreateResponse is the response of SnapshotCreateService.Do.
type SnapshotCreateResponse struct {
// Accepted indicates whether the request was accepted by elasticsearch.
// It's available when waitForCompletion is false.
Accepted *bool `json:"accepted"`

// Snapshot is available when waitForCompletion is true.
Snapshot *struct {
Copy link
Owner

Choose a reason for hiding this comment

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

The *struct, although valid, doesn't look that good. Does ES always send back the snapshot? We have to look it up in the Java source. If it does, we should make it a normal struct, not a pointer to a struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Snapshot string `json:"snapshot"`
UUID string `json:"uuid"`
VersionID int `json:"version_id"`
Version string `json:"version"`
Indices []string `json:"indices"`
State string `json:"state"`
Reason string `json:"reason"`
StartTime time.Time `json:"start_time"`
StartTimeInMillis int64 `json:"start_time_in_millis"`
EndTime time.Time `json:"end_time"`
EndTimeInMillis int64 `json:"end_time_in_millis"`
DurationInMillis int64 `json:"duration_in_millis"`
Failures []SnapshotShardFailure `json:"failures"`
Shards shardsInfo `json:"shards"`
} `json:"snapshot"`
}
63 changes: 63 additions & 0 deletions snapshot_create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package elastic

import (
"net/url"
"reflect"
"testing"
)

func TestSnapshotValidate(t *testing.T) {
var client *Client

err := NewSnapshotCreateService(client).Validate()
got := err.Error()
expected := "missing required fields: [Repository Snapshot]"
if got != expected {
t.Errorf("expected %q; got: %q", expected, got)
}
}

func TestSnapshotPutURL(t *testing.T) {
client := setupTestClient(t)

tests := []struct {
Repository string
Snapshot string
Pretty bool
MasterTimeout string
WaitForCompletion bool
ExpectedPath string
ExpectedParams url.Values
}{
{
Repository: "repo",
Snapshot: "snapshot_of_sunday",
Pretty: true,
MasterTimeout: "60s",
WaitForCompletion: true,
ExpectedPath: "/_snapshot/repo/snapshot_of_sunday",
ExpectedParams: url.Values{
"pretty": []string{"1"},
"master_timeout": []string{"60s"},
"wait_for_completion": []string{"true"},
},
},
}

for _, test := range tests {
path, params, err := client.SnapshotCreate(test.Repository, test.Snapshot).
Pretty(test.Pretty).
MasterTimeout(test.MasterTimeout).
WaitForCompletion(test.WaitForCompletion).
buildURL()
if err != nil {
t.Fatal(err)
}
if path != test.ExpectedPath {
t.Errorf("expected %q; got: %q", test.ExpectedPath, path)
}
if !reflect.DeepEqual(params, test.ExpectedParams) {
t.Errorf("expected %q; got: %q", test.ExpectedParams, params)
}
}
}