From ec5489a08cf1ab3ce4b2abc156f03cb85c0c8365 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 19 Mar 2021 14:03:27 +0100 Subject: [PATCH] introduce config --services, --volumes, --hash for backward compatibility Signed-off-by: Nicolas De Loof --- cli/cmd/compose/convert.go | 68 ++++++++++++++++++++++++-- local/compose/containers.go | 6 ++- local/compose/convergence.go | 5 +- local/compose/create.go | 7 +-- local/compose/dependencies.go | 8 +-- local/compose/logs.go | 2 +- local/compose/util.go => utils/hash.go | 18 +++---- 7 files changed, 87 insertions(+), 27 deletions(-) rename local/compose/util.go => utils/hash.go (74%) diff --git a/cli/cmd/compose/convert.go b/cli/cmd/compose/convert.go index d4165f93b..5fac6ad49 100644 --- a/cli/cmd/compose/convert.go +++ b/cli/cmd/compose/convert.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "os" + "strings" "github.com/cnabio/cnab-to-oci/remotes" "github.com/distribution/distribution/v3/reference" @@ -32,14 +33,18 @@ import ( "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/config" + "github.com/docker/compose-cli/utils" ) type convertOptions struct { *projectOptions - Format string - Output string - quiet bool - resolve bool + Format string + Output string + quiet bool + resolve bool + services bool + volumes bool + hash string } var addFlagsFuncs []func(cmd *cobra.Command, opts *convertOptions) @@ -60,6 +65,16 @@ func convertCommand(p *projectOptions) *cobra.Command { } os.Stdout = devnull } + if opts.services { + return runServices(opts) + } + if opts.volumes { + return runVolumes(opts) + } + if opts.hash != "" { + return runHash(opts) + } + return runConvert(cmd.Context(), opts, args) }, } @@ -68,6 +83,10 @@ func convertCommand(p *projectOptions) *cobra.Command { flags.BoolVar(&opts.resolve, "resolve-image-digests", false, "Pin image tags to digests.") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only validate the configuration, don't print anything.") + flags.BoolVar(&opts.services, "services", false, "Print the service names, one per line.") + flags.BoolVar(&opts.volumes, "volumes", false, "Print the volume names, one per line.") + flags.StringVar(&opts.hash, "hash", "", "Print the service config hash, one per line.") + // add flags for hidden backends for _, f := range addFlagsFuncs { f(cmd, &opts) @@ -126,3 +145,44 @@ func runConvert(ctx context.Context, opts convertOptions, services []string) err _, err = fmt.Fprint(out, string(json)) return err } + +func runServices(opts convertOptions) error { + project, err := opts.toProject(nil) + if err != nil { + return err + } + for _, s := range project.Services { + fmt.Println(s.Name) + } + return nil +} + +func runVolumes(opts convertOptions) error { + project, err := opts.toProject(nil) + if err != nil { + return err + } + for _, v := range project.Volumes { + fmt.Println(v.Name) + } + return nil +} + +func runHash(opts convertOptions) error { + var services []string + if opts.hash != "*" { + services = append(services, strings.Split(opts.hash, ",")...) + } + project, err := opts.toProject(services) + if err != nil { + return err + } + for _, s := range project.Services { + hash, err := utils.ServiceHash(s) + if err != nil { + return err + } + fmt.Printf("%s %s\n", s.Name, hash) + } + return nil +} diff --git a/local/compose/containers.go b/local/compose/containers.go index 4d6de3b1a..1e26b6a90 100644 --- a/local/compose/containers.go +++ b/local/compose/containers.go @@ -24,6 +24,8 @@ import ( "github.com/compose-spec/compose-go/types" moby "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + + "github.com/docker/compose-cli/utils" ) // Containers is a set of moby Container @@ -69,14 +71,14 @@ type containerPredicate func(c moby.Container) bool func isService(services ...string) containerPredicate { return func(c moby.Container) bool { service := c.Labels[serviceLabel] - return contains(services, service) + return utils.StringContains(services, service) } } func isNotService(services ...string) containerPredicate { return func(c moby.Container) bool { service := c.Labels[serviceLabel] - return !contains(services, service) + return !utils.StringContains(services, service) } } diff --git a/local/compose/convergence.go b/local/compose/convergence.go index bcd09b0ab..f1d1fdb5d 100644 --- a/local/compose/convergence.go +++ b/local/compose/convergence.go @@ -33,6 +33,7 @@ import ( "github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/progress" status "github.com/docker/compose-cli/local/moby" + "github.com/docker/compose-cli/utils" ) const ( @@ -97,7 +98,7 @@ func (s *composeService) ensureService(ctx context.Context, project *types.Proje return nil } - expected, err := jsonHash(service) + expected, err := utils.ServiceHash(service) if err != nil { return err } @@ -249,7 +250,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P // setDependentLifecycle define the Lifecycle strategy for all services to depend on specified service func setDependentLifecycle(project *types.Project, service string, strategy string) { for i, s := range project.Services { - if contains(s.GetDependencies(), service) { + if utils.StringContains(s.GetDependencies(), service) { if s.Extensions == nil { s.Extensions = map[string]interface{}{} } diff --git a/local/compose/create.go b/local/compose/create.go index 8dd154d91..0636462b2 100644 --- a/local/compose/create.go +++ b/local/compose/create.go @@ -40,6 +40,7 @@ import ( "github.com/docker/compose-cli/api/compose" "github.com/docker/compose-cli/api/progress" convert "github.com/docker/compose-cli/local/moby" + "github.com/docker/compose-cli/utils" ) func (s *composeService) Create(ctx context.Context, project *types.Project, opts compose.CreateOptions) error { @@ -102,7 +103,7 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt prepareNetworkMode(project) return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error { - if contains(opts.Services, service.Name) { + if utils.StringContains(opts.Services, service.Name) { return s.ensureService(c, project, service, opts.Recreate, opts.Inherit, opts.Timeout) } return s.ensureService(c, project, service, opts.RecreateDependencies, opts.Inherit, opts.Timeout) @@ -121,7 +122,7 @@ func prepareVolumes(p *types.Project) error { p.Services[i].DependsOn = make(types.DependsOnConfig, len(dependServices)) } for _, service := range p.Services { - if contains(dependServices, service.Name) { + if utils.StringContains(dependServices, service.Name) { p.Services[i].DependsOn[service.Name] = types.ServiceDependency{ Condition: types.ServiceConditionStarted, } @@ -196,7 +197,7 @@ func getImageName(service types.ServiceConfig, projectName string) string { func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, service types.ServiceConfig, number int, inherit *moby.Container, autoRemove bool) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) { - hash, err := jsonHash(service) + hash, err := utils.ServiceHash(service) if err != nil { return nil, nil, nil, err } diff --git a/local/compose/dependencies.go b/local/compose/dependencies.go index 91a0c033a..00d7b3ef1 100644 --- a/local/compose/dependencies.go +++ b/local/compose/dependencies.go @@ -24,6 +24,8 @@ import ( "github.com/compose-spec/compose-go/types" "golang.org/x/sync/errgroup" + + "github.com/docker/compose-cli/utils" ) // ServiceStatus indicates the status of a service @@ -313,7 +315,7 @@ func (g *Graph) HasCycles() (bool, error) { path := []string{ vertex.Key, } - if !contains(discovered, vertex.Key) && !contains(finished, vertex.Key) { + if !utils.StringContains(discovered, vertex.Key) && !utils.StringContains(finished, vertex.Key) { var err error discovered, finished, err = g.visit(vertex.Key, path, discovered, finished) @@ -331,11 +333,11 @@ func (g *Graph) visit(key string, path []string, discovered []string, finished [ for _, v := range g.Vertices[key].Children { path := append(path, v.Key) - if contains(discovered, v.Key) { + if utils.StringContains(discovered, v.Key) { return nil, nil, fmt.Errorf("cycle found: %s", strings.Join(path, " -> ")) } - if !contains(finished, v.Key) { + if !utils.StringContains(finished, v.Key) { if _, _, err := g.visit(v.Key, path, discovered, finished); err != nil { return nil, nil, err } diff --git a/local/compose/logs.go b/local/compose/logs.go index 7559283e1..cd54d2c59 100644 --- a/local/compose/logs.go +++ b/local/compose/logs.go @@ -43,7 +43,7 @@ func (s *composeService) Logs(ctx context.Context, projectName string, consumer } if len(options.Services) > 0 { ignore = func(s string) bool { - return !contains(options.Services, s) + return !utils.StringContains(options.Services, s) } } diff --git a/local/compose/util.go b/utils/hash.go similarity index 74% rename from local/compose/util.go rename to utils/hash.go index dd9cbbcfe..ee3dc4e21 100644 --- a/local/compose/util.go +++ b/utils/hash.go @@ -14,27 +14,21 @@ limitations under the License. */ -package compose +package utils import ( "encoding/json" + "github.com/compose-spec/compose-go/types" "github.com/opencontainers/go-digest" ) -func jsonHash(o interface{}) (string, error) { +// ServiceHash compute configuration has for a service +// TODO move this to compose-go +func ServiceHash(o types.ServiceConfig) (string, error) { bytes, err := json.Marshal(o) if err != nil { return "", err } - return digest.SHA256.FromBytes(bytes).String(), nil -} - -func contains(slice []string, item string) bool { - for _, v := range slice { - if v == item { - return true - } - } - return false + return digest.SHA256.FromBytes(bytes).Encoded(), nil }