diff --git a/cli/app/version.go b/cli/app/version.go index 208109e3..64084091 100644 --- a/cli/app/version.go +++ b/cli/app/version.go @@ -2,12 +2,12 @@ package app import ( "fmt" - "sort" "strings" abraFormatter "coopcloud.tech/abra/cli/formatter" "coopcloud.tech/abra/cli/internal" - appPkg "coopcloud.tech/abra/pkg/app" + "coopcloud.tech/abra/pkg/catalogue" + "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/client/stack" "coopcloud.tech/abra/pkg/config" "github.com/docker/distribution/reference" @@ -32,65 +32,56 @@ func getImagePath(image string) (string, error) { var appVersionCommand = &cli.Command{ Name: "version", Aliases: []string{"v"}, - Usage: "Show version of all services in app", + Usage: "Show app version", Action: func(c *cli.Context) error { app := internal.ValidateApp(c) + stackName := app.StackName() - composeFiles, err := config.GetAppComposeFiles(app.Type, app.Env) - if err != nil { - logrus.Fatal(err) - } - opts := stack.Deploy{Composefiles: composeFiles} - compose, err := config.GetAppComposeConfig(app.Type, opts, app.Env) + cl, err := client.New(app.Server) if err != nil { logrus.Fatal(err) } - ch := make(chan stack.StackStatus, len(compose.Services)) - for _, service := range compose.Services { - label := fmt.Sprintf("coop-cloud.%s.%s.version", app.StackName(), service.Name) - go func(s string, l string) { - ch <- stack.GetDeployedServicesByLabel(s, l) - }(app.Server, label) + logrus.Debugf("checking whether '%s' is already deployed", stackName) + + isDeployed, deployedVersion, err := stack.IsDeployed(c.Context, cl, stackName) + if err != nil { + logrus.Fatal(err) } - tableCol := []string{"name", "image", "version", "digest"} + if deployedVersion == "" { + logrus.Fatalf("failed to determine version of deployed '%s'", app.Name) + } + + if !isDeployed { + logrus.Fatalf("'%s' is not deployed?", app.Name) + } + + recipeMeta, err := catalogue.GetRecipeMeta(app.Type) + if err != nil { + logrus.Fatal(err) + } + + versionsMeta := make(map[string]catalogue.ServiceMeta) + for _, recipeVersion := range recipeMeta.Versions { + if currentVersion, exists := recipeVersion[deployedVersion]; exists { + versionsMeta = currentVersion + } + } + + if len(versionsMeta) == 0 { + logrus.Fatalf("PANIC: could not retrieve deployed version ('%s') from recipe catalogue?", deployedVersion) + } + + tableCol := []string{"name", "image", "version", "tag", "digest"} table := abraFormatter.CreateTable(tableCol) - statuses := make(map[string]stack.StackStatus) - for range compose.Services { - status := <-ch - if len(status.Services) > 0 { - serviceName := appPkg.ParseServiceName(status.Services[0].Spec.Name) - statuses[serviceName] = status - } - } - - sort.SliceStable(compose.Services, func(i, j int) bool { - return compose.Services[i].Name < compose.Services[j].Name - }) - - for _, service := range compose.Services { - if status, ok := statuses[service.Name]; ok { - statusService := status.Services[0] - label := fmt.Sprintf("coop-cloud.%s.%s.version", app.StackName(), service.Name) - version, digest := appPkg.ParseVersionLabel(statusService.Spec.Labels[label]) - image, err := getImagePath(statusService.Spec.Labels["com.docker.stack.image"]) - if err != nil { - logrus.Fatal(err) - } - table.Append([]string{service.Name, image, version, digest}) - continue - } - - image, err := getImagePath(service.Image) - if err != nil { - logrus.Fatal(err) - } - table.Append([]string{service.Name, image, "?", "?"}) + for serviceName, versionMeta := range versionsMeta { + table.Append([]string{serviceName, versionMeta.Image, deployedVersion, versionMeta.Tag, versionMeta.Digest}) } table.Render() + return nil }, BashComplete: func(c *cli.Context) { diff --git a/pkg/catalogue/catalogue.go b/pkg/catalogue/catalogue.go index 09be2e4f..f41b8dab 100644 --- a/pkg/catalogue/catalogue.go +++ b/pkg/catalogue/catalogue.go @@ -54,15 +54,15 @@ type tag = string // service represents a service within a recipe. type service = string -// serviceMeta represents meta info associated with a service. -type serviceMeta struct { +// ServiceMeta represents meta info associated with a service. +type ServiceMeta struct { Digest string `json:"digest"` Image string `json:"image"` Tag string `json:"tag"` } // RecipeVersions are the versions associated with a recipe. -type RecipeVersions []map[tag]map[service]serviceMeta +type RecipeVersions []map[tag]map[service]ServiceMeta // RecipeMeta represents metadata for a recipe in the abra catalogue. type RecipeMeta struct { @@ -420,7 +420,7 @@ func GetRecipeVersions(recipeName string) (RecipeVersions, error) { return err } - versionMeta := make(map[string]serviceMeta) + versionMeta := make(map[string]ServiceMeta) for _, service := range recipe.Config.Services { img, err := reference.ParseNormalizedNamed(service.Image) @@ -438,7 +438,7 @@ func GetRecipeVersions(recipeName string) (RecipeVersions, error) { return err } - versionMeta[service.Name] = serviceMeta{ + versionMeta[service.Name] = ServiceMeta{ Digest: digest, Image: path, Tag: img.(reference.NamedTagged).Tag(), @@ -447,7 +447,7 @@ func GetRecipeVersions(recipeName string) (RecipeVersions, error) { logrus.Debugf("collecting digest: '%s', image: '%s', tag: '%s'", digest, path, tag) } - versions = append(versions, map[string]map[string]serviceMeta{tag: versionMeta}) + versions = append(versions, map[string]map[string]ServiceMeta{tag: versionMeta}) return nil }); err != nil { diff --git a/pkg/client/stack/stack.go b/pkg/client/stack/stack.go index 49392db8..03dd9e9d 100644 --- a/pkg/client/stack/stack.go +++ b/pkg/client/stack/stack.go @@ -93,6 +93,19 @@ func GetAllDeployedServices(contextName string) StackStatus { return StackStatus{services, nil} } +// GetDeployedServicesByName filters services by name +func GetDeployedServicesByName(ctx context.Context, cl *dockerclient.Client, stackName, serviceName string) StackStatus { + filters := filters.NewArgs() + filters.Add("name", fmt.Sprintf("%s_%s", stackName, serviceName)) + + services, err := cl.ServiceList(ctx, types.ServiceListOptions{Filters: filters}) + if err != nil { + return StackStatus{[]swarm.Service{}, err} + } + + return StackStatus{services, nil} +} + // IsDeployed chekcks whether an appp is deployed or not. func IsDeployed(ctx context.Context, cl *dockerclient.Client, stackName string) (bool, string, error) { version := ""