Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions loader/full-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ services:
tags:
- foo:v1.0.0
- docker.io/username/foo:my-other-tag
- ${COMPOSE_PROJECT_NAME}:1.0.0


cap_add:
Expand Down
6 changes: 4 additions & 2 deletions loader/full-struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
Mode: uint32Ptr(0440),
},
},
Tags: []string{"foo:v1.0.0", "docker.io/username/foo:my-other-tag"},
Tags: []string{"foo:v1.0.0", "docker.io/username/foo:my-other-tag", "full_example_project_name:1.0.0"},
},
CapAdd: []string{"ALL"},
CapDrop: []string{"NET_ADMIN", "SYS_ADMIN"},
Expand Down Expand Up @@ -602,6 +602,7 @@ services:
tags:
- foo:v1.0.0
- docker.io/username/foo:my-other-tag
- full_example_project_name:1.0.0
cap_add:
- ALL
cap_drop:
Expand Down Expand Up @@ -1133,7 +1134,8 @@ func fullExampleJSON(workingDir, homeDir string) string {
],
"tags": [
"foo:v1.0.0",
"docker.io/username/foo:my-other-tag"
"docker.io/username/foo:my-other-tag",
"full_example_project_name:1.0.0"
]
},
"cap_add": [
Expand Down
35 changes: 26 additions & 9 deletions loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
op(opts)
}

projectName := projectName(configDetails, opts)

var configs []*types.Config
for i, file := range configDetails.ConfigFiles {
configDict := file.Config
Expand Down Expand Up @@ -207,15 +209,6 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
s.EnvFile = newEnvFiles
}

projectName, projectNameImperativelySet := opts.GetProjectName()
model.Name = NormalizeProjectName(model.Name)
if !projectNameImperativelySet && model.Name != "" {
projectName = model.Name
}

if projectName != "" {
configDetails.Environment[consts.ComposeProjectName] = projectName
}
project := &types.Project{
Name: projectName,
WorkingDir: configDetails.WorkingDir,
Expand Down Expand Up @@ -245,6 +238,30 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
return project, nil
}

func projectName(details types.ConfigDetails, opts *Options) string {
projectName, projectNameImperativelySet := opts.GetProjectName()
var pjNameFromConfigFile string

for _, configFile := range details.ConfigFiles {
yml, err := ParseYAML(configFile.Content)
if err != nil {
return ""
}
if val, ok := yml["name"]; ok && val != "" {
pjNameFromConfigFile = yml["name"].(string)
}
}
pjNameFromConfigFile = NormalizeProjectName(pjNameFromConfigFile)
if !projectNameImperativelySet && pjNameFromConfigFile != "" {
projectName = pjNameFromConfigFile
}

if _, ok := details.Environment[consts.ComposeProjectName]; !ok && projectName != "" {
details.Environment[consts.ComposeProjectName] = projectName
}
return projectName
}

func NormalizeProjectName(s string) string {
r := regexp.MustCompile("[a-z0-9_-]")
s = strings.ToLower(s)
Expand Down
93 changes: 89 additions & 4 deletions loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,32 @@ import (
)

func buildConfigDetails(yaml string, env map[string]string) types.ConfigDetails {
return buildConfigDetailsMultipleFiles(env, yaml)
}

func buildConfigDetailsMultipleFiles(env map[string]string, yamls ...string) types.ConfigDetails {
workingDir, err := os.Getwd()
if err != nil {
panic(err)
}

return types.ConfigDetails{
WorkingDir: workingDir,
ConfigFiles: []types.ConfigFile{
{Filename: "filename.yml", Content: []byte(yaml)},
},
WorkingDir: workingDir,
ConfigFiles: buildConfigFiles(yamls),
Environment: env,
}
}

func buildConfigFiles(yamls []string) []types.ConfigFile {
configFiles := []types.ConfigFile{}
for i, yaml := range yamls {
configFiles = append(configFiles, types.ConfigFile{
Filename: fmt.Sprintf("filename%d.yml", i),
Content: []byte(yaml)})
}
return configFiles
}

func loadYAML(yaml string) (*types.Project, error) {
return loadYAMLWithEnv(yaml, nil)
}
Expand Down Expand Up @@ -1951,3 +1963,76 @@ services:
assert.NilError(t, err)
assert.Equal(t, "value2", sshValue)
}

func TestProjectNameInterpolation(t *testing.T) {
t.Run("project name simple interpolation", func(t *testing.T) {
yaml := `
name: interpolated
services:
web:
image: web
container_name: ${COMPOSE_PROJECT_NAME}-web
`
configDetails := buildConfigDetails(yaml, map[string]string{})

actual, err := Load(configDetails)
assert.NilError(t, err)
svc, err := actual.GetService("web")
assert.NilError(t, err)
assert.Equal(t, "interpolated-web", svc.ContainerName)
})

t.Run("project name interpolation with override", func(t *testing.T) {
yaml1 := `
name: interpolated
services:
web:
image: web
container_name: ${COMPOSE_PROJECT_NAME}-web
`
yaml2 := `
name: overrided
services:
db:
image: db
container_name: ${COMPOSE_PROJECT_NAME}-db
`
yaml3 := `
services:
proxy:
image: proxy
container_name: ${COMPOSE_PROJECT_NAME}-proxy
`
configDetails := buildConfigDetailsMultipleFiles(map[string]string{}, yaml1, yaml2, yaml3)

actual, err := Load(configDetails)
assert.NilError(t, err)
svc, err := actual.GetService("web")
assert.NilError(t, err)
assert.Equal(t, "overrided-web", svc.ContainerName)

svc, err = actual.GetService("db")
assert.NilError(t, err)
assert.Equal(t, "overrided-db", svc.ContainerName)

svc, err = actual.GetService("proxy")
assert.NilError(t, err)
assert.Equal(t, "overrided-proxy", svc.ContainerName)
})

t.Run("project name env variable interpolation", func(t *testing.T) {
yaml := `
name: interpolated
services:
web:
image: web
container_name: ${COMPOSE_PROJECT_NAME}-web
`
configDetails := buildConfigDetails(yaml, map[string]string{"COMPOSE_PROJECT_NAME": "env-var"})
actual, err := Load(configDetails)
assert.NilError(t, err)
svc, err := actual.GetService("web")
assert.NilError(t, err)
assert.Equal(t, "env-var-web", svc.ContainerName)
})
}