github.com/profects/terraform@v0.9.0-beta1.0.20170227135739-92d4809db30d/command/untaint.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	clistate "github.com/hashicorp/terraform/command/state"
     9  	"github.com/hashicorp/terraform/state"
    10  )
    11  
    12  // UntaintCommand is a cli.Command implementation that manually untaints
    13  // a resource, marking it as primary and ready for service.
    14  type UntaintCommand struct {
    15  	Meta
    16  }
    17  
    18  func (c *UntaintCommand) Run(args []string) int {
    19  	args = c.Meta.process(args, false)
    20  
    21  	var allowMissing bool
    22  	var module string
    23  	cmdFlags := c.Meta.flagSet("untaint")
    24  	cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "module")
    25  	cmdFlags.StringVar(&module, "module", "", "module")
    26  	cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
    27  	cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
    28  	cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
    29  	cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
    30  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    31  	if err := cmdFlags.Parse(args); err != nil {
    32  		return 1
    33  	}
    34  
    35  	// Require the one argument for the resource to untaint
    36  	args = cmdFlags.Args()
    37  	if len(args) != 1 {
    38  		c.Ui.Error("The untaint command expects exactly one argument.")
    39  		cmdFlags.Usage()
    40  		return 1
    41  	}
    42  
    43  	name := args[0]
    44  	if module == "" {
    45  		module = "root"
    46  	} else {
    47  		module = "root." + module
    48  	}
    49  
    50  	// Load the backend
    51  	b, err := c.Backend(nil)
    52  	if err != nil {
    53  		c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
    54  		return 1
    55  	}
    56  
    57  	// Get the state
    58  	st, err := b.State()
    59  	if err != nil {
    60  		c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
    61  		return 1
    62  	}
    63  	if err := st.RefreshState(); err != nil {
    64  		c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
    65  		return 1
    66  	}
    67  
    68  	if c.Meta.stateLock {
    69  		lockInfo := state.NewLockInfo()
    70  		lockInfo.Operation = "untaint"
    71  		lockID, err := clistate.Lock(st, lockInfo, c.Ui, c.Colorize())
    72  		if err != nil {
    73  			c.Ui.Error(fmt.Sprintf("Error locking state: %s", err))
    74  			return 1
    75  		}
    76  
    77  		defer clistate.Unlock(st, lockID, c.Ui, c.Colorize())
    78  	}
    79  
    80  	// Get the actual state structure
    81  	s := st.State()
    82  	if s.Empty() {
    83  		if allowMissing {
    84  			return c.allowMissingExit(name, module)
    85  		}
    86  
    87  		c.Ui.Error(fmt.Sprintf(
    88  			"The state is empty. The most common reason for this is that\n" +
    89  				"an invalid state file path was given or Terraform has never\n " +
    90  				"been run for this infrastructure. Infrastructure must exist\n" +
    91  				"for it to be untainted."))
    92  		return 1
    93  	}
    94  
    95  	// Get the proper module holding the resource we want to untaint
    96  	modPath := strings.Split(module, ".")
    97  	mod := s.ModuleByPath(modPath)
    98  	if mod == nil {
    99  		if allowMissing {
   100  			return c.allowMissingExit(name, module)
   101  		}
   102  
   103  		c.Ui.Error(fmt.Sprintf(
   104  			"The module %s could not be found. There is nothing to untaint.",
   105  			module))
   106  		return 1
   107  	}
   108  
   109  	// If there are no resources in this module, it is an error
   110  	if len(mod.Resources) == 0 {
   111  		if allowMissing {
   112  			return c.allowMissingExit(name, module)
   113  		}
   114  
   115  		c.Ui.Error(fmt.Sprintf(
   116  			"The module %s has no resources. There is nothing to untaint.",
   117  			module))
   118  		return 1
   119  	}
   120  
   121  	// Get the resource we're looking for
   122  	rs, ok := mod.Resources[name]
   123  	if !ok {
   124  		if allowMissing {
   125  			return c.allowMissingExit(name, module)
   126  		}
   127  
   128  		c.Ui.Error(fmt.Sprintf(
   129  			"The resource %s couldn't be found in the module %s.",
   130  			name,
   131  			module))
   132  		return 1
   133  	}
   134  
   135  	// Untaint the resource
   136  	rs.Untaint()
   137  
   138  	log.Printf("[INFO] Writing state output to: %s", c.Meta.StateOutPath())
   139  	if err := st.WriteState(s); err != nil {
   140  		c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err))
   141  		return 1
   142  	}
   143  	if err := st.PersistState(); err != nil {
   144  		c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err))
   145  		return 1
   146  	}
   147  
   148  	c.Ui.Output(fmt.Sprintf(
   149  		"The resource %s in the module %s has been successfully untainted!",
   150  		name, module))
   151  	return 0
   152  }
   153  
   154  func (c *UntaintCommand) Help() string {
   155  	helpText := `
   156  Usage: terraform untaint [options] name
   157  
   158    Manually unmark a resource as tainted, restoring it as the primary
   159    instance in the state.  This reverses either a manual 'terraform taint'
   160    or the result of provisioners failing on a resource.
   161  
   162    This will not modify your infrastructure. This command changes your
   163    state to unmark a resource as tainted.  This command can be undone by
   164    reverting the state backup file that is created, or by running
   165    'terraform taint' on the resource.
   166  
   167  Options:
   168  
   169    -allow-missing      If specified, the command will succeed (exit code 0)
   170                        even if the resource is missing.
   171  
   172    -backup=path        Path to backup the existing state file before
   173                        modifying. Defaults to the "-state-out" path with
   174                        ".backup" extension. Set to "-" to disable backup.
   175  
   176    -lock=true          Lock the state file when locking is supported.
   177  
   178    -module=path        The module path where the resource lives. By
   179                        default this will be root. Child modules can be specified
   180                        by names. Ex. "consul" or "consul.vpc" (nested modules).
   181  
   182    -no-color           If specified, output won't contain any color.
   183  
   184    -state=path         Path to read and save state (unless state-out
   185                        is specified). Defaults to "terraform.tfstate".
   186  
   187    -state-out=path     Path to write updated state file. By default, the
   188                        "-state" path will be used.
   189  
   190  `
   191  	return strings.TrimSpace(helpText)
   192  }
   193  
   194  func (c *UntaintCommand) Synopsis() string {
   195  	return "Manually unmark a resource as tainted"
   196  }
   197  
   198  func (c *UntaintCommand) allowMissingExit(name, module string) int {
   199  	c.Ui.Output(fmt.Sprintf(
   200  		"The resource %s in the module %s was not found, but\n"+
   201  			"-allow-missing is set, so we're exiting successfully.",
   202  		name, module))
   203  	return 0
   204  }