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  }