cloudfoundry-incubator/stratos

View on GitHub
src/jetstream/plugins/analysis/container/main.go

Summary

Maintainability
A
1 hr
Test Coverage
package main

import (
    "fmt"
    "os"
    "path/filepath"
    "strings"
    "time"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
    log "github.com/sirupsen/logrus"
)

const (
    defaultPort      = 8090
    defaultAddress   = "0.0.0.0"
    reportsDirEnvVar = "ANALYSIS_REPORTS_DIR"
    scriptsDirEnvVar = "ANALYSIS_SCRIPTS_DIR"
)

type Analyzer struct {
    reportsDir string
    jobs       map[string]*AnalysisJob
}

func main() {
    log.SetFormatter(&log.TextFormatter{ForceColors: true, FullTimestamp: true, TimestampFormat: time.UnixDate})

    log.SetOutput(os.Stdout)

    log.Info("========================================")
    log.Info("=== Stratos Analysis API Server      ===")
    log.Info("========================================")
    log.Info("")
    log.Info("Initialization started.")

    analyzer := Analyzer{}
    analyzer.jobs = make(map[string]*AnalysisJob)

    analyzer.Start()
}

func (a *Analyzer) Start() {

    // Reports folder

    // Init reports directory
    if reportsDir, ok := os.LookupEnv(reportsDirEnvVar); ok {
        dir, err := filepath.Abs(reportsDir)
        if err != nil {
            log.Fatal("Can not get absolute path for reports folder")
        }
        a.reportsDir = dir
    } else {
        a.reportsDir = filepath.Join(os.TempDir(), "stratos-analysis")
    }
    log.Infof("Using reports folder: %s", a.reportsDir)

    // Make the directory if it does not exit
    if _, err := os.Stat(a.reportsDir); os.IsNotExist(err) {
        if os.MkdirAll(a.reportsDir, os.ModePerm) != nil {
            log.Fatal("Could not create folder for analysis reports")
        }
    }

    // Start a simple web server
    e := echo.New()
    e.HideBanner = true
    e.HidePort = true
    customLoggerConfig := middleware.LoggerConfig{
        Format: `Request: [${time_rfc3339}] Remote-IP:"${remote_ip}" ` +
            `Method:"${method}" Path:"${path}" Status:${status} Latency:${latency_human} ` +
            `Bytes-In:${bytes_in} Bytes-Out:${bytes_out}` + "\n",
    }
    e.Use(middleware.LoggerWithConfig(customLoggerConfig))
    e.Use(middleware.Recover())

    a.registerRoutes(e)

    var engineErr error
    address := fmt.Sprintf("%s:%d", defaultAddress, defaultPort)
    log.Infof("Starting HTTP Server at address: %s", address)
    engineErr = e.Start(address)

    if engineErr != nil {
        engineErrStr := fmt.Sprintf("%s", engineErr)
        if !strings.Contains(engineErrStr, "Server closed") {
            log.Warnf("Failed to start HTTP/S server: %+v", engineErr)
        }
    }
}

func (a *Analyzer) registerRoutes(e *echo.Echo) {
    api := e.Group("/api")
    api.Use(setSecureCacheContentMiddleware)

    // Liveness check
    api.GET("/v1/ping", a.ping)
    // Run the given analyzer
    api.POST("/v1/run/:analyzer", a.run)
    // Get status
    api.POST("/v1/status", a.status)
    // Get a report
    api.GET("/v1/report/:user/:endpoint/:id/:file", a.report)
    // Delete a report
    api.DELETE("/v1/report/:user/:endpoint/:id", a.delete)
    // Delete all reports for an endpoint
    api.DELETE("/v1/report/:endpoint", a.deleteEndpoint)
}

func setSecureCacheContentMiddleware(h echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        c.Response().Header().Set("cache-control", "no-store")
        c.Response().Header().Set("pragma", "no-cache")
        return h(c)
    }
}

// Set the name of the job
func setJobNameAndPath(job *AnalysisJob, title string) {
    job.Name = fmt.Sprintf("%s cluster analysis", title)
    job.Path = ""

    log.Info("setJobNameAndPath")
    log.Infof("%+v", job.Config)

    if job.Config != nil {
        if len(job.Config.Namespace) > 0 {
            if len(job.Config.App) > 0 {
                job.Name = fmt.Sprintf("%s workload analysis: %s in %s", title, job.Config.App, job.Config.Namespace)
                job.Path = fmt.Sprintf("%s/%s", job.Config.Namespace, job.Config.App)
            } else {
                job.Name = fmt.Sprintf("%s namespace analysis: %s", title, job.Config.Namespace)
                job.Path = job.Config.Namespace
            }
        }
    }
}

func getScriptFolder() string {
    fallbackPath, err := os.Getwd()
    if err != nil {
        fallbackPath = "."
    }

    // Look first at the env var, then at a relative path to the executable
    if dir, ok := os.LookupEnv(scriptsDirEnvVar); ok {
        return dir
    }

    // Relative to the executable
    dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
    if err != nil {
        log.Error("Could not get folder of the running program")
        return fallbackPath
    }

    scripts := filepath.Join(dir, "scripts")
    if _, err := os.Stat(scripts); !os.IsNotExist(err) {
        return scripts
    }

    scripts = filepath.Join(dir, "pluginsĀ±", "analysis", "container", "scripts")
    if _, err := os.Stat(scripts); !os.IsNotExist(err) {
        return scripts
    }

    log.Error("Unable to locate scripts folder")
    return fallbackPath
}