internal/setup/setup.go

Summary

Maintainability
A
35 mins
Test Coverage
// Package setup provides up.json initialization.
package setup

import (
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"
    "sort"

    "github.com/mitchellh/go-homedir"
    "github.com/tj/go/term"
    "github.com/tj/survey"

    "github.com/apex/up/internal/util"
    "github.com/apex/up/internal/validate"
    "github.com/apex/up/platform/aws/regions"
)

// ErrNoCredentials is the error returned when no AWS credential profiles are available.
var ErrNoCredentials = errors.New("no credentials")

// config saved to up.json
type config struct {
    Name    string   `json:"name"`
    Profile string   `json:"profile"`
    Regions []string `json:"regions"`
}

// questions for the user.
var questions = []*survey.Question{
    {
        Name: "name",
        Prompt: &survey.Input{
            Message: "Project name:",
            Default: defaultName(),
        },
        Validate: validateName,
    },
    {
        Name: "profile",
        Prompt: &survey.Select{
            Message:  "AWS profile:",
            Options:  awsProfiles(),
            Default:  os.Getenv("AWS_PROFILE"),
            PageSize: 10,
        },
        Validate: survey.Required,
    },
    {
        Name: "region",
        Prompt: &survey.Select{
            Message:  "AWS region:",
            Options:  regions.Names,
            Default:  defaultRegion(),
            PageSize: 15,
        },
        Validate: survey.Required,
    },
}

// Create an up.json file for the user.
func Create() error {
    var in struct {
        Name    string `json:"name"`
        Profile string `json:"profile"`
        Region  string `json:"region"`
    }

    if len(awsProfiles()) == 0 {
        return ErrNoCredentials
    }

    println()

    // confirm
    var ok bool
    err := survey.AskOne(&survey.Confirm{
        Message: fmt.Sprintf("No up.json found, create a new project?"),
        Default: true,
    }, &ok, nil)

    if err != nil {
        return err
    }

    if !ok {
        return errors.New("aborted")
    }

    // prompt
    term.MoveUp(1)
    term.ClearLine()
    if err := survey.Ask(questions, &in); err != nil {
        return err
    }

    c := config{
        Name:    in.Name,
        Profile: in.Profile,
        Regions: []string{
            regions.GetIdByName(in.Region),
        },
    }

    b, _ := json.MarshalIndent(c, "", "  ")
    return ioutil.WriteFile("up.json", b, 0644)
}

// defaultName returns the default app name.
// The name is only inferred if it is valid.
func defaultName() string {
    dir, _ := os.Getwd()
    name := filepath.Base(dir)
    if validate.Name(name) != nil {
        return ""
    }
    return name
}

// defaultRegion returns the default aws region.
func defaultRegion() string {
    if s := os.Getenv("AWS_DEFAULT_REGION"); s != "" {
        return s
    }

    if s := os.Getenv("AWS_REGION"); s != "" {
        return s
    }

    return ""
}

// validateName validates the name prompt.
func validateName(v interface{}) error {
    if err := validate.Name(v.(string)); err != nil {
        return err
    }

    return survey.Required(v)
}

// awsProfiles returns the AWS profiles found.
func awsProfiles() []string {
    path, err := homedir.Expand("~/.aws/credentials")
    if err != nil {
        return nil
    }

    f, err := os.Open(path)
    if err != nil {
        return nil
    }
    defer f.Close()

    s, err := util.ParseSections(f)
    if err != nil {
        return nil
    }

    sort.Strings(s)
    return s
}