From 2a3ce93e339db90c5ccd9f9b3afb283867a737e3 Mon Sep 17 00:00:00 2001 From: Assil Ksiksi Date: Thu, 21 Dec 2023 12:58:07 -0500 Subject: [PATCH] read config file content if missing This ensures that project name validation occurs in cases where the content is missing (e.g., when constructed via ToConfigFiles). Signed-off-by: Assil Ksiksi --- loader/loader.go | 12 ++++++- loader/loader_test.go | 41 ++++++++++++++++++++++ loader/testdata/compose-include-cycle.yaml | 1 + 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/loader/loader.go b/loader/loader.go index 2501dab7..fc0f3545 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -484,7 +484,17 @@ func projectName(details types.ConfigDetails, opts *Options) (string, error) { if !projectNameImperativelySet { var pjNameFromConfigFile string for _, configFile := range details.ConfigFiles { - yml, err := ParseYAML(configFile.Content) + content := configFile.Content + if content == nil { + // This can be hit when Filename is set but Content is not. One + // example is when using ToConfigFiles(). + d, err := os.ReadFile(configFile.Filename) + if err != nil { + return "", fmt.Errorf("failed to read file %q: %w", configFile.Filename, err) + } + content = d + } + yml, err := ParseYAML(content) if err != nil { // HACK: the way that loading is currently structured, this is // a duplicative parse just for the `name`. if it fails, we diff --git a/loader/loader_test.go b/loader/loader_test.go index 4783ac4e..76bedb92 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -405,6 +405,7 @@ services: }, func(options *Options) { options.SkipNormalization = true options.SkipConsistencyCheck = true + options.SetProjectName("project", true) }) assert.NilError(t, err) assert.Assert(t, is.Len(actual.Services, 2)) @@ -2854,6 +2855,46 @@ networks: assert.ErrorContains(t, err, "service redis declares mutually exclusive `network_mode` and `networks`") } +func TestLoadEmptyContent(t *testing.T) { + yaml := `name: load-multi-docs +services: + test: + image: nginx:latest` + tmpPath := filepath.Join(t.TempDir(), "docker-compose.yaml") + if err := os.WriteFile(tmpPath, []byte(yaml), 0o644); err != nil { + t.Fatalf("failed to write temporary file: %s", err) + } + _, err := Load(types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{ + { + Filename: tmpPath, + }, + }, + }) + if err != nil { + t.Fatal(err) + } +} + +func TestLoadEmptyContent_MissingProject(t *testing.T) { + yaml := ` +services: + test: + image: nginx:latest` + tmpPath := filepath.Join(t.TempDir(), "docker-compose.yaml") + if err := os.WriteFile(tmpPath, []byte(yaml), 0o644); err != nil { + t.Fatalf("failed to write temporary file: %s", err) + } + _, err := Load(types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{ + { + Filename: tmpPath, + }, + }, + }) + assert.ErrorContains(t, err, "project name must not be empty") +} + func TestLoadUnitBytes(t *testing.T) { project, err := loadYAML(` name: load-unit-bytes diff --git a/loader/testdata/compose-include-cycle.yaml b/loader/testdata/compose-include-cycle.yaml index a34172b3..37005314 100644 --- a/loader/testdata/compose-include-cycle.yaml +++ b/loader/testdata/compose-include-cycle.yaml @@ -1,6 +1,7 @@ include: - compose-include-cycle.yaml +name: project services: foo: image: foo