Files
infra-provisioner/main.go

277 lines
6.6 KiB
Go

package main
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"github.com/a8m/envsubst"
"github.com/asaskevich/govalidator"
"github.com/caarlos0/env/v6"
"github.com/urfave/cli/v2"
)
var tfActions Actions
func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func parseManifest(manifestPath string, tfActions *Actions) (*Manifest, error) {
rawManifest, err := ioutil.ReadFile(manifestPath)
if err != nil {
log.Fatalf("\nErr: %v", err)
return nil, err
}
//Do Environment substitution
manifestWithEnvironment, err := envsubst.Bytes(rawManifest)
if err != nil {
log.Fatalf("\nErr: %v", err)
return nil, err
}
var manifest Manifest
err = json.Unmarshal(manifestWithEnvironment, &manifest)
if err != nil {
log.Fatalf("\nErr: %v", err)
return nil, err
}
err = env.Parse(&manifest)
if err != nil {
log.Fatalf("\nErr: %v", err)
return nil, err
}
_, err = govalidator.ValidateStruct(manifest)
if err != nil {
log.Fatalf("\nErr: %v", err.Error())
return nil, err
}
manifest.Actions = tfActions
if manifest.ExtraResources.Environment == "" {
manifest.ExtraResources.Environment = manifest.Environment
}
if manifest.InfraVertical == "" {
manifest.InfraVertical = DefaultInfraVertical
}
if manifest.Team.Owner == "" {
manifest.Team.Owner = ownerMap[manifest.InfraVertical]
}
if manifest.Team.Product == "" {
manifest.Team.Product = productMap[manifest.InfraVertical]
}
if manifest.ExtraResources.Database != nil && manifest.ExtraResources.Database.KmsKeyId == "" {
manifest.ExtraResources.Database.KmsKeyId = kmsKeyIdMap[manifest.InfraVertical][manifest.ExtraResources.Environment]
}
if manifest.Deployment == nil {
manifest.Deployment = &Deployment{}
}
if manifest.ExtraResources.Workspace == "" {
var workspace = ""
if manifest.Deployment.Cluster != "" {
workspace = manifest.Deployment.Cluster
log.Printf("Got workspace from manifest.deployment.cluster: %v", workspace)
} else {
workspace = workspaceMap[manifest.InfraVertical][manifest.ExtraResources.Environment]
log.Printf("Got workspace from workspace map: %v", workspace)
}
manifest.ExtraResources.Workspace = workspace
}
if globalTagsMapVar, ok := globalTagsMap[manifest.ExtraResources.Environment]; ok {
manifest.ExtraResources.GlobalTags = &GlobalTags{}
marshal, err := json.Marshal(globalTagsMapVar)
if err != nil {
log.Fatalf("\nErr: %v", err)
return nil, err
}
err = json.Unmarshal(marshal, &manifest.ExtraResources.GlobalTags)
if err != nil {
log.Fatalf("\nErr: %v", err)
return nil, err
}
}
manifest.NameSuffix = DeploymentNameSuffix
//Fix: Required for templating storage backend
var stateStoreBucketName = storageBackendBucketMap[manifest.InfraVertical][manifest.ExtraResources.Environment]
manifest.StateStoreBackend = &StateStoreBackend{
BucketName: stateStoreBucketName,
AWSProfile: storageBackendAWSProfileMap[stateStoreBucketName],
}
return &manifest, nil
}
func setActions(templateOnly bool, plan bool, destroy bool) (*Actions, error) {
var actions Actions
actions.TemplateOnly = templateOnly
actions.Plan = plan
actions.Destroy = destroy
if !plan {
actions.Apply = true
}
return &actions, nil
}
func executeCommand(c *cli.Context, resourceName string, resourceDir string) error {
tfActions, err := setActions(c.Bool("template-only"), c.Bool("plan"), c.Bool("destroy"))
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
manifest, err := parseManifest(c.String("manifest"), tfActions)
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
err = provisionResource(resourceName, resourceDir, manifest)
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
}
func main() {
app := &cli.App{
Name: "infra-provisioner",
Version: "2.0.0",
//Common flags for all subcommands
Flags: []cli.Flag{
&cli.StringFlag{
Name: "manifest",
Usage: "Path of the manifest file",
Aliases: []string{"m"},
Required: true,
},
&cli.BoolFlag{
Name: "template-only",
Usage: "Provisions just the template for given resource",
Aliases: []string{"t"},
},
&cli.BoolFlag{
Name: "plan",
Usage: "Run plan for the given resources",
Aliases: []string{"p"},
},
&cli.BoolFlag{
Name: "destroy",
Usage: "Run destroy for the given terraform resources",
Aliases: []string{"d"},
},
},
Commands: []*cli.Command{
{
Name: "database",
Usage: "Provision database instance",
Action: func(c *cli.Context) error {
err := executeCommand(c, "rds", "rds-tf")
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
},
},
{
Name: "redis",
Usage: "Provision elastic cache instance",
Action: func(c *cli.Context) error {
err := executeCommand(c, "elasticCache", "elastic-cache-tf")
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
},
},
{
Name: "docdb",
Usage: "Provision document db instance",
Action: func(c *cli.Context) error {
err := executeCommand(c, "docdb", "document-db-tf")
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
},
},
{
Name: "iam-roles",
Usage: "Provision iam service roles",
Action: func(c *cli.Context) error {
err := executeCommand(c, "roles", "aws-roles-tf")
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
},
},
{
Name: "s3-buckets",
Usage: "Provision s3 buckets",
Action: func(c *cli.Context) error {
err := executeCommand(c, "s3", "aws-s3-bucket-tf")
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
},
},
{
Name: "dynamo-db",
Usage: "Provision dynamo db tables",
Action: func(c *cli.Context) error {
err := executeCommand(c, "dynamodb", "dynamo-db-tf")
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
},
},
{
Name: "all",
Usage: "Provision all resources",
Action: func(c *cli.Context) error {
tfActions, err := setActions(c.Bool("template-only"), c.Bool("plan"), c.Bool("destroy"))
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
manifest, err := parseManifest(c.String("manifest"), tfActions)
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
err = provisionAllResource(manifest)
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
},
},
},
}
app.Run(os.Args)
}