github.com/hs0210/hashicorp-terraform@v0.11.12-beta1/commands.go (about)

     1  package main
     2  
     3  import (
     4  	"log"
     5  	"os"
     6  	"os/signal"
     7  
     8  	"github.com/hashicorp/terraform/command"
     9  	pluginDiscovery "github.com/hashicorp/terraform/plugin/discovery"
    10  	"github.com/hashicorp/terraform/svchost"
    11  	"github.com/hashicorp/terraform/svchost/auth"
    12  	"github.com/hashicorp/terraform/svchost/disco"
    13  	"github.com/mitchellh/cli"
    14  )
    15  
    16  // runningInAutomationEnvName gives the name of an environment variable that
    17  // can be set to any non-empty value in order to suppress certain messages
    18  // that assume that Terraform is being run from a command prompt.
    19  const runningInAutomationEnvName = "TF_IN_AUTOMATION"
    20  
    21  // Commands is the mapping of all the available Terraform commands.
    22  var Commands map[string]cli.CommandFactory
    23  var PlumbingCommands map[string]struct{}
    24  
    25  // Ui is the cli.Ui used for communicating to the outside world.
    26  var Ui cli.Ui
    27  
    28  const (
    29  	ErrorPrefix  = "e:"
    30  	OutputPrefix = "o:"
    31  )
    32  
    33  func initCommands(config *Config, services *disco.Disco) {
    34  	var inAutomation bool
    35  	if v := os.Getenv(runningInAutomationEnvName); v != "" {
    36  		inAutomation = true
    37  	}
    38  
    39  	for userHost, hostConfig := range config.Hosts {
    40  		host, err := svchost.ForComparison(userHost)
    41  		if err != nil {
    42  			// We expect the config was already validated by the time we get
    43  			// here, so we'll just ignore invalid hostnames.
    44  			continue
    45  		}
    46  		services.ForceHostServices(host, hostConfig.Services)
    47  	}
    48  
    49  	dataDir := os.Getenv("TF_DATA_DIR")
    50  
    51  	meta := command.Meta{
    52  		Color:            true,
    53  		GlobalPluginDirs: globalPluginDirs(),
    54  		PluginOverrides:  &PluginOverrides,
    55  		Ui:               Ui,
    56  
    57  		Services: services,
    58  
    59  		RunningInAutomation: inAutomation,
    60  		PluginCacheDir:      config.PluginCacheDir,
    61  		OverrideDataDir:     dataDir,
    62  
    63  		ShutdownCh: makeShutdownCh(),
    64  	}
    65  
    66  	// The command list is included in the terraform -help
    67  	// output, which is in turn included in the docs at
    68  	// website/source/docs/commands/index.html.markdown; if you
    69  	// add, remove or reclassify commands then consider updating
    70  	// that to match.
    71  
    72  	PlumbingCommands = map[string]struct{}{
    73  		"state":        struct{}{}, // includes all subcommands
    74  		"debug":        struct{}{}, // includes all subcommands
    75  		"force-unlock": struct{}{},
    76  	}
    77  
    78  	Commands = map[string]cli.CommandFactory{
    79  		"apply": func() (cli.Command, error) {
    80  			return &command.ApplyCommand{
    81  				Meta: meta,
    82  			}, nil
    83  		},
    84  
    85  		"console": func() (cli.Command, error) {
    86  			return &command.ConsoleCommand{
    87  				Meta: meta,
    88  			}, nil
    89  		},
    90  
    91  		"destroy": func() (cli.Command, error) {
    92  			return &command.ApplyCommand{
    93  				Meta:    meta,
    94  				Destroy: true,
    95  			}, nil
    96  		},
    97  
    98  		"env": func() (cli.Command, error) {
    99  			return &command.WorkspaceCommand{
   100  				Meta:       meta,
   101  				LegacyName: true,
   102  			}, nil
   103  		},
   104  
   105  		"env list": func() (cli.Command, error) {
   106  			return &command.WorkspaceListCommand{
   107  				Meta:       meta,
   108  				LegacyName: true,
   109  			}, nil
   110  		},
   111  
   112  		"env select": func() (cli.Command, error) {
   113  			return &command.WorkspaceSelectCommand{
   114  				Meta:       meta,
   115  				LegacyName: true,
   116  			}, nil
   117  		},
   118  
   119  		"env new": func() (cli.Command, error) {
   120  			return &command.WorkspaceNewCommand{
   121  				Meta:       meta,
   122  				LegacyName: true,
   123  			}, nil
   124  		},
   125  
   126  		"env delete": func() (cli.Command, error) {
   127  			return &command.WorkspaceDeleteCommand{
   128  				Meta:       meta,
   129  				LegacyName: true,
   130  			}, nil
   131  		},
   132  
   133  		"fmt": func() (cli.Command, error) {
   134  			return &command.FmtCommand{
   135  				Meta: meta,
   136  			}, nil
   137  		},
   138  
   139  		"get": func() (cli.Command, error) {
   140  			return &command.GetCommand{
   141  				Meta: meta,
   142  			}, nil
   143  		},
   144  
   145  		"graph": func() (cli.Command, error) {
   146  			return &command.GraphCommand{
   147  				Meta: meta,
   148  			}, nil
   149  		},
   150  
   151  		"import": func() (cli.Command, error) {
   152  			return &command.ImportCommand{
   153  				Meta: meta,
   154  			}, nil
   155  		},
   156  
   157  		"init": func() (cli.Command, error) {
   158  			return &command.InitCommand{
   159  				Meta: meta,
   160  			}, nil
   161  		},
   162  
   163  		"internal-plugin": func() (cli.Command, error) {
   164  			return &command.InternalPluginCommand{
   165  				Meta: meta,
   166  			}, nil
   167  		},
   168  
   169  		"output": func() (cli.Command, error) {
   170  			return &command.OutputCommand{
   171  				Meta: meta,
   172  			}, nil
   173  		},
   174  
   175  		"plan": func() (cli.Command, error) {
   176  			return &command.PlanCommand{
   177  				Meta: meta,
   178  			}, nil
   179  		},
   180  
   181  		"providers": func() (cli.Command, error) {
   182  			return &command.ProvidersCommand{
   183  				Meta: meta,
   184  			}, nil
   185  		},
   186  
   187  		"push": func() (cli.Command, error) {
   188  			return &command.PushCommand{
   189  				Meta: meta,
   190  			}, nil
   191  		},
   192  
   193  		"refresh": func() (cli.Command, error) {
   194  			return &command.RefreshCommand{
   195  				Meta: meta,
   196  			}, nil
   197  		},
   198  
   199  		"show": func() (cli.Command, error) {
   200  			return &command.ShowCommand{
   201  				Meta: meta,
   202  			}, nil
   203  		},
   204  
   205  		"taint": func() (cli.Command, error) {
   206  			return &command.TaintCommand{
   207  				Meta: meta,
   208  			}, nil
   209  		},
   210  
   211  		"validate": func() (cli.Command, error) {
   212  			return &command.ValidateCommand{
   213  				Meta: meta,
   214  			}, nil
   215  		},
   216  
   217  		"version": func() (cli.Command, error) {
   218  			return &command.VersionCommand{
   219  				Meta:              meta,
   220  				Revision:          GitCommit,
   221  				Version:           Version,
   222  				VersionPrerelease: VersionPrerelease,
   223  				CheckFunc:         commandVersionCheck,
   224  			}, nil
   225  		},
   226  
   227  		"untaint": func() (cli.Command, error) {
   228  			return &command.UntaintCommand{
   229  				Meta: meta,
   230  			}, nil
   231  		},
   232  
   233  		"workspace": func() (cli.Command, error) {
   234  			return &command.WorkspaceCommand{
   235  				Meta: meta,
   236  			}, nil
   237  		},
   238  
   239  		"workspace list": func() (cli.Command, error) {
   240  			return &command.WorkspaceListCommand{
   241  				Meta: meta,
   242  			}, nil
   243  		},
   244  
   245  		"workspace select": func() (cli.Command, error) {
   246  			return &command.WorkspaceSelectCommand{
   247  				Meta: meta,
   248  			}, nil
   249  		},
   250  
   251  		"workspace show": func() (cli.Command, error) {
   252  			return &command.WorkspaceShowCommand{
   253  				Meta: meta,
   254  			}, nil
   255  		},
   256  
   257  		"workspace new": func() (cli.Command, error) {
   258  			return &command.WorkspaceNewCommand{
   259  				Meta: meta,
   260  			}, nil
   261  		},
   262  
   263  		"workspace delete": func() (cli.Command, error) {
   264  			return &command.WorkspaceDeleteCommand{
   265  				Meta: meta,
   266  			}, nil
   267  		},
   268  
   269  		//-----------------------------------------------------------
   270  		// Plumbing
   271  		//-----------------------------------------------------------
   272  
   273  		"debug": func() (cli.Command, error) {
   274  			return &command.DebugCommand{
   275  				Meta: meta,
   276  			}, nil
   277  		},
   278  
   279  		"debug json2dot": func() (cli.Command, error) {
   280  			return &command.DebugJSON2DotCommand{
   281  				Meta: meta,
   282  			}, nil
   283  		},
   284  
   285  		"force-unlock": func() (cli.Command, error) {
   286  			return &command.UnlockCommand{
   287  				Meta: meta,
   288  			}, nil
   289  		},
   290  
   291  		"state": func() (cli.Command, error) {
   292  			return &command.StateCommand{}, nil
   293  		},
   294  
   295  		"state list": func() (cli.Command, error) {
   296  			return &command.StateListCommand{
   297  				Meta: meta,
   298  			}, nil
   299  		},
   300  
   301  		"state rm": func() (cli.Command, error) {
   302  			return &command.StateRmCommand{
   303  				StateMeta: command.StateMeta{
   304  					Meta: meta,
   305  				},
   306  			}, nil
   307  		},
   308  
   309  		"state mv": func() (cli.Command, error) {
   310  			return &command.StateMvCommand{
   311  				StateMeta: command.StateMeta{
   312  					Meta: meta,
   313  				},
   314  			}, nil
   315  		},
   316  
   317  		"state pull": func() (cli.Command, error) {
   318  			return &command.StatePullCommand{
   319  				Meta: meta,
   320  			}, nil
   321  		},
   322  
   323  		"state push": func() (cli.Command, error) {
   324  			return &command.StatePushCommand{
   325  				Meta: meta,
   326  			}, nil
   327  		},
   328  
   329  		"state show": func() (cli.Command, error) {
   330  			return &command.StateShowCommand{
   331  				Meta: meta,
   332  			}, nil
   333  		},
   334  	}
   335  }
   336  
   337  // makeShutdownCh creates an interrupt listener and returns a channel.
   338  // A message will be sent on the channel for every interrupt received.
   339  func makeShutdownCh() <-chan struct{} {
   340  	resultCh := make(chan struct{})
   341  
   342  	signalCh := make(chan os.Signal, 4)
   343  	signal.Notify(signalCh, ignoreSignals...)
   344  	signal.Notify(signalCh, forwardSignals...)
   345  	go func() {
   346  		for {
   347  			<-signalCh
   348  			resultCh <- struct{}{}
   349  		}
   350  	}()
   351  
   352  	return resultCh
   353  }
   354  
   355  func credentialsSource(config *Config) auth.CredentialsSource {
   356  	creds := auth.NoCredentials
   357  	if len(config.Credentials) > 0 {
   358  		staticTable := map[svchost.Hostname]map[string]interface{}{}
   359  		for userHost, creds := range config.Credentials {
   360  			host, err := svchost.ForComparison(userHost)
   361  			if err != nil {
   362  				// We expect the config was already validated by the time we get
   363  				// here, so we'll just ignore invalid hostnames.
   364  				continue
   365  			}
   366  			staticTable[host] = creds
   367  		}
   368  		creds = auth.StaticCredentialsSource(staticTable)
   369  	}
   370  
   371  	for helperType, helperConfig := range config.CredentialsHelpers {
   372  		log.Printf("[DEBUG] Searching for credentials helper named %q", helperType)
   373  		available := pluginDiscovery.FindPlugins("credentials", globalPluginDirs())
   374  		available = available.WithName(helperType)
   375  		if available.Count() == 0 {
   376  			log.Printf("[ERROR] Unable to find credentials helper %q; ignoring", helperType)
   377  			break
   378  		}
   379  
   380  		selected := available.Newest()
   381  
   382  		helperSource := auth.HelperProgramCredentialsSource(selected.Path, helperConfig.Args...)
   383  		creds = auth.Credentials{
   384  			creds,
   385  			auth.CachingCredentialsSource(helperSource), // cached because external operation may be slow/expensive
   386  		}
   387  
   388  		// There should only be zero or one "credentials_helper" blocks. We
   389  		// assume that the config was validated earlier and so we don't check
   390  		// for extras here.
   391  		break
   392  	}
   393  
   394  	return creds
   395  }