Skip to content

Commit

Permalink
Merge branch 'master' into implement-poc-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
Direside committed Dec 26, 2019
2 parents 920a4bb + c04455b commit 05d34b6
Show file tree
Hide file tree
Showing 31 changed files with 662 additions and 198 deletions.
101 changes: 62 additions & 39 deletions cmd/create.go
Expand Up @@ -35,62 +35,82 @@ func Create(projectName string, outDir string, t *templator.Templator) string {
log.Fatalln(aurora.Red(emoji.Sprintf(":exclamation: Error creating root: %v ", err)))
}

// @TODO : Clean up the following aws stuff
projectConfig := defaultProjConfig(projectName)

chooseCloudProvider(&projectConfig)

s := secrets.GetSecrets(rootDir)

fillProviderDetails(&projectConfig, s)

var wg sync.WaitGroup
util.TemplateFileIfDoesNotExist(rootDir, util.CommitYml, t.Commit0, &wg, projectConfig)
util.TemplateFileIfDoesNotExist(rootDir, ".gitignore", t.GitIgnore, &wg, projectName)

wg.Wait()
return rootDir
}

func chooseCloudProvider(projectConfig *util.ProjectConfiguration) {
providerPrompt := promptui.Select{
Label: "Select Cloud Provider",
Items: []string{"Amazon AWS", "Google GCP", "Microsoft Azure"},
}

_, _, err = providerPrompt.Run()

regionPrompt := promptui.Select{
Label: "Select AWS Region ",
Items: []string{"us-west-1", "us-west-2", "us-east-1", "us-east-2", "ca-central-1",
"eu-central-1", "eu-west-1", "ap-east-1", "ap-south-1"},
}

_, regionResult, err := regionPrompt.Run()

_, providerResult, err := providerPrompt.Run()
if err != nil {
log.Fatalf("Prompt failed %v\n", err)
panic(err)
}

s := secrets.GetSecrets(rootDir)

sess, err := session.NewSession(&aws.Config{
Region: aws.String(regionResult),
Credentials: credentials.NewStaticCredentials(s.AWS.AccessKeyID, s.AWS.SecretAccessKey, ""),
})
if providerResult == "Amazon AWS" {
// @TODO : Move this stuff from util into another package
projectConfig.Infrastructure.AWS = &util.AWS{}
regionPrompt := promptui.Select{
Label: "Select AWS Region ",
Items: []string{"us-west-1", "us-west-2", "us-east-1", "us-east-2", "ca-central-1",
"eu-central-1", "eu-west-1", "ap-east-1", "ap-south-1"},
}

svc := sts.New(sess)
input := &sts.GetCallerIdentityInput{}
_, regionResult, err := regionPrompt.Run()

awsCaller, err := svc.GetCallerIdentity(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
default:
log.Fatalf(aerr.Error())
}
} else {
log.Fatalf(err.Error())
if err != nil {
log.Fatalf("Prompt failed %v\n", err)
panic(err)
}
}

defaultProjConfig := defaultProjConfig(projectName)

defaultProjConfig.Infrastructure.AWS.Region = regionResult
if awsCaller != nil && awsCaller.Account != nil {
defaultProjConfig.Infrastructure.AWS.AccountID = *awsCaller.Account
projectConfig.Infrastructure.AWS.Region = regionResult
} else {
log.Fatalf("Only the AWS provider is available at this time")
}
}

var wg sync.WaitGroup
util.TemplateFileIfDoesNotExist(rootDir, util.CommitYml, t.Commit0, &wg, defaultProjConfig)
util.TemplateFileIfDoesNotExist(rootDir, ".gitignore", t.GitIgnore, &wg, projectName)
func fillProviderDetails(projectConfig *util.ProjectConfiguration, s secrets.Secrets) {
if projectConfig.Infrastructure.AWS != nil {
sess, err := session.NewSession(&aws.Config{
Region: aws.String(projectConfig.Infrastructure.AWS.Region),
Credentials: credentials.NewStaticCredentials(s.AWS.AccessKeyID, s.AWS.SecretAccessKey, ""),
})

svc := sts.New(sess)
input := &sts.GetCallerIdentityInput{}

awsCaller, err := svc.GetCallerIdentity(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
default:
log.Fatalf(aerr.Error())
}
} else {
log.Fatalf(err.Error())
}
}

wg.Wait()
return rootDir
if awsCaller != nil && awsCaller.Account != nil {
projectConfig.Infrastructure.AWS.AccountID = *awsCaller.Account
}
}
}

func defaultProjConfig(projectName string) util.ProjectConfiguration {
Expand All @@ -109,6 +129,9 @@ func defaultProjConfig(projectName string) util.ProjectConfiguration {
Language: "go",
GitRepo: "github.com/test/repo",
}},
Infrastructure: util.Infrastructure{
AWS: nil,
},
}
}

Expand Down
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -9,6 +9,7 @@ require (
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
github.com/gobuffalo/logger v1.0.1 // indirect
github.com/gobuffalo/packr/v2 v2.5.2
github.com/google/uuid v1.1.1
github.com/gorilla/mux v1.7.3
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -26,6 +26,8 @@ github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packr/v2 v2.5.2 h1:4EvjeIpQLZuRIljwnidYgbRXbr1yIzVRrESiLjqKj6s=
github.com/gobuffalo/packr/v2 v2.5.2/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
Expand Down
4 changes: 2 additions & 2 deletions internal/generate/generate_helper.go
Expand Up @@ -18,12 +18,12 @@ import (

func GenerateArtifactsHelper(t *templator.Templator, cfg *config.Commit0Config, pathPrefix string, runInit bool, runApply bool) {
var wg sync.WaitGroup
if !util.ValidateLanguage(cfg.Frontend.Framework) {
if cfg.Frontend.Framework != "" && !util.ValidateLanguage(cfg.Frontend.Framework) {
log.Fatalln(aurora.Red(emoji.Sprintf(":exclamation: '%s' is not a supported framework.", cfg.Frontend.Framework)))
}

for _, s := range cfg.Services {
if !util.ValidateLanguage(cfg.Frontend.Framework) {
if !util.ValidateLanguage(s.Language) {
log.Fatalln(aurora.Red(emoji.Sprintf(":exclamation: '%s' in service '%s' is not a supported language.", s.Name, s.Language)))
}
}
Expand Down
21 changes: 13 additions & 8 deletions internal/generate/terraform/generate.go
Expand Up @@ -17,11 +17,11 @@ import (

// @TODO : These are specific to a k8s version. If we make the version a config option we will need to change this
var amiLookup = map[string]string{
"us-east-1": "ami-0392bafc801b7520f",
"us-east-2": "ami-082bb518441d3954c",
"us-west-2": "ami-05d586e6f773f6abf",
"eu-west-1": "ami-059c6874350e63ca9",
"eu-central-1": "ami-0e21bc066a9dbabfa",
"us-east-1": "ami-07d6c8e62ce328a10",
"us-east-2": "ami-053250833d1030033",
"us-west-2": "ami-07be7092831897fd6",
"eu-west-1": "ami-02dca57ad67c7bf57",
"eu-central-1": "ami-03fbd442f4f3aa689",
}

func Generate(t *templator.Templator, cfg *config.Commit0Config, wg *sync.WaitGroup, pathPrefix string) {
Expand Down Expand Up @@ -58,7 +58,6 @@ func GetOutputs(cfg *config.Commit0Config, pathPrefix string, outputs []string)

// Init sets up anything required by Execute
func Init(cfg *config.Commit0Config, pathPrefix string) {
// @TODO : Change this check. Most likely we should discover the accountid
if cfg.Infrastructure.AWS.AccountId != "" {
log.Println("Preparing aws environment...")

Expand All @@ -69,13 +68,19 @@ func Init(cfg *config.Commit0Config, pathPrefix string) {
// @TODO : A check here would be nice to see if this stuff exists first, mostly for testing
log.Println(aurora.Cyan(emoji.Sprintf(":alarm_clock: Initializing remote backend...")))
util.ExecuteCommand(exec.Command("terraform", "init"), filepath.Join(pathPrefix, "bootstrap/remote-state"), envars)
util.ExecuteCommand(exec.Command("terraform", "apply", "-auto-approve"), filepath.Join(pathPrefix, "bootstrap/remote-state"), envars)
// @TODO : Properly loop through environments when we support that
util.ExecuteCommand(exec.Command("terraform", "apply", "-auto-approve", "-var", "environment=staging", "-state-out=staging.tfstate"), filepath.Join(pathPrefix, "bootstrap/remote-state"), envars)
util.ExecuteCommand(exec.Command("terraform", "apply", "-auto-approve", "-var", "environment=production", "-state-out=staging.tfstate"), filepath.Join(pathPrefix, "bootstrap/remote-state"), envars)

log.Println("Creating users...")
util.ExecuteCommand(exec.Command("terraform", "init"), filepath.Join(pathPrefix, "bootstrap/create-users"), envars)
util.ExecuteCommand(exec.Command("terraform", "apply", "-auto-approve"), filepath.Join(pathPrefix, "bootstrap/create-users"), envars)

}
}

// Execute terrafrom init & plan. May modify the config passed in
func Execute(cfg *config.Commit0Config, pathPrefix string) {
// @TODO : Change this check. Most likely we should discover the accountid
if cfg.Infrastructure.AWS.AccountId != "" {
log.Println("Preparing aws environment...")

Expand Down
4 changes: 3 additions & 1 deletion internal/util/projectAttributes.go
@@ -1,5 +1,7 @@
package util

// @TODO : Move this stuff from util into another package

const (
Go = "go"
React = "react"
Expand Down Expand Up @@ -43,7 +45,7 @@ type ProjectConfiguration struct {
}

type Infrastructure struct {
AWS AWS `json:"aws"`
AWS *AWS `json:"aws"`
}
type AWS struct {
AccountID string
Expand Down
99 changes: 77 additions & 22 deletions internal/util/secrets/secrets.go
Expand Up @@ -18,7 +18,9 @@ import (

// Secrets - AWS prompted credentials
type Secrets struct {
AWS AWS
AWS AWS
CircleCIKey string
GithubToken string
}

type AWS struct {
Expand Down Expand Up @@ -47,21 +49,13 @@ func GetSecrets(baseDir string) Secrets {
if err != nil {
log.Fatal(err)
}
credsFile := filepath.Join(usr.HomeDir, ".aws/credentials")

var awsSecrets Secrets
var secrets Secrets

// Load the credentials file to look for profiles
credsFile := filepath.Join(usr.HomeDir, ".aws/credentials")
creds, err := ioutil.ReadFile(credsFile)
profiles, err := GetAWSProfiles()
if err == nil {
// Get all profiles
re := regexp.MustCompile(`\[(.*)\]`)
profileMatches := re.FindAllStringSubmatch(string(creds), -1)
profiles := make([]string, len(profileMatches))
for i, p := range profileMatches {
profiles[i] = p[1]
}

profilePrompt := promptui.Select{
Label: "Select AWS Profile",
Items: profiles,
Expand All @@ -71,7 +65,7 @@ func GetSecrets(baseDir string) Secrets {

creds, err := credentials.NewSharedCredentials(credsFile, profileResult).Get()
if err == nil {
awsSecrets = Secrets{
secrets = Secrets{
AWS: AWS{
AccessKeyID: creds.AccessKeyID,
SecretAccessKey: creds.SecretAccessKey,
Expand All @@ -81,13 +75,51 @@ func GetSecrets(baseDir string) Secrets {
}

// We couldn't load the credentials file, get the user to just paste them
if awsSecrets == (Secrets{}) {
awsSecrets = promptCredentials()
if secrets.AWS == (AWS{}) {
promptAWSCredentials(&secrets)
}

if secrets.CircleCIKey == "" || secrets.GithubToken == "" {
ciPrompt := promptui.Select{
Label: "Which Continuous integration provider do you want to use?",
Items: []string{"CircleCI", "GitHub Actions"},
}

_, ciResult, _ := ciPrompt.Run()

if ciResult == "CircleCI" {
promptCircleCICredentials(&secrets)
} else if ciResult == "GitHub Actions" {
promptGitHubCredentials(&secrets)
}
}

writeSecrets(secretsFile, awsSecrets)
return awsSecrets
writeSecrets(secretsFile, secrets)
return secrets
}
}

// GetAWSProfiles returns a list of AWS forprofiles set up on the user's sytem
func GetAWSProfiles() ([]string, error) {
usr, err := user.Current()
if err != nil {
return nil, err
}

// Load the credentials file to look for profiles
credsFile := filepath.Join(usr.HomeDir, ".aws/credentials")
creds, err := ioutil.ReadFile(credsFile)
if err != nil {
return nil, err
}
// Get all profiles
re := regexp.MustCompile(`\[(.*)\]`)
profileMatches := re.FindAllStringSubmatch(string(creds), -1)
profiles := make([]string, len(profileMatches))
for i, p := range profileMatches {
profiles[i] = p[1]
}
return profiles, nil
}

func readSecrets(secretsFile string) Secrets {
Expand Down Expand Up @@ -122,7 +154,7 @@ func writeSecrets(secretsFile string, s Secrets) {
}
}

func promptCredentials() Secrets {
func promptAWSCredentials(secrets *Secrets) {

validateAKID := func(input string) error {
// 20 uppercase alphanumeric characters
Expand Down Expand Up @@ -167,12 +199,35 @@ func promptCredentials() Secrets {
panic(err)
}

awsSecrets := Secrets{}
awsSecrets.AWS.AccessKeyID = accessKeyIDResult
awsSecrets.AWS.SecretAccessKey = secretAccessKeyResult
secrets.AWS.AccessKeyID = accessKeyIDResult
secrets.AWS.SecretAccessKey = secretAccessKeyResult
}

return awsSecrets
func promptGitHubCredentials(secrets *Secrets) {
}

func promptCircleCICredentials(secrets *Secrets) {
validateKey := func(input string) error {
// 40 base64 characters
var awsSecretAccessKeyPat = regexp.MustCompile(`^[A-Za-z0-9]{40}$`)
if !awsSecretAccessKeyPat.MatchString(input) {
return errors.New("Invalid CircleCI API Key")
}
return nil
}

prompt := promptui.Prompt{
Label: "Please enter your CircleCI API key (you can create one at https://circleci.com/account/api) ",
Validate: validateKey,
}

key, err := prompt.Run()

if err != nil {
log.Fatalf("Prompt failed %v\n", err)
panic(err)
}
secrets.CircleCIKey = key
}

func fileExists(filename string) bool {
Expand Down
2 changes: 2 additions & 0 deletions internal/util/util.go
Expand Up @@ -12,6 +12,7 @@ import (
"text/template"

"github.com/kyokomi/emoji"
"github.com/google/uuid"
"github.com/logrusorgru/aurora"
)

Expand All @@ -31,6 +32,7 @@ var FuncMap = template.FuncMap{
"Title": strings.Title,
"ToLower": strings.ToLower,
"CleanGoIdentifier": CleanGoIdentifier,
"GenerateUUID": uuid.New,
}

func GetCwd() string {
Expand Down

0 comments on commit 05d34b6

Please sign in to comment.