mirror of
https://github.com/docker/compose.git
synced 2025-04-08 17:05:13 +02:00
Load a compose file and pass Project to cobra command
close #2 Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
ba6c599de2
commit
40bf8c2dae
@ -35,6 +35,7 @@ require (
|
||||
github.com/miekg/pkcs11 v1.0.3 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/sirupsen/logrus v1.5.0
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.3
|
||||
github.com/theupdateframework/notary v0.6.1 // indirect
|
||||
@ -44,6 +45,7 @@ require (
|
||||
gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
|
||||
gopkg.in/fatih/pool.v2 v2.0.0 // indirect
|
||||
gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect
|
||||
gotest.tools/v3 v3.0.2
|
||||
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787 // indirect
|
||||
)
|
||||
|
||||
|
@ -140,6 +140,7 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
@ -180,6 +181,7 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw=
|
||||
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
@ -192,6 +194,7 @@ github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -280,9 +283,12 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU
|
||||
github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/weppos/publicsuffix-go v0.5.0 h1:rutRtjBJViU/YjcI5d80t4JAVvDltS6bciJg2K1HrLU=
|
||||
github.com/weppos/publicsuffix-go v0.5.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 h1:j2hhcujLRHAg872RWAV5yaUrEjHEObwDv3aImCaNLek=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
@ -392,6 +398,7 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.0 h1:d+tVGRu6X0ZBQ+kyAR8JKi6AXhTP2gmQaoIYaGFz634=
|
||||
gotest.tools/v3 v3.0.0/go.mod h1:TUP+/YtXl/dp++T+SZ5v2zUmLVBHmptSb/ajDLCJ+3c=
|
||||
gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -1,7 +1,7 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
_ "github.com/compose-spec/compose-go/types"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
@ -14,3 +14,17 @@ func (o *ProjectOptions) AddFlags(flags *pflag.FlagSet) {
|
||||
flags.StringArrayVarP(&o.ConfigPaths, "file", "f", nil, "Specify an alternate compose file")
|
||||
flags.StringVarP(&o.name, "project-name", "n", "", "Specify an alternate project name (default: directory name)")
|
||||
}
|
||||
|
||||
|
||||
type ProjectFunc func(project *Project, args []string) error
|
||||
|
||||
// WithProject wrap a ProjectFunc into a cobra command
|
||||
func WithProject(options *ProjectOptions, f ProjectFunc) func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
project, err := projectFromOptions(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(project, args)
|
||||
}
|
||||
}
|
||||
|
165
ecs/pkg/compose/project.go
Normal file
165
ecs/pkg/compose/project.go
Normal file
@ -0,0 +1,165 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
types.Config
|
||||
projectDir string
|
||||
Name string `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
func NewProject(config types.ConfigDetails, name string) (*Project, error) {
|
||||
model, err := loader.Load(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := Project{
|
||||
Config: *model,
|
||||
projectDir: config.WorkingDir,
|
||||
Name: name,
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
|
||||
// projectFromOptions load a compose project based on command line options
|
||||
func projectFromOptions(options *ProjectOptions) (*Project, error) {
|
||||
configPath, err := getConfigPathFromOptions(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := options.name
|
||||
if name == "" {
|
||||
name = os.Getenv("COMPOSE_PROJECT_NAME")
|
||||
}
|
||||
|
||||
workingDir := filepath.Dir(configPath[0])
|
||||
|
||||
if name == "" {
|
||||
r := regexp.MustCompile(`[^a-z0-9\\-_]+`)
|
||||
name = r.ReplaceAllString(strings.ToLower(filepath.Base(workingDir)), "")
|
||||
}
|
||||
|
||||
configs, err := parseConfigs(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewProject(types.ConfigDetails{
|
||||
WorkingDir: workingDir,
|
||||
ConfigFiles: configs,
|
||||
Environment: environment(),
|
||||
}, name)
|
||||
}
|
||||
|
||||
func getConfigPathFromOptions(options *ProjectOptions) ([]string, error) {
|
||||
paths := []string{}
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(options.ConfigPaths) != 0 {
|
||||
for _, f := range options.ConfigPaths {
|
||||
if f == "-" {
|
||||
paths = append(paths, f)
|
||||
continue
|
||||
}
|
||||
if !filepath.IsAbs(f) {
|
||||
f = filepath.Join(pwd, f)
|
||||
}
|
||||
if _, err := os.Stat(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, f)
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
sep := os.Getenv("COMPOSE_FILE_SEPARATOR")
|
||||
if sep == "" {
|
||||
sep = string(os.PathListSeparator)
|
||||
}
|
||||
f := os.Getenv("COMPOSE_FILE")
|
||||
if f != "" {
|
||||
return strings.Split(f, sep), nil
|
||||
}
|
||||
|
||||
for {
|
||||
candidates := []string{}
|
||||
for _, n := range SupportedFilenames {
|
||||
f := filepath.Join(pwd, n)
|
||||
if _, err := os.Stat(f); err == nil {
|
||||
candidates = append(candidates, f)
|
||||
}
|
||||
}
|
||||
if len(candidates) > 0 {
|
||||
winner := candidates[0]
|
||||
if len(candidates) > 1 {
|
||||
logrus.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", "))
|
||||
logrus.Warnf("Using %s\n", winner)
|
||||
}
|
||||
return []string{winner}, nil
|
||||
}
|
||||
parent := filepath.Dir(pwd)
|
||||
if parent == pwd {
|
||||
return nil, fmt.Errorf("Can't find a suitable configuration file in this directory or any parent. Are you in the right directory?")
|
||||
}
|
||||
pwd = parent
|
||||
}
|
||||
}
|
||||
|
||||
var SupportedFilenames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"}
|
||||
|
||||
func parseConfigs(configPaths []string) ([]types.ConfigFile, error) {
|
||||
files := []types.ConfigFile{}
|
||||
for _, f := range configPaths {
|
||||
var (
|
||||
b []byte
|
||||
err error
|
||||
)
|
||||
if f == "-" {
|
||||
b, err = ioutil.ReadAll(os.Stdin)
|
||||
} else {
|
||||
if _, err := os.Stat(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err = ioutil.ReadFile(f)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, err := loader.ParseYAML(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files = append(files, types.ConfigFile{Filename: f, Config: config})
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func environment() map[string]string {
|
||||
return getAsEqualsMap(os.Environ())
|
||||
}
|
||||
|
||||
// getAsEqualsMap split key=value formatted strings into a key : value map
|
||||
func getAsEqualsMap(em []string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for _, v := range em {
|
||||
kv := strings.SplitN(v, "=", 2)
|
||||
m[kv[0]] = kv[1]
|
||||
}
|
||||
return m
|
||||
}
|
45
ecs/pkg/compose/project_test.go
Normal file
45
ecs/pkg/compose/project_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"gotest.tools/v3/assert"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_project_name(t *testing.T) {
|
||||
p, err := projectFromOptions(&ProjectOptions{
|
||||
name: "my_project",
|
||||
ConfigPaths: []string{"testdata/simple/compose.yaml"},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "my_project")
|
||||
|
||||
p, err = projectFromOptions(&ProjectOptions{
|
||||
name: "",
|
||||
ConfigPaths: []string{"testdata/simple/compose.yaml"},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "simple")
|
||||
|
||||
os.Setenv("COMPOSE_PROJECT_NAME", "my_project_from_env")
|
||||
p, err = projectFromOptions(&ProjectOptions{
|
||||
name: "",
|
||||
ConfigPaths: []string{"testdata/simple/compose.yaml"},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, p.Name, "my_project_from_env")
|
||||
}
|
||||
|
||||
func Test_project_from_set_of_files(t *testing.T) {
|
||||
p, err := projectFromOptions(&ProjectOptions{
|
||||
name: "my_project",
|
||||
ConfigPaths: []string{
|
||||
"testdata/simple/compose.yaml",
|
||||
"testdata/simple/compose-with-overrides.yaml",
|
||||
},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
service, err := p.GetService("simple")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, service.Image, "haproxy")
|
||||
}
|
4
ecs/pkg/compose/testdata/simple/compose-with-overrides.yaml
vendored
Normal file
4
ecs/pkg/compose/testdata/simple/compose-with-overrides.yaml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
version: "3"
|
||||
services:
|
||||
simple:
|
||||
image: haproxy
|
4
ecs/pkg/compose/testdata/simple/compose.yaml
vendored
Normal file
4
ecs/pkg/compose/testdata/simple/compose.yaml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
version: "3"
|
||||
services:
|
||||
simple:
|
||||
image: nginx
|
Loading…
x
Reference in New Issue
Block a user