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