-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
operator_snapshot_save.go
149 lines (115 loc) · 3.85 KB
/
operator_snapshot_save.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package command
import (
"fmt"
"io"
"os"
"strings"
"time"
"github.com/hashicorp/nomad/api"
"github.com/posener/complete"
)
type OperatorSnapshotSaveCommand struct {
Meta
}
func (c *OperatorSnapshotSaveCommand) Help() string {
helpText := `
Usage: nomad operator snapshot save [options] <file>
Retrieves an atomic, point-in-time snapshot of the state of the Nomad servers
which includes jobs, nodes, allocations, periodic jobs, and ACLs.
If ACLs are enabled, a management token must be supplied in order to perform
snapshot operations.
To create a snapshot from the leader server and save it to "backup.snap":
$ nomad snapshot save backup.snap
To create a potentially stale snapshot from any available server (useful if no
leader is available):
$ nomad snapshot save -stale backup.snap
This is useful for situations where a cluster is in a degraded state and no
leader is available. To target a specific server for a snapshot, you can run
the 'nomad operator snapshot save' command on that specific server.
General Options:
` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + `
Snapshot Save Options:
-stale=[true|false]
The -stale argument defaults to "false" which means the leader provides the
result. If the cluster is in an outage state without a leader, you may need
to set -stale to "true" to get the configuration from a non-leader server.
`
return strings.TrimSpace(helpText)
}
func (c *OperatorSnapshotSaveCommand) AutocompleteFlags() complete.Flags {
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
complete.Flags{
"-stale": complete.PredictAnything,
})
}
func (c *OperatorSnapshotSaveCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictNothing
}
func (c *OperatorSnapshotSaveCommand) Synopsis() string {
return "Saves snapshot of Nomad server state"
}
func (c *OperatorSnapshotSaveCommand) Name() string { return "operator snapshot save" }
func (c *OperatorSnapshotSaveCommand) Run(args []string) int {
var stale bool
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.BoolVar(&stale, "stale", false, "")
if err := flags.Parse(args); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to parse args: %v", err))
return 1
}
// Check for misuse
// Check that we either got no filename or exactly one.
args = flags.Args()
if len(args) > 1 {
c.Ui.Error("This command takes either no arguments or one: <filename>")
c.Ui.Error(commandErrorText(c))
return 1
}
now := time.Now()
filename := fmt.Sprintf("nomad-state-%04d%02d%0d-%d.snap", now.Year(), now.Month(), now.Day(), now.Unix())
if len(args) == 1 {
filename = args[0]
}
if _, err := os.Lstat(filename); err == nil {
c.Ui.Error(fmt.Sprintf("Destination file already exists: %q", filename))
c.Ui.Error(commandErrorText(c))
return 1
} else if !os.IsNotExist(err) {
c.Ui.Error(fmt.Sprintf("Unexpected failure checking %q: %v", filename, err))
return 1
}
// Set up a client.
client, err := c.Meta.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
return 1
}
tmpFile, err := os.Create(filename + ".tmp")
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to create file: %v", err))
return 1
}
// Fetch the current configuration.
q := &api.QueryOptions{
AllowStale: stale,
}
snapIn, err := client.Operator().Snapshot(q)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to get snapshot file: %v", err))
return 1
}
defer snapIn.Close()
_, err = io.Copy(tmpFile, snapIn)
if err != nil {
c.Ui.Error(fmt.Sprintf("Filed to download snapshot file: %v", err))
return 1
}
err = os.Rename(tmpFile.Name(), filename)
if err != nil {
c.Ui.Error(fmt.Sprintf("Filed to finalize snapshot file: %v", err))
return 1
}
c.Ui.Output(fmt.Sprintf("State file written to %v", filename))
return 0
}