Files
infra-provisioner/main.go

310 lines
8.1 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"
)
func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func parseManifest(manifestPath string, tfActions *Actions) (*Manifest, error) {
log.Printf("Parsing Manifest")
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.Workspace == "" {
var workspace string
if manifest.Cluster != "" {
workspace = manifest.Cluster
log.Printf("Got workspace from manifest.cluster: %v", workspace)
} else if manifest.Deployment.Cluster != "" {
workspace = manifest.Deployment.Cluster
log.Printf("Got workspace from manifest.deployment.cluster: %v", workspace)
} else {
workspace = defaultWorkspaceMap[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
stateStoreBucketName := DefaultStateStorageBucket
if _, ok := storageBackendBucketMap[manifest.InfraVertical]; ok {
if _, ok = storageBackendBucketMap[manifest.InfraVertical][manifest.ExtraResources.Environment]; ok {
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, runAdditionalScripts bool) (*Actions, error) {
log.Printf("Setting actions. \nTemplateOnly: %v, \nPlan: %v, \nDestroy: %v, \n" +
"RunAdditionalScripts: %v\n", templateOnly, plan, destroy, runAdditionalScripts)
var actions Actions
actions.TemplateOnly = templateOnly
actions.Plan = plan
actions.Destroy = destroy
actions.RunAdditionalScripts = runAdditionalScripts
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"), false)
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
}
if manifest.Actions.Apply {
sendResourceDeploymentStatus(manifest)
}
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: "run-additional-scripts",
Usage: "Runs configured scripts before and after terraform apply",
Aliases: []string{"s"},
},
&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 rds database instance",
Action: func(c *cli.Context) error {
log.Printf("Provisioning the rds database resource specified in the manifest.")
err := executeCommand(c, RESOURCE_RDS, DIR_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 {
log.Printf("Provisioning the redis resource specified in the manifest.")
err := executeCommand(c, RESOURCE_ELASTIC_CACHE, DIR_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 {
log.Printf("Provisioning the document db resource specified in the manifest.")
err := executeCommand(c, RESOURCE_DOCDB, DIR_DOCDB_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 {
log.Printf("Provisioning the iam roles specified in the manifest.")
err := executeCommand(c, RESOURCE_AWS_ROLES, DIR_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 {
log.Printf("Provisioning the s3 buckets specified in the manifest.")
err := executeCommand(c, RESOURCE_S3_BUCKETS, DIR_S3_BUCKETS_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 {
log.Printf("Provisioning the dynamo db tables specified in the manifest.")
err := executeCommand(c, RESOURCE_DYNAMODB, DIR_DYNAMODB_TF)
if err != nil {
log.Fatalf("\nErr: %v", err)
return err
}
return nil
},
},
{
Name: "aurora-db",
Usage: "Provision aurora database instance/cluster",
Action: func(c *cli.Context) error {
log.Printf("Provisioning the aurora database resource specified in the manifest.")
err := executeCommand(c, RESOURCE_AURORADB, DIR_AURORADB_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 {
log.Printf("Provisioning all resources specified in the manifest.")
tfActions, err := setActions(c.Bool("template-only"), c.Bool("plan"), c.Bool("destroy"), false)
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
},
},
},
}
err := app.Run(os.Args)
if err != nil {
log.Fatalf("\nErr: %v", err)
}
}