github.com/profects/terraform@v0.9.0-beta1.0.20170227135739-92d4809db30d/backend/local/backend.go (about) 1 package local 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 8 "github.com/hashicorp/terraform/backend" 9 "github.com/hashicorp/terraform/helper/schema" 10 "github.com/hashicorp/terraform/state" 11 "github.com/hashicorp/terraform/terraform" 12 "github.com/mitchellh/cli" 13 "github.com/mitchellh/colorstring" 14 ) 15 16 // Local is an implementation of EnhancedBackend that performs all operations 17 // locally. This is the "default" backend and implements normal Terraform 18 // behavior as it is well known. 19 type Local struct { 20 // CLI and Colorize control the CLI output. If CLI is nil then no CLI 21 // output will be done. If CLIColor is nil then no coloring will be done. 22 CLI cli.Ui 23 CLIColor *colorstring.Colorize 24 25 // StatePath is the local path where state is read from. 26 // 27 // StateOutPath is the local path where the state will be written. 28 // If this is empty, it will default to StatePath. 29 // 30 // StateBackupPath is the local path where a backup file will be written. 31 // If this is empty, no backup will be taken. 32 StatePath string 33 StateOutPath string 34 StateBackupPath string 35 36 // we only want to create a single instance of the local state 37 state state.State 38 39 // ContextOpts are the base context options to set when initializing a 40 // Terraform context. Many of these will be overridden or merged by 41 // Operation. See Operation for more details. 42 ContextOpts *terraform.ContextOpts 43 44 // OpInput will ask for necessary input prior to performing any operations. 45 // 46 // OpValidation will perform validation prior to running an operation. The 47 // variable naming doesn't match the style of others since we have a func 48 // Validate. 49 OpInput bool 50 OpValidation bool 51 52 // Backend, if non-nil, will use this backend for non-enhanced behavior. 53 // This allows local behavior with remote state storage. It is a way to 54 // "upgrade" a non-enhanced backend to an enhanced backend with typical 55 // behavior. 56 // 57 // If this is nil, local performs normal state loading and storage. 58 Backend backend.Backend 59 60 schema *schema.Backend 61 opLock sync.Mutex 62 once sync.Once 63 } 64 65 func (b *Local) Input( 66 ui terraform.UIInput, c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { 67 b.once.Do(b.init) 68 69 f := b.schema.Input 70 if b.Backend != nil { 71 f = b.Backend.Input 72 } 73 74 return f(ui, c) 75 } 76 77 func (b *Local) Validate(c *terraform.ResourceConfig) ([]string, []error) { 78 b.once.Do(b.init) 79 80 f := b.schema.Validate 81 if b.Backend != nil { 82 f = b.Backend.Validate 83 } 84 85 return f(c) 86 } 87 88 func (b *Local) Configure(c *terraform.ResourceConfig) error { 89 b.once.Do(b.init) 90 91 f := b.schema.Configure 92 if b.Backend != nil { 93 f = b.Backend.Configure 94 } 95 96 return f(c) 97 } 98 99 func (b *Local) State() (state.State, error) { 100 // If we have a backend handling state, defer to that. 101 if b.Backend != nil { 102 return b.Backend.State() 103 } 104 105 if b.state != nil { 106 return b.state, nil 107 } 108 109 // Otherwise, we need to load the state. 110 var s state.State = &state.LocalState{ 111 Path: b.StatePath, 112 PathOut: b.StateOutPath, 113 } 114 115 // If we are backing up the state, wrap it 116 if path := b.StateBackupPath; path != "" { 117 s = &state.BackupState{ 118 Real: s, 119 Path: path, 120 } 121 } 122 123 b.state = s 124 return s, nil 125 } 126 127 // Operation implements backend.Enhanced 128 // 129 // This will initialize an in-memory terraform.Context to perform the 130 // operation within this process. 131 // 132 // The given operation parameter will be merged with the ContextOpts on 133 // the structure with the following rules. If a rule isn't specified and the 134 // name conflicts, assume that the field is overwritten if set. 135 func (b *Local) Operation(ctx context.Context, op *backend.Operation) (*backend.RunningOperation, error) { 136 // Determine the function to call for our operation 137 var f func(context.Context, *backend.Operation, *backend.RunningOperation) 138 switch op.Type { 139 case backend.OperationTypeRefresh: 140 f = b.opRefresh 141 case backend.OperationTypePlan: 142 f = b.opPlan 143 case backend.OperationTypeApply: 144 f = b.opApply 145 default: 146 return nil, fmt.Errorf( 147 "Unsupported operation type: %s\n\n"+ 148 "This is a bug in Terraform and should be reported. The local backend\n"+ 149 "is built-in to Terraform and should always support all operations.", 150 op.Type) 151 } 152 153 // Lock 154 b.opLock.Lock() 155 156 // Build our running operation 157 runningCtx, runningCtxCancel := context.WithCancel(context.Background()) 158 runningOp := &backend.RunningOperation{Context: runningCtx} 159 160 // Do it 161 go func() { 162 defer b.opLock.Unlock() 163 defer runningCtxCancel() 164 f(ctx, op, runningOp) 165 }() 166 167 // Return 168 return runningOp, nil 169 } 170 171 // Colorize returns the Colorize structure that can be used for colorizing 172 // output. This is gauranteed to always return a non-nil value and so is useful 173 // as a helper to wrap any potentially colored strings. 174 func (b *Local) Colorize() *colorstring.Colorize { 175 if b.CLIColor != nil { 176 return b.CLIColor 177 } 178 179 return &colorstring.Colorize{ 180 Colors: colorstring.DefaultColors, 181 Disable: true, 182 } 183 } 184 185 func (b *Local) init() { 186 b.schema = &schema.Backend{ 187 Schema: map[string]*schema.Schema{ 188 "path": &schema.Schema{ 189 Type: schema.TypeString, 190 Optional: true, 191 }, 192 }, 193 194 ConfigureFunc: b.schemaConfigure, 195 } 196 } 197 198 func (b *Local) schemaConfigure(ctx context.Context) error { 199 d := schema.FromContextBackendConfig(ctx) 200 201 // Set the path if it is set 202 pathRaw, ok := d.GetOk("path") 203 if ok { 204 path := pathRaw.(string) 205 if path == "" { 206 return fmt.Errorf("configured path is empty") 207 } 208 209 b.StatePath = path 210 } 211 212 return nil 213 }