github.com/bpineau/terraform@v0.8.0-rc1.0.20161126184705-a8886012d185/command/refresh.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "strings" 8 9 "github.com/hashicorp/go-multierror" 10 "github.com/hashicorp/terraform/terraform" 11 ) 12 13 // RefreshCommand is a cli.Command implementation that refreshes the state 14 // file. 15 type RefreshCommand struct { 16 Meta 17 } 18 19 func (c *RefreshCommand) Run(args []string) int { 20 args = c.Meta.process(args, true) 21 22 cmdFlags := c.Meta.flagSet("refresh") 23 cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") 24 cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", 0, "parallelism") 25 cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") 26 cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") 27 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 28 if err := cmdFlags.Parse(args); err != nil { 29 return 1 30 } 31 32 var configPath string 33 args = cmdFlags.Args() 34 if len(args) > 1 { 35 c.Ui.Error("The refresh command expects at most one argument.") 36 cmdFlags.Usage() 37 return 1 38 } else if len(args) == 1 { 39 configPath = args[0] 40 } else { 41 var err error 42 configPath, err = os.Getwd() 43 if err != nil { 44 c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) 45 } 46 } 47 48 // Check if remote state is enabled 49 state, err := c.State() 50 if err != nil { 51 c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) 52 return 1 53 } 54 55 // Verify that the state path exists. The "ContextArg" function below 56 // will actually do this, but we want to provide a richer error message 57 // if possible. 58 if !state.State().IsRemote() { 59 if _, err := os.Stat(c.Meta.statePath); err != nil { 60 if os.IsNotExist(err) { 61 c.Ui.Error(fmt.Sprintf( 62 "The Terraform state file for your infrastructure does not\n"+ 63 "exist. The 'refresh' command only works and only makes sense\n"+ 64 "when there is existing state that Terraform is managing. Please\n"+ 65 "double-check the value given below and try again. If you\n"+ 66 "haven't created infrastructure with Terraform yet, use the\n"+ 67 "'terraform apply' command.\n\n"+ 68 "Path: %s", 69 c.Meta.statePath)) 70 return 1 71 } 72 73 c.Ui.Error(fmt.Sprintf( 74 "There was an error reading the Terraform state that is needed\n"+ 75 "for refreshing. The path and error are shown below.\n\n"+ 76 "Path: %s\n\nError: %s", 77 c.Meta.statePath, 78 err)) 79 return 1 80 } 81 } 82 83 // This is going to keep track of shadow errors 84 var shadowErr error 85 86 // Build the context based on the arguments given 87 ctx, _, err := c.Context(contextOpts{ 88 Path: configPath, 89 StatePath: c.Meta.statePath, 90 Parallelism: c.Meta.parallelism, 91 }) 92 if err != nil { 93 c.Ui.Error(err.Error()) 94 return 1 95 } 96 97 if err := ctx.Input(c.InputMode()); err != nil { 98 c.Ui.Error(fmt.Sprintf("Error configuring: %s", err)) 99 return 1 100 } 101 102 // Record any shadow errors for later 103 if err := ctx.ShadowError(); err != nil { 104 shadowErr = multierror.Append(shadowErr, multierror.Prefix( 105 err, "input operation:")) 106 } 107 108 if !validateContext(ctx, c.Ui) { 109 return 1 110 } 111 112 newState, err := ctx.Refresh() 113 if err != nil { 114 c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) 115 return 1 116 } 117 118 log.Printf("[INFO] Writing state output to: %s", c.Meta.StateOutPath()) 119 if err := c.Meta.PersistState(newState); err != nil { 120 c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err)) 121 return 1 122 } 123 124 if outputs := outputsAsString(newState, terraform.RootModulePath, ctx.Module().Config().Outputs, true); outputs != "" { 125 c.Ui.Output(c.Colorize().Color(outputs)) 126 } 127 128 // Record any shadow errors for later 129 if err := ctx.ShadowError(); err != nil { 130 shadowErr = multierror.Append(shadowErr, multierror.Prefix( 131 err, "refresh operation:")) 132 } 133 134 // If we have an error in the shadow graph, let the user know. 135 c.outputShadowError(shadowErr, true) 136 137 return 0 138 } 139 140 func (c *RefreshCommand) Help() string { 141 helpText := ` 142 Usage: terraform refresh [options] [dir] 143 144 Update the state file of your infrastructure with metadata that matches 145 the physical resources they are tracking. 146 147 This will not modify your infrastructure, but it can modify your 148 state file to update metadata. This metadata might cause new changes 149 to occur when you generate a plan or call apply next. 150 151 Options: 152 153 -backup=path Path to backup the existing state file before 154 modifying. Defaults to the "-state-out" path with 155 ".backup" extension. Set to "-" to disable backup. 156 157 -input=true Ask for input for variables if not directly set. 158 159 -no-color If specified, output won't contain any color. 160 161 -state=path Path to read and save state (unless state-out 162 is specified). Defaults to "terraform.tfstate". 163 164 -state-out=path Path to write updated state file. By default, the 165 "-state" path will be used. 166 167 -target=resource Resource to target. Operation will be limited to this 168 resource and its dependencies. This flag can be used 169 multiple times. 170 171 -var 'foo=bar' Set a variable in the Terraform configuration. This 172 flag can be set multiple times. 173 174 -var-file=foo Set variables in the Terraform configuration from 175 a file. If "terraform.tfvars" is present, it will be 176 automatically loaded if this flag is not specified. 177 178 ` 179 return strings.TrimSpace(helpText) 180 } 181 182 func (c *RefreshCommand) Synopsis() string { 183 return "Update local state file against real resources" 184 }