infctl-cli/app/app.go

226 lines
5.2 KiB
Go
Raw Normal View History

package app
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"log/slog"
"os"
"time"
"headshed/infctl-cli/config"
"headshed/infctl-cli/database"
)
type AppState struct {
Config config.BaseConfig
Customer config.CustomerConfig
DB *sql.DB
}
type PipelineStep struct {
Name string
Function string // Name of the function to call
Params []string // Parameters for the function
RetryCount int
ShouldAbort bool
}
var functionMap = map[string]func([]string) error{
"k8sNamespaceExists": func(params []string) error {
if len(params) != 1 {
return fmt.Errorf("invalid parameters for k8sNamespaceExists")
}
return k8sNamespaceExists(params[0])
},
"RunCommand": func(params []string) error {
if len(params) != 1 {
return fmt.Errorf("invalid parameters for RunCommand")
}
return RunCommand(params[0])
},
// Add more functions as needed
}
func parseStepsFromJSON(filePath string) ([]PipelineStep, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("failed to read JSON file: %w", err)
}
var steps []PipelineStep
if err := json.Unmarshal(data, &steps); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
}
return steps, nil
}
func (app *AppState) ToDoDeployment() []PipelineStep {
slog.Info("ToDo deployment is not implemented yet")
return []PipelineStep{}
}
func (app *AppState) RunJsonDeployment() []PipelineStep {
jsonFile := app.Config.DeploymentFile
if jsonFile == "" {
log.Fatal("no config specified with --deployment-file=<path_to_config_file>")
}
file, err := os.Open(jsonFile)
if err != nil {
slog.Error(fmt.Sprintf("Failed to open JSON file: %s", err))
}
defer file.Close()
steps, err := parseStepsFromJSON(jsonFile)
if err != nil {
slog.Error(fmt.Sprintf("Failed to parse JSON file: %s", err))
}
for _, step := range steps {
slog.Info(fmt.Sprintf("🔄 Running step: %s", step.Name))
function, exists := functionMap[step.Function]
if !exists {
slog.Error(fmt.Sprintf("Unknown function: %s", step.Function))
continue
}
err := function(step.Params)
if err != nil {
slog.Error(fmt.Sprintf("❌ Step failed: %s, error: %v", step.Name, err))
if step.ShouldAbort {
log.Fatalf("🚨Critical failure at step: %s", step.Name)
}
} else {
slog.Info(fmt.Sprintf("✅ Step completed: %s", step.Name))
}
}
return steps
}
func (app *AppState) getPipeline() []PipelineStep {
switch app.Config.DeploymentType {
case "api":
return app.ToDoDeployment()
case "json":
return app.RunJsonDeployment()
default:
return app.RunJsonDeployment()
}
}
func NewAppState(cust config.CustomerConfig, config config.BaseConfig, dbPath string) (*AppState, error) {
db, err := database.NewDatabase(dbPath)
if err != nil {
return nil, err
}
return &AppState{
Config: config,
Customer: cust,
DB: db,
}, nil
}
func (app *AppState) runPipeline(steps []PipelineStep) error {
for _, step := range steps {
slog.Info(fmt.Sprintf("🔄 Running step: %s\n", step.Name))
// Look up the function in the functionMap
function, exists := functionMap[step.Function]
if !exists {
slog.Error(fmt.Sprintf("❌ Unknown function: %s", step.Function))
if step.ShouldAbort {
return fmt.Errorf("🚨critical failure: unknown function %s", step.Function)
}
continue
}
// Execute the function with the provided parameters
err := function(step.Params)
if err != nil {
slog.Error(fmt.Sprintf("❌ Step failed: %s, error: %v", step.Name, err))
// Retry logic
if step.RetryCount > 0 {
for i := 0; i < step.RetryCount; i++ {
slog.Info("Waiting for 20 seconds before retrying...")
time.Sleep(20 * time.Second)
if innerErr := function(step.Params); innerErr == nil {
slog.Info(fmt.Sprintf("✅ Step completed: %s\n", step.Name))
err = nil
break
} else {
err = innerErr
}
}
}
// Handle failure after retries
if err != nil {
if step.ShouldAbort {
return fmt.Errorf("🚨critical failure at step: %s", step.Name)
}
continue
}
}
slog.Info(fmt.Sprintf("✅ Step completed: %s\n", step.Name))
}
return nil
}
func (app *AppState) SetUpNewCustomer() error {
/*
| --------------------------
| main pipeline
| --------------------------
*/
steps := app.getPipeline()
app.runPipeline(steps)
slog.Info(fmt.Sprintln("🎉 Pipeline setup complete!"))
return nil
}
func (app *AppState) CreatePipeline() error {
isNew, err := database.CheckProjectName(app.DB, app.Customer.Project)
if err != nil {
return fmt.Errorf("failed to check project name: %w", err)
}
if isNew {
port, err := database.GetNextPortNumber(app.DB)
if err != nil {
return fmt.Errorf("failed to get next port number: %w", err)
}
err = database.AddProjectName(app.DB, app.Customer.Project, port)
if err != nil {
return fmt.Errorf("failed to add project name: %w", err)
}
slog.Info(fmt.Sprintln("Project name added:", app.Customer.Project))
fmt.Printf("Port number assigned: %d\n", port)
app.Config.Port = port
} else {
slog.Info(fmt.Sprintln("Project name already exists:", app.Customer.Project))
}
err = app.SetUpNewCustomer()
if err != nil {
return fmt.Errorf("failed to set up new customer: %w", err)
}
return nil
}