Files
coop-cloud-wizard/cli/list.go
2026-04-18 15:10:12 -04:00

193 lines
4.3 KiB
Go

package cli
import (
"log"
"net/http"
"os/exec"
"encoding/json"
"strings"
"sort"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/tagcmp"
)
func (h *abraHandler) handleListApps(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("abra", "app", "ls", "-m")
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("Error: ", err)
InternalServerErrorHandler(w, r)
return
}
var resp ServerAppsResponse
// no filtering
listAppServer := ""
recipeFilter := ""
if err := json.Unmarshal(output, &resp); err != nil {
http.Error(w, "invalid JSON from CLI", http.StatusInternalServerError)
return
}
appFiles, err := appPkg.LoadAppFiles(listAppServer)
if err != nil {
log.Fatal(err)
}
apps, err := appPkg.GetApps(appFiles, recipeFilter)
if err != nil {
log.Fatal(err)
}
statuses := make(map[string]map[string]string)
alreadySeen := make(map[string]bool)
for _, app := range apps {
if _, ok := alreadySeen[app.Server]; !ok {
alreadySeen[app.Server] = true
}
}
statuses, err = appPkg.GetAppStatuses(apps, true)
if err != nil {
log.Fatal(err)
}
for _, app := range apps {
var stats ServerApps
stats = resp[app.Server]
var appStats *AbraApp
for _, sapp := range stats.Apps {
if sapp.AppName == app.Name {
appStats = &sapp
}
}
status := "unknown"
version := "unknown"
chaos := "unknown"
chaosVersion := "unknown"
if statusMeta, ok := statuses[app.StackName()]; ok {
if currentVersion, exists := statusMeta["version"]; exists {
if currentVersion != "" {
version = currentVersion
}
}
if chaosDeploy, exists := statusMeta["chaos"]; exists {
chaos = chaosDeploy
}
if chaosDeployVersion, exists := statusMeta["chaosVersion"]; exists {
chaosVersion = chaosDeployVersion
}
if statusMeta["status"] != "" {
status = statusMeta["status"]
}
stats.VersionCount++
} else {
stats.UnversionedCount++
}
appStats.Status = status
appStats.Chaos = chaos
appStats.ChaosVersion = chaosVersion
appStats.Version = version
localApp := true
var newUpdates []string
if version != "unknown" && chaos == "false" {
if err := app.Recipe.EnsureExists(); err != nil {
log.Printf("unable to clone %s: %s", app.Name, err)
InternalServerErrorHandler(w, r)
return
}
updates, err := app.Recipe.Tags()
if err != nil {
localApp = false
} else {
parsedVersion, err := tagcmp.Parse(version)
if err != nil {
log.Fatal(err)
}
for _, update := range updates {
if ok := tagcmp.IsParsable(update); !ok {
log.Printf("unable to parse %s, skipping as upgrade option", update)
continue
}
parsedUpdate, err := tagcmp.Parse(update)
if err != nil {
log.Fatal(err)
}
if update != version && parsedUpdate.IsGreaterThan(parsedVersion) {
newUpdates = append(newUpdates, update)
}
}
}
}
if len(newUpdates) == 0 {
if version == "unknown" {
appStats.Upgrade = "unknown"
} else if localApp {
appStats.Upgrade = "latest"
stats.LatestCount++
} else {
appStats.Upgrade = "unknown"
}
} else {
newUpdates = SortVersionsDesc(newUpdates)
appStats.Upgrade = strings.Join(newUpdates, "\n")
stats.UpgradeCount++
}
for i, sapp := range stats.Apps {
if sapp.AppName == app.Name {
stats.Apps[i] = *appStats
}
}
resp[app.Server] = stats
}
jsonBytes, err := json.Marshal(resp)
if err != nil {
log.Printf("JSON conversion failed: %s\n", err)
InternalServerErrorHandler(w, r)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(jsonBytes)
}
func (h *abraHandler) handleListServers (w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("abra", "server", "ls", "-m")
abraServers, err := cmd.CombinedOutput()
if err != nil {
log.Printf("Error: ", err)
InternalServerErrorHandler(w, r)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(abraServers)
}
func SortVersionsDesc(versions []string) []string {
var tags []tagcmp.Tag
for _, v := range versions {
parsed, _ := tagcmp.Parse(v) // skips unsupported tags
tags = append(tags, parsed)
}
sort.Sort(tagcmp.ByTagDesc(tags))
var desc []string
for _, t := range tags {
desc = append(desc, t.String())
}
return desc
}