github.com/vic3lord/terraform@v0.8.0-rc1.0.20170626102919-16c6dd2cb372/command/state_meta.go (about)

     1  package command
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	backendlocal "github.com/hashicorp/terraform/backend/local"
     9  	"github.com/hashicorp/terraform/state"
    10  	"github.com/hashicorp/terraform/terraform"
    11  )
    12  
    13  // StateMeta is the meta struct that should be embedded in state subcommands.
    14  type StateMeta struct{}
    15  
    16  // State returns the state for this meta. This gets the appropriate state from
    17  // the backend, but changes the way that backups are done. This configures
    18  // backups to be timestamped rather than just the original state path plus a
    19  // backup path.
    20  func (c *StateMeta) State(m *Meta) (state.State, error) {
    21  	var realState state.State
    22  	backupPath := m.backupPath
    23  	stateOutPath := m.statePath
    24  
    25  	// use the specified state
    26  	if m.statePath != "" {
    27  		realState = &state.LocalState{
    28  			Path: m.statePath,
    29  		}
    30  	} else {
    31  		// Load the backend
    32  		b, err := m.Backend(nil)
    33  		if err != nil {
    34  			return nil, err
    35  		}
    36  
    37  		env := m.Workspace()
    38  		// Get the state
    39  		s, err := b.State(env)
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  
    44  		// Get a local backend
    45  		localRaw, err := m.Backend(&BackendOpts{ForceLocal: true})
    46  		if err != nil {
    47  			// This should never fail
    48  			panic(err)
    49  		}
    50  		localB := localRaw.(*backendlocal.Local)
    51  		_, stateOutPath, _ = localB.StatePaths(env)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  
    56  		realState = s
    57  	}
    58  
    59  	// We always backup state commands, so set the back if none was specified
    60  	// (the default is "-", but some tests bypass the flag parsing).
    61  	if backupPath == "-" || backupPath == "" {
    62  		// Determine the backup path. stateOutPath is set to the resulting
    63  		// file where state is written (cached in the case of remote state)
    64  		backupPath = fmt.Sprintf(
    65  			"%s.%d%s",
    66  			stateOutPath,
    67  			time.Now().UTC().Unix(),
    68  			DefaultBackupExtension)
    69  	}
    70  
    71  	// Wrap it for backups
    72  	realState = &state.BackupState{
    73  		Real: realState,
    74  		Path: backupPath,
    75  	}
    76  
    77  	return realState, nil
    78  }
    79  
    80  // filterInstance filters a single instance out of filter results.
    81  func (c *StateMeta) filterInstance(rs []*terraform.StateFilterResult) (*terraform.StateFilterResult, error) {
    82  	var result *terraform.StateFilterResult
    83  	for _, r := range rs {
    84  		if _, ok := r.Value.(*terraform.InstanceState); !ok {
    85  			continue
    86  		}
    87  
    88  		if result != nil {
    89  			return nil, errors.New(errStateMultiple)
    90  		}
    91  
    92  		result = r
    93  	}
    94  
    95  	return result, nil
    96  }
    97  
    98  const errStateMultiple = `Multiple instances found for the given pattern!
    99  
   100  This command requires that the pattern match exactly one instance
   101  of a resource. To view the matched instances, use "terraform state list".
   102  Please modify the pattern to match only a single instance.`