github.com/pulumi/terraform@v1.4.0/pkg/command/workspace_new.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/pulumi/terraform/pkg/command/arguments"
    10  	"github.com/pulumi/terraform/pkg/command/clistate"
    11  	"github.com/pulumi/terraform/pkg/command/views"
    12  	"github.com/pulumi/terraform/pkg/states/statefile"
    13  	"github.com/pulumi/terraform/pkg/tfdiags"
    14  	"github.com/mitchellh/cli"
    15  	"github.com/posener/complete"
    16  )
    17  
    18  type WorkspaceNewCommand struct {
    19  	Meta
    20  	LegacyName bool
    21  }
    22  
    23  func (c *WorkspaceNewCommand) Run(args []string) int {
    24  	args = c.Meta.process(args)
    25  	envCommandShowWarning(c.Ui, c.LegacyName)
    26  
    27  	var stateLock bool
    28  	var stateLockTimeout time.Duration
    29  	var statePath string
    30  	cmdFlags := c.Meta.defaultFlagSet("workspace new")
    31  	cmdFlags.BoolVar(&stateLock, "lock", true, "lock state")
    32  	cmdFlags.DurationVar(&stateLockTimeout, "lock-timeout", 0, "lock timeout")
    33  	cmdFlags.StringVar(&statePath, "state", "", "terraform state file")
    34  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    35  	if err := cmdFlags.Parse(args); err != nil {
    36  		c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
    37  		return 1
    38  	}
    39  
    40  	args = cmdFlags.Args()
    41  	if len(args) != 1 {
    42  		c.Ui.Error("Expected a single argument: NAME.\n")
    43  		return cli.RunResultHelp
    44  	}
    45  
    46  	workspace := args[0]
    47  
    48  	if !validWorkspaceName(workspace) {
    49  		c.Ui.Error(fmt.Sprintf(envInvalidName, workspace))
    50  		return 1
    51  	}
    52  
    53  	// You can't ask to create a workspace when you're overriding the
    54  	// workspace name to be something different.
    55  	if current, isOverridden := c.WorkspaceOverridden(); current != workspace && isOverridden {
    56  		c.Ui.Error(envIsOverriddenNewError)
    57  		return 1
    58  	}
    59  
    60  	configPath, err := ModulePath(args[1:])
    61  	if err != nil {
    62  		c.Ui.Error(err.Error())
    63  		return 1
    64  	}
    65  
    66  	var diags tfdiags.Diagnostics
    67  
    68  	backendConfig, backendDiags := c.loadBackendConfig(configPath)
    69  	diags = diags.Append(backendDiags)
    70  	if diags.HasErrors() {
    71  		c.showDiagnostics(diags)
    72  		return 1
    73  	}
    74  
    75  	// Load the backend
    76  	b, backendDiags := c.Backend(&BackendOpts{
    77  		Config: backendConfig,
    78  	})
    79  	diags = diags.Append(backendDiags)
    80  	if backendDiags.HasErrors() {
    81  		c.showDiagnostics(diags)
    82  		return 1
    83  	}
    84  
    85  	// This command will not write state
    86  	c.ignoreRemoteVersionConflict(b)
    87  
    88  	workspaces, err := b.Workspaces()
    89  	if err != nil {
    90  		c.Ui.Error(fmt.Sprintf("Failed to get configured named states: %s", err))
    91  		return 1
    92  	}
    93  	for _, ws := range workspaces {
    94  		if workspace == ws {
    95  			c.Ui.Error(fmt.Sprintf(envExists, workspace))
    96  			return 1
    97  		}
    98  	}
    99  
   100  	_, err = b.StateMgr(workspace)
   101  	if err != nil {
   102  		c.Ui.Error(err.Error())
   103  		return 1
   104  	}
   105  
   106  	// now set the current workspace locally
   107  	if err := c.SetWorkspace(workspace); err != nil {
   108  		c.Ui.Error(fmt.Sprintf("Error selecting new workspace: %s", err))
   109  		return 1
   110  	}
   111  
   112  	c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
   113  		strings.TrimSpace(envCreated), workspace)))
   114  
   115  	if statePath == "" {
   116  		// if we're not loading a state, then we're done
   117  		return 0
   118  	}
   119  
   120  	// load the new Backend state
   121  	stateMgr, err := b.StateMgr(workspace)
   122  	if err != nil {
   123  		c.Ui.Error(err.Error())
   124  		return 1
   125  	}
   126  
   127  	if stateLock {
   128  		stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
   129  		if diags := stateLocker.Lock(stateMgr, "workspace-new"); diags.HasErrors() {
   130  			c.showDiagnostics(diags)
   131  			return 1
   132  		}
   133  		defer func() {
   134  			if diags := stateLocker.Unlock(); diags.HasErrors() {
   135  				c.showDiagnostics(diags)
   136  			}
   137  		}()
   138  	}
   139  
   140  	// read the existing state file
   141  	f, err := os.Open(statePath)
   142  	if err != nil {
   143  		c.Ui.Error(err.Error())
   144  		return 1
   145  	}
   146  
   147  	stateFile, err := statefile.Read(f)
   148  	if err != nil {
   149  		c.Ui.Error(err.Error())
   150  		return 1
   151  	}
   152  
   153  	// save the existing state in the new Backend.
   154  	err = stateMgr.WriteState(stateFile.State)
   155  	if err != nil {
   156  		c.Ui.Error(err.Error())
   157  		return 1
   158  	}
   159  	err = stateMgr.PersistState(nil)
   160  	if err != nil {
   161  		c.Ui.Error(err.Error())
   162  		return 1
   163  	}
   164  
   165  	return 0
   166  }
   167  
   168  func (c *WorkspaceNewCommand) AutocompleteArgs() complete.Predictor {
   169  	return completePredictSequence{
   170  		complete.PredictAnything,
   171  		complete.PredictDirs(""),
   172  	}
   173  }
   174  
   175  func (c *WorkspaceNewCommand) AutocompleteFlags() complete.Flags {
   176  	return complete.Flags{
   177  		"-state": complete.PredictFiles("*.tfstate"),
   178  	}
   179  }
   180  
   181  func (c *WorkspaceNewCommand) Help() string {
   182  	helpText := `
   183  Usage: terraform [global options] workspace new [OPTIONS] NAME
   184  
   185    Create a new Terraform workspace.
   186  
   187  Options:
   188  
   189      -lock=false         Don't hold a state lock during the operation. This is
   190                          dangerous if others might concurrently run commands
   191                          against the same workspace.
   192  
   193      -lock-timeout=0s    Duration to retry a state lock.
   194  
   195      -state=path         Copy an existing state file into the new workspace.
   196  
   197  `
   198  	return strings.TrimSpace(helpText)
   199  }
   200  
   201  func (c *WorkspaceNewCommand) Synopsis() string {
   202  	return "Create a new workspace"
   203  }