package cli import ( "encoding/json" "fmt" "log" "net/http" "sort" "strings" "time" "github.com/samber/lo" appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/tagcmp" ) func (h *abraHandler) handleListApps(w http.ResponseWriter, r *http.Request) { output, err := h.cmd.Run("abra", "app", "ls", "-m") if err != nil { InternalServerError(w, err) return } var resp ServerAppsResponse // no filtering listAppServer := "" recipeFilter := "" if err := json.Unmarshal(output, &resp); err != nil { InternalServerError(w, fmt.Errorf("invalid JSON from CLI")) return } // Get abra apps from remote apps, err := h.ap.GetApps(recipeFilter, listAppServer) if err != nil { InternalServerError(w, err) } start := time.Now() // Returns map of all servers that are reachable/don't time out reachable, err := h.sp.GetReachableServers(apps) if err != nil { InternalServerError(w, err) } elapsed := time.Since(start) log.Printf("Checking server availability took %s", elapsed) // TODO: I think this will check every server for every app, need to remove some first. // rapps is all reachable apps rapps := lo.Filter(apps, func(app appPkg.App, index int) bool { _, ok := reachable[app.Server] return ok }) statuses := make(map[string]map[string]string) statuses, err = h.ap.GetAppStatuses(rapps, true) if err != nil { InternalServerError(w, err) } // TODO: if a server is unreachable, skip its apps, maybe flag something? 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 } } _, ok := reachable[app.Server] if !ok { appStats.Chaos = "unknown" appStats.ChaosVersion = "unknown" appStats.Version = "unknown" appStats.Status = "Server unreachable" for i, sapp := range stats.Apps { if sapp.AppName == app.Name { stats.Apps[i] = *appStats } } resp[app.Server] = stats continue } if app.Recipe.Name == "backup-bot-two" { stats.BackupEnabled = true } status := "unknown" version := "unknown" chaos := "unknown" chaosVersion := "unknown" if statusMeta, ok := statuses[h.ap.StackName(app)]; 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++ } stats.AppCount++ appStats.Status = status appStats.Chaos = chaos appStats.ChaosVersion = chaosVersion appStats.Version = version localApp := true var newUpdates []string if version != "unknown" && chaos == "false" { if err := h.rp.EnsureExists(app); err != nil { InternalServerError(w, fmt.Errorf("unable to clone %s: %s", app.Name, err)) return } updates, err := h.rp.Tags(app) 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 { InternalServerError(w, fmt.Errorf("JSON conversion failed: %s\n", err)) 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) { abraServers, err := h.cmd.Run("abra", "server", "ls", "-m") if err != nil { InternalServerError(w, err) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(abraServers) } func (h *abraHandler) handleListVersions(w http.ResponseWriter, r *http.Request, appName string) { app, err := appPkg.Get(appName) if err != nil { InternalServerError(w, err) return } versions, err := app.Recipe.Tags() if err != nil { InternalServerError(w, err) return } jsonBytes, err := json.Marshal(versions) if err != nil { InternalServerError(w, err) return } log.Printf("%v", versions) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(jsonBytes) } 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 }