github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/builtin/infra/aws/infra.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/otto/helper/bindata"
    10  	"github.com/hashicorp/otto/helper/sshagent"
    11  	"github.com/hashicorp/otto/helper/terraform"
    12  	"github.com/hashicorp/otto/infrastructure"
    13  	"github.com/hashicorp/otto/ui"
    14  	"github.com/mitchellh/go-homedir"
    15  )
    16  
    17  //go:generate go-bindata -pkg=aws -nomemcopy -nometadata ./data/...
    18  
    19  // Infra returns the infrastructure.Infrastructure implementation.
    20  // This function is a infrastructure.Factory.
    21  func Infra() (infrastructure.Infrastructure, error) {
    22  	return &terraform.Infrastructure{
    23  		CredsFunc:       creds,
    24  		VerifyCredsFunc: verifyCreds,
    25  		Bindata: &bindata.Data{
    26  			Asset:    Asset,
    27  			AssetDir: AssetDir,
    28  		},
    29  		Variables: map[string]string{
    30  			"aws_region": "us-east-1",
    31  		},
    32  	}, nil
    33  }
    34  
    35  func creds(ctx *infrastructure.Context) (map[string]string, error) {
    36  	fields := []*ui.InputOpts{
    37  		&ui.InputOpts{
    38  			Id:          "aws_access_key",
    39  			Query:       "AWS Access Key",
    40  			Description: "AWS access key used for API calls.",
    41  			EnvVars:     []string{"AWS_ACCESS_KEY_ID"},
    42  		},
    43  		&ui.InputOpts{
    44  			Id:          "aws_secret_key",
    45  			Query:       "AWS Secret Key",
    46  			Description: "AWS secret key used for API calls.",
    47  			EnvVars:     []string{"AWS_SECRET_ACCESS_KEY"},
    48  		},
    49  		&ui.InputOpts{
    50  			Id:          "ssh_public_key_path",
    51  			Query:       "SSH Public Key Path",
    52  			Description: "Path to an SSH public key that will be granted access to EC2 instances",
    53  			Default:     "~/.ssh/id_rsa.pub",
    54  			EnvVars:     []string{"AWS_SSH_PUBLIC_KEY_PATH"},
    55  		},
    56  	}
    57  
    58  	result := make(map[string]string, len(fields))
    59  	for _, f := range fields {
    60  		value, err := ctx.Ui.Input(f)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  
    65  		result[f.Id] = value
    66  	}
    67  
    68  	// Load SSH public key contents
    69  	sshPath, err := homedir.Expand(result["ssh_public_key_path"])
    70  	if err != nil {
    71  		return nil, fmt.Errorf("Error expanding homedir for SSH key: %s", err)
    72  	}
    73  
    74  	sshKey, err := ioutil.ReadFile(sshPath)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("Error reading SSH key: %s", err)
    77  	}
    78  	result["ssh_public_key"] = string(sshKey)
    79  
    80  	return result, nil
    81  }
    82  
    83  func verifyCreds(ctx *infrastructure.Context) error {
    84  	found, err := sshagent.HasKey(ctx.InfraCreds["ssh_public_key"])
    85  	if err != nil {
    86  		return sshAgentError(err)
    87  	}
    88  	if !found {
    89  		ok, _ := guessAndLoadPrivateKey(
    90  			ctx.Ui, ctx.InfraCreds["ssh_public_key_path"])
    91  		if ok {
    92  			ctx.Ui.Message(
    93  				"A private key was found and loaded. Otto will now check\n" +
    94  					"the SSH Agent again and continue if the correct key is loaded")
    95  
    96  			found, err = sshagent.HasKey(ctx.InfraCreds["ssh_public_key"])
    97  			if err != nil {
    98  				return sshAgentError(err)
    99  			}
   100  		}
   101  	}
   102  
   103  	if !found {
   104  		return sshAgentError(fmt.Errorf(
   105  			"You specified an SSH public key of: %q, but the private key from this\n"+
   106  				"keypair is not loaded the SSH Agent. To load it, run:\n\n"+
   107  				"  ssh-add [PATH_TO_PRIVATE_KEY]",
   108  			ctx.InfraCreds["ssh_public_key_path"]))
   109  	}
   110  	return nil
   111  }
   112  
   113  func sshAgentError(err error) error {
   114  	return fmt.Errorf(
   115  		"Otto uses your SSH Agent to authenticate with instances created in\n"+
   116  			"AWS, but it could not verify that your SSH key is loaded into the agent.\n"+
   117  			"The error message follows:\n\n%s", err)
   118  }
   119  
   120  // guessAndLoadPrivateKey takes a path to a public key and determines if a
   121  // private key exists by just stripping ".pub" from the end of it. if so,
   122  // it attempts to load that key into the agent.
   123  func guessAndLoadPrivateKey(ui ui.Ui, pubKeyPath string) (bool, error) {
   124  	fullPath, err := homedir.Expand(pubKeyPath)
   125  	if err != nil {
   126  		return false, err
   127  	}
   128  	if !strings.HasSuffix(fullPath, ".pub") {
   129  		return false, fmt.Errorf("No .pub suffix, cannot guess path.")
   130  	}
   131  	privKeyGuess := strings.TrimSuffix(fullPath, ".pub")
   132  	if _, err := os.Stat(privKeyGuess); os.IsNotExist(err) {
   133  		return false, fmt.Errorf("No file at guessed path.")
   134  	}
   135  
   136  	ui.Header("Loading key into SSH Agent")
   137  	ui.Message(fmt.Sprintf(
   138  		"The key you provided (%s) was not found in your SSH Agent.", pubKeyPath))
   139  	ui.Message(fmt.Sprintf(
   140  		"However, Otto found a private key here: %s", privKeyGuess))
   141  	ui.Message(fmt.Sprintf(
   142  		"Automatically running 'ssh-add %s'.", privKeyGuess))
   143  	ui.Message("If your SSH key has a passphrase, you will be prompted for it.")
   144  	ui.Message("")
   145  
   146  	if err := sshagent.Add(ui, privKeyGuess); err != nil {
   147  		return false, err
   148  	}
   149  
   150  	return true, nil
   151  }