diff --git a/catalogue/catalogue.go b/catalogue/catalogue.go new file mode 100644 index 00000000..b2ac73b2 --- /dev/null +++ b/catalogue/catalogue.go @@ -0,0 +1,149 @@ +package catalogue + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "os" + "strings" + "time" + + "coopcloud.tech/abra/config" +) + +type Image struct { + Image string `json:"image"` + Rating string `json:"rating"` + Source string `json:"source"` + URL string `json:"url"` +} + +type Feature struct { + Backups string `json:"backups"` + Email string `json:"email"` + Healthcheck string `json:"healthcheck"` + Image Image `json:"image"` + Status int `json:"status"` + Tests string `json:"tests"` +} + +type Tag = string +type Service = string +type ServiceMeta struct { + Digest string `json:"digest"` + Image string `json:"image"` + Tag string `json:"tag"` +} + +type App struct { + Category string `json:"category"` + DefaultBranch string `json:"default_branch"` + Description string `json:"description"` + Features Feature `json:"features"` + Icon string `json:"icon"` + Name string `json:"name"` + Repository string `json:"repository"` + Versions map[Tag]map[Service]ServiceMeta `json:"versions"` + Website string `json:"website"` +} + +type Name = string +type AppsCatalogue map[Name]App + +func (a AppsCatalogue) Flatten() []App { + apps := make([]App, 0, len(a)) + for name := range a { + apps = append(apps, a[name]) + } + return apps +} + +type ByAppName []App + +func (a ByAppName) Len() int { return len(a) } +func (a ByAppName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByAppName) Less(i, j int) bool { + return strings.ToLower(a[i].Name) < strings.ToLower(a[j].Name) +} + +var AppsCatalogueURL = "https://apps.coopcloud.tech" + +func AppsCatalogueFSIsLatest() (bool, error) { + httpClient := &http.Client{Timeout: 5 * time.Second} + res, err := httpClient.Head(AppsCatalogueURL) + if err != nil { + return false, err + } + + lastModified := res.Header["Last-Modified"][0] + parsed, err := time.Parse(time.RFC1123, lastModified) + if err != nil { + return false, err + } + info, err := os.Stat(config.APPS_JSON) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + + localModifiedTime := info.ModTime().Unix() + remoteModifiedTime := parsed.Unix() + + if localModifiedTime < remoteModifiedTime { + return false, nil + } + + return true, nil +} + +func ReadAppsCatalogue() (AppsCatalogue, error) { + apps := make(AppsCatalogue) + + appsFSIsLatest, err := AppsCatalogueFSIsLatest() + if err != nil { + return nil, err + } + + if !appsFSIsLatest { + if err := ReadAppsCatalogueWeb(&apps); err != nil { + return nil, err + } + return apps, nil + } + + if err := ReadAppsCatalogueFS(&apps); err != nil { + return nil, err + } + + return apps, nil +} + +func ReadAppsCatalogueFS(target interface{}) error { + appsJsonFS, err := ioutil.ReadFile(config.APPS_JSON) + if err != nil { + return err + } + if err := json.Unmarshal(appsJsonFS, &target); err != nil { + return err + } + return nil +} + +func ReadAppsCatalogueWeb(target interface{}) error { + if err := readJson(AppsCatalogueURL, &target); err != nil { + return err + } + + appsJson, err := json.MarshalIndent(target, "", " ") + if err != nil { + return err + } + + if err := ioutil.WriteFile(config.APPS_JSON, appsJson, 0644); err != nil { + return err + } + + return nil +} diff --git a/catalogue/common.go b/catalogue/common.go new file mode 100644 index 00000000..d2b22cb0 --- /dev/null +++ b/catalogue/common.go @@ -0,0 +1,17 @@ +package catalogue + +import ( + "encoding/json" + "net/http" + "time" +) + +func readJson(url string, target interface{}) error { + httpClient := &http.Client{Timeout: 5 * time.Second} + res, err := httpClient.Get(url) + if err != nil { + return err + } + defer res.Body.Close() + return json.NewDecoder(res.Body).Decode(target) +} diff --git a/cli/app.go b/cli/app.go index 766b7a68..398245b2 100644 --- a/cli/app.go +++ b/cli/app.go @@ -6,6 +6,7 @@ import ( "sort" "strings" + "coopcloud.tech/abra/catalogue" "coopcloud.tech/abra/client" "coopcloud.tech/abra/config" diff --git a/cli/recipe.go b/cli/recipe.go index e3eb5177..7c5289c9 100644 --- a/cli/recipe.go +++ b/cli/recipe.go @@ -1,17 +1,13 @@ package cli import ( - "encoding/json" "fmt" - "io/ioutil" - "net/http" "os" "path" "sort" - "strings" "text/template" - "time" + "coopcloud.tech/abra/catalogue" "coopcloud.tech/abra/config" "github.com/go-git/go-git/v5" @@ -19,165 +15,17 @@ import ( "github.com/urfave/cli/v2" ) -type Image struct { - Image string `json:"image"` - Rating string `json:"rating"` - Source string `json:"source"` - URL string `json:"url"` -} - -type Feature struct { - Backups string `json:"backups"` - Email string `json:"email"` - Healthcheck string `json:"healthcheck"` - Image Image `json:"image"` - Status int `json:"status"` - Tests string `json:"tests"` -} - -type Tag = string -type Service = string -type ServiceMeta struct { - Digest string `json:"digest"` - Image string `json:"image"` - Tag string `json:"tag"` -} - -type App struct { - Category string `json:"category"` - DefaultBranch string `json:"default_branch"` - Description string `json:"description"` - Features Feature `json:"features"` - Icon string `json:"icon"` - Name string `json:"name"` - Repository string `json:"repository"` - Versions map[Tag]map[Service]ServiceMeta `json:"versions"` - Website string `json:"website"` -} - -type Name = string -type AppsCatalogue map[Name]App - -func (a AppsCatalogue) Flatten() []App { - apps := make([]App, 0, len(a)) - for name := range a { - apps = append(apps, a[name]) - } - return apps -} - -type ByAppName []App - -func (a ByAppName) Len() int { return len(a) } -func (a ByAppName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByAppName) Less(i, j int) bool { - return strings.ToLower(a[i].Name) < strings.ToLower(a[j].Name) -} - -var httpClient = &http.Client{Timeout: 5 * time.Second} - -var AppsCatalogueURL = "https://apps.coopcloud.tech" - -func readJson(url string, target interface{}) error { - res, err := httpClient.Get(url) - if err != nil { - return err - } - defer res.Body.Close() - return json.NewDecoder(res.Body).Decode(target) -} - -func AppsCatalogueFSIsLatest() (bool, error) { - res, err := httpClient.Head(AppsCatalogueURL) - if err != nil { - return false, err - } - - lastModified := res.Header["Last-Modified"][0] - parsed, err := time.Parse(time.RFC1123, lastModified) - if err != nil { - return false, err - } - - info, err := os.Stat(config.APPS_JSON) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - - localModifiedTime := info.ModTime().Unix() - remoteModifiedTime := parsed.Unix() - - if localModifiedTime < remoteModifiedTime { - return false, nil - } - - return true, nil -} - -func ReadAppsCatalogue() (AppsCatalogue, error) { - apps := make(AppsCatalogue) - - appsFSIsLatest, err := AppsCatalogueFSIsLatest() - if err != nil { - return nil, err - } - - if !appsFSIsLatest { - if err := ReadAppsCatalogueWeb(&apps); err != nil { - return nil, err - } - return apps, nil - } - - if err := ReadAppsCatalogueFS(&apps); err != nil { - return nil, err - } - - return apps, nil -} - -func ReadAppsCatalogueFS(target interface{}) error { - appsJsonFS, err := ioutil.ReadFile(config.APPS_JSON) - if err != nil { - return err - } - if err := json.Unmarshal(appsJsonFS, &target); err != nil { - return err - } - return nil -} - -func ReadAppsCatalogueWeb(target interface{}) error { - if err := readJson(AppsCatalogueURL, &target); err != nil { - return err - } - - appsJson, err := json.MarshalIndent(target, "", " ") - if err != nil { - return err - } - - if err := ioutil.WriteFile(config.APPS_JSON, appsJson, 0644); err != nil { - return err - } - - return nil -} - var recipeListCommand = &cli.Command{ Name: "list", Usage: "List all available recipes", Aliases: []string{"ls"}, Action: func(c *cli.Context) error { - catalogue, err := ReadAppsCatalogue() + catl, err := catalogue.ReadAppsCatalogue() if err != nil { logrus.Fatal(err.Error()) } - apps := catalogue.Flatten() - sort.Sort(ByAppName(apps)) + apps := catl.Flatten() + sort.Sort(catalogue.ByAppName(apps)) tableCol := []string{"Name", "Category", "Status"} table := createTable(tableCol) for _, app := range apps { @@ -201,13 +49,13 @@ var recipeVersionCommand = &cli.Command{ return nil } - apps, err := ReadAppsCatalogue() + catalogue, err := catalogue.ReadAppsCatalogue() if err != nil { logrus.Fatal(err) return nil } - if app, ok := apps[recipe]; ok { + if app, ok := catalogue[recipe]; ok { tableCol := []string{"Version", "Service", "Image", "Digest"} table := createTable(tableCol) for version := range app.Versions {