github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/command/unlock.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/terraform/state"
     8  	"github.com/hashicorp/terraform/terraform"
     9  	"github.com/mitchellh/cli"
    10  )
    11  
    12  // UnlockCommand is a cli.Command implementation that manually unlocks
    13  // the state.
    14  type UnlockCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *UnlockCommand) Run(args []string) int {
    19  	args, err := c.Meta.process(args, false)
    20  	if err != nil {
    21  		return 1
    22  	}
    23  
    24  	force := false
    25  	cmdFlags := c.Meta.flagSet("force-unlock")
    26  	cmdFlags.BoolVar(&force, "force", false, "force")
    27  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    28  	if err := cmdFlags.Parse(args); err != nil {
    29  		return 1
    30  	}
    31  
    32  	args = cmdFlags.Args()
    33  	if len(args) == 0 {
    34  		c.Ui.Error("unlock requires a lock id argument")
    35  		return cli.RunResultHelp
    36  	}
    37  
    38  	lockID := args[0]
    39  	args = args[1:]
    40  
    41  	// assume everything is initialized. The user can manually init if this is
    42  	// required.
    43  	configPath, err := ModulePath(args)
    44  	if err != nil {
    45  		c.Ui.Error(err.Error())
    46  		return 1
    47  	}
    48  
    49  	conf, err := c.Config(configPath)
    50  	if err != nil {
    51  		c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
    52  		return 1
    53  	}
    54  
    55  	// Load the backend
    56  	b, err := c.Backend(&BackendOpts{
    57  		Config: conf,
    58  	})
    59  	if err != nil {
    60  		c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
    61  		return 1
    62  	}
    63  
    64  	env := c.Workspace()
    65  	st, err := b.State(env)
    66  	if err != nil {
    67  		c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
    68  		return 1
    69  	}
    70  
    71  	isLocal := false
    72  	switch s := st.(type) {
    73  	case *state.BackupState:
    74  		if _, ok := s.Real.(*state.LocalState); ok {
    75  			isLocal = true
    76  		}
    77  	case *state.LocalState:
    78  		isLocal = true
    79  	}
    80  
    81  	if !force {
    82  		// Forcing this doesn't do anything, but doesn't break anything either,
    83  		// and allows us to run the basic command test too.
    84  		if isLocal {
    85  			c.Ui.Error("Local state cannot be unlocked by another process")
    86  			return 1
    87  		}
    88  
    89  		desc := "Terraform will remove the lock on the remote state.\n" +
    90  			"This will allow local Terraform commands to modify this state, even though it\n" +
    91  			"may be still be in use. Only 'yes' will be accepted to confirm."
    92  
    93  		v, err := c.UIInput().Input(&terraform.InputOpts{
    94  			Id:          "force-unlock",
    95  			Query:       "Do you really want to force-unlock?",
    96  			Description: desc,
    97  		})
    98  		if err != nil {
    99  			c.Ui.Error(fmt.Sprintf("Error asking for confirmation: %s", err))
   100  			return 1
   101  		}
   102  		if v != "yes" {
   103  			c.Ui.Output("force-unlock cancelled.")
   104  			return 1
   105  		}
   106  	}
   107  
   108  	if err := st.Unlock(lockID); err != nil {
   109  		c.Ui.Error(fmt.Sprintf("Failed to unlock state: %s", err))
   110  		return 1
   111  	}
   112  
   113  	c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputUnlockSuccess)))
   114  	return 0
   115  }
   116  
   117  func (c *UnlockCommand) Help() string {
   118  	helpText := `
   119  Usage: terraform force-unlock LOCK_ID [DIR]
   120  
   121    Manually unlock the state for the defined configuration.
   122  
   123    This will not modify your infrastructure. This command removes the lock on the
   124    state for the current configuration. The behavior of this lock is dependent
   125    on the backend being used. Local state files cannot be unlocked by another
   126    process.
   127  
   128  Options:
   129  
   130    -force                 Don't ask for input for unlock confirmation.
   131  `
   132  	return strings.TrimSpace(helpText)
   133  }
   134  
   135  func (c *UnlockCommand) Synopsis() string {
   136  	return "Manually unlock the terraform state"
   137  }
   138  
   139  const outputUnlockSuccess = `
   140  [reset][bold][green]Terraform state has been successfully unlocked![reset][green]
   141  
   142  The state has been unlocked, and Terraform commands should now be able to
   143  obtain a new lock on the remote state.
   144  `