266 lines
8.0 KiB
Go
266 lines
8.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/Masterminds/sprig/v3"
|
|
"github.com/navi-infra/infra-provisioner/v2/bindata"
|
|
)
|
|
|
|
type void struct{}
|
|
|
|
const TemplatesDir = "templates"
|
|
const InitScript = "./deploy.sh"
|
|
const deploymentPortalUrl = "deployment-portal-backend.cmd.navi-tech.in"
|
|
|
|
func getInfraVertical(vertical string) string {
|
|
return InfraVerticals[vertical]
|
|
}
|
|
|
|
func getDeploymentPortalHostUrl(vertical string) string {
|
|
env := os.Getenv("TESTING_ENV")
|
|
if env == "dev" {
|
|
return "http://localhost:8080"
|
|
}
|
|
return fmt.Sprintf("https://%s%s", getInfraVertical(vertical), deploymentPortalUrl)
|
|
}
|
|
|
|
func getDeploymentPortalUrl(infraVertical string, environment string, name string) string {
|
|
return fmt.Sprintf("%s/api/manifest/status/env/%s/name/%s",
|
|
getDeploymentPortalHostUrl(infraVertical),
|
|
environment,
|
|
name)
|
|
}
|
|
|
|
var extraResourcesDeploymentStatus ExtraResourcesDeployment
|
|
|
|
func provisionResource(resourceName, resourceDir string, manifest *Manifest) error {
|
|
|
|
err := templateResourceTf(resourceName, resourceDir, manifest, resourceDir)
|
|
|
|
if err != nil {
|
|
log.Fatalf("\nErr: %v", err)
|
|
return err
|
|
}
|
|
|
|
if !manifest.Actions.TemplateOnly {
|
|
err = executeResourceTf(resourceDir)
|
|
if err != nil {
|
|
log.Fatalf("\nErr: %v", err)
|
|
return err
|
|
}
|
|
deploymentStatus := true
|
|
if manifest.Actions.Destroy {
|
|
deploymentStatus = false
|
|
}
|
|
setResourceDeploymentStatus(resourceName, deploymentStatus, manifest)
|
|
}
|
|
return nil
|
|
}
|
|
func setExtraResourceStatus(resourceName string, deploymentStatus bool, resource *ExtraResourceData) {
|
|
resource.ResourceName = resourceName
|
|
resource.IsDeployed = deploymentStatus
|
|
}
|
|
|
|
func appendExtraResourceStatus(resourceName string, deploymentStatus bool, resource *[]ExtraResourceData) {
|
|
var extraResource ExtraResourceData
|
|
extraResource.ResourceName = resourceName
|
|
extraResource.IsDeployed = deploymentStatus
|
|
*resource = append(*resource, extraResource)
|
|
}
|
|
|
|
func setResourceDeploymentStatus(resourceName string, deploymentStatus bool, manifest *Manifest) {
|
|
defer handlePanic()
|
|
switch resourceName {
|
|
case RESOURCE_RDS:
|
|
setExtraResourceStatus(manifest.ExtraResources.Database.InstanceName, deploymentStatus,
|
|
&extraResourcesDeploymentStatus.Database)
|
|
case RESOURCE_AURORADB:
|
|
setExtraResourceStatus(manifest.ExtraResources.Database.InstanceName, deploymentStatus,
|
|
&extraResourcesDeploymentStatus.Database)
|
|
case RESOURCE_ELASTIC_CACHE:
|
|
setExtraResourceStatus(manifest.ExtraResources.ElasticCache.InstanceName, deploymentStatus,
|
|
&extraResourcesDeploymentStatus.ElasticCache)
|
|
case RESOURCE_DOCDB:
|
|
setExtraResourceStatus(manifest.ExtraResources.DocDb.InstanceName, deploymentStatus,
|
|
&extraResourcesDeploymentStatus.DocDb)
|
|
case RESOURCE_AWS_ROLES:
|
|
setExtraResourceStatus(manifest.ExtraResources.ServiceRole.RoleName, deploymentStatus,
|
|
&extraResourcesDeploymentStatus.AwsAccess)
|
|
case RESOURCE_S3_BUCKETS:
|
|
for _, bucket := range manifest.ExtraResources.S3Buckets {
|
|
appendExtraResourceStatus(bucket.BucketName, deploymentStatus, &extraResourcesDeploymentStatus.S3Buckets)
|
|
}
|
|
case RESOURCE_DYNAMODB:
|
|
for _, table := range manifest.ExtraResources.Dynamodb.Tables {
|
|
appendExtraResourceStatus(table.TableName, deploymentStatus, &extraResourcesDeploymentStatus.Dynamodb)
|
|
}
|
|
default:
|
|
log.Panicf("\nErr: %v", "Invalid Resource Name "+resourceName)
|
|
}
|
|
}
|
|
|
|
func handlePanic() {
|
|
if r := recover(); r != nil {
|
|
log.Printf("Recovered from panic: %v", r)
|
|
}
|
|
}
|
|
|
|
// sendResourceDeploymentStatusBlackList is a list of verticals for which deployment status should not be sent
|
|
var sendResourceDeploymentStatusBlackList = map[string]void{
|
|
"ktk": {},
|
|
}
|
|
|
|
func sendResourceDeploymentStatus(manifest *Manifest) {
|
|
if _, ok := sendResourceDeploymentStatusBlackList[manifest.InfraVertical]; ok {
|
|
log.Printf("Skipping sending deployment status for vertical %s", manifest.InfraVertical)
|
|
return
|
|
}
|
|
|
|
defer handlePanic()
|
|
client := &http.Client{}
|
|
url := getDeploymentPortalUrl(manifest.InfraVertical, manifest.Environment, manifest.Name)
|
|
jsonData, err := json.Marshal(extraResourcesDeploymentStatus)
|
|
if err != nil {
|
|
log.Panicf("Error encoding JSON:#{err}")
|
|
return
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
log.Panicf("\nErr: #{err} \nFailed to create Get Request")
|
|
return
|
|
}
|
|
deploymentPortalToken, tokenStatus := os.LookupEnv("DEPLOYMENT_PORTAL_TOKEN")
|
|
if !tokenStatus {
|
|
log.Panicf("\nFailed to get Deployment Portal Token")
|
|
return
|
|
}
|
|
req.Header.Set("X_AUTH_TOKEN", deploymentPortalToken)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Panicf("\nErr: %v\nFailed to get response", err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != 202 && resp.StatusCode != 404 {
|
|
log.Panicf("\nFailed to set deployment Status of Resources, Response Code: %v", resp.StatusCode)
|
|
}
|
|
}
|
|
|
|
func provisionAllResource(manifest *Manifest) error {
|
|
var extraResource ExtraResources
|
|
structType := getStructType(reflect.TypeOf(extraResource))
|
|
for i := 0; i < structType.NumField(); i++ {
|
|
field := structType.Field(i)
|
|
jsonName, jsonOk := field.Tag.Lookup("json")
|
|
modules, modOk := field.Tag.Lookup("module")
|
|
moduleDirs, modDirOk := field.Tag.Lookup("moduleDir")
|
|
if jsonOk && modOk && modDirOk && checkResourceExists(jsonName, manifest) {
|
|
if modules == "" || moduleDirs == "" {
|
|
continue
|
|
}
|
|
module, moduleDir := getModuleDir(jsonName, modules, moduleDirs, manifest)
|
|
err := provisionResource(module, moduleDir, manifest)
|
|
if err != nil {
|
|
log.Fatalf("\nErr: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if manifest.Actions.Apply {
|
|
sendResourceDeploymentStatus(manifest)
|
|
}
|
|
return nil
|
|
}
|
|
func getModuleDir(jsonName, modules, moduleDirs string, manifest *Manifest) (string, string) {
|
|
listModule := strings.Split(modules, ",")
|
|
listModuleDir := strings.Split(moduleDirs, ",")
|
|
|
|
if jsonName == "database" {
|
|
return getDatabaseModule(listModule, listModuleDir, manifest)
|
|
}
|
|
return listModule[0], listModuleDir[0]
|
|
}
|
|
|
|
func getDatabaseModule(listModule, listModuleDir []string, manifest *Manifest) (string, string) {
|
|
if manifest.ExtraResources.Database.DbEngineType == "rds-aurora-postgres" {
|
|
return listModule[1], listModuleDir[1]
|
|
}
|
|
return listModule[0], listModuleDir[0]
|
|
}
|
|
|
|
func checkResourceExists(resourceName string, manifest *Manifest) bool {
|
|
var myMap map[string]interface{}
|
|
data, _ := json.Marshal(manifest.ExtraResources)
|
|
err := json.Unmarshal(data, &myMap)
|
|
if err != nil {
|
|
log.Fatalf("\nErr: %v", err)
|
|
}
|
|
|
|
val, ok := myMap[resourceName]
|
|
if ok && val != nil {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func templateResourceTf(templateName, resourceDir string, manifest *Manifest, destinationDir string) error {
|
|
log.Printf("Processing path %s", filepath.Join(TemplatesDir, resourceDir))
|
|
|
|
assetNames := bindata.AssetNames()
|
|
filteredAssets := []string{}
|
|
for _, assetName := range assetNames {
|
|
if strings.HasPrefix(assetName, filepath.Join(TemplatesDir, resourceDir)) {
|
|
filteredAssets = append(filteredAssets, assetName)
|
|
}
|
|
}
|
|
|
|
for _, asset := range filteredAssets {
|
|
tfBytes := bindata.MustAsset(asset)
|
|
t := template.Must(template.New(templateName).Funcs(sprig.TxtFuncMap()).Parse(string(tfBytes)))
|
|
directoryPath := filepath.Dir(filepath.Join(destinationDir, strings.TrimPrefix(asset, filepath.Join(TemplatesDir, resourceDir))))
|
|
tfOut, err := createFile(directoryPath+"/", filepath.Base(asset))
|
|
if err != nil {
|
|
log.Fatalf("\nErr: %v", err)
|
|
return err
|
|
}
|
|
|
|
err = t.Execute(tfOut, manifest)
|
|
if err != nil {
|
|
log.Fatalf("\nError while Templating %s. \nPlease ensure %s is present in the manifest, if present please check if the configurations are correct.\n"+
|
|
"Err: %v", resourceDir, templateDirMap[resourceDir], err)
|
|
return err
|
|
}
|
|
tfOut.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func executeResourceTf(resourceDir string) error {
|
|
var cmd *exec.Cmd
|
|
log.Printf("Running terraform for %s", resourceDir)
|
|
cmd = exec.Command(InitScript)
|
|
cmd.Dir = resourceDir
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
log.Fatalf("\nErr: %v", err)
|
|
return err
|
|
}
|
|
return err
|
|
}
|