Skip to content

Commit 156e22d

Browse files
ndeloofglours
authored andcommitted
introduce OmitEmpty in yaml processing pipeline
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent 74c1d59 commit 156e22d

File tree

5 files changed

+98
-1
lines changed

5 files changed

+98
-1
lines changed

loader/loader.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,8 @@ func loadYamlFile(ctx context.Context, file types.ConfigFile, opts *Options, wor
516516
return err
517517
}
518518

519+
dict = OmitEmpty(dict)
520+
519521
// Canonical transformation can reveal duplicates, typically as ports can be a range and conflict with an override
520522
dict, err = override.EnforceUnicity(dict)
521523
return err

loader/loader_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3581,3 +3581,14 @@ services:
35813581
},
35823582
})
35833583
}
3584+
3585+
func TestOmitEmptyDNS(t *testing.T) {
3586+
p, err := loadYAML(`
3587+
name: load-empty-dsn
3588+
services:
3589+
test:
3590+
dns: ${UNSET_VAR}
3591+
`)
3592+
assert.NilError(t, err)
3593+
assert.Equal(t, len(p.Services["test"].DNS), 0)
3594+
}

loader/normalize.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) {
135135
}
136136
services[name] = service
137137
}
138+
138139
dict["services"] = services
139140
}
140-
141141
setNameFromKey(dict)
142142

143143
return dict, nil

loader/omitEmpty.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
Copyright 2020 The Compose Specification Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package loader
18+
19+
import "github.com/compose-spec/compose-go/v2/tree"
20+
21+
var omitempty = []tree.Path{
22+
"services.*.dns"}
23+
24+
// OmitEmpty removes empty attributes which are irrelevant when unset
25+
func OmitEmpty(yaml map[string]any) map[string]any {
26+
cleaned := omitEmpty(yaml, tree.NewPath())
27+
return cleaned.(map[string]any)
28+
}
29+
30+
func omitEmpty(data any, p tree.Path) any {
31+
switch v := data.(type) {
32+
case map[string]any:
33+
for k, e := range v {
34+
if isEmpty(e) && mustOmit(p) {
35+
delete(v, k)
36+
continue
37+
}
38+
39+
v[k] = omitEmpty(e, p.Next(k))
40+
}
41+
return v
42+
case []any:
43+
var c []any
44+
for _, e := range v {
45+
if isEmpty(e) && mustOmit(p) {
46+
continue
47+
}
48+
49+
c = append(c, omitEmpty(e, p.Next("[]")))
50+
}
51+
return c
52+
default:
53+
return data
54+
}
55+
}
56+
57+
func mustOmit(p tree.Path) bool {
58+
for _, pattern := range omitempty {
59+
if p.Matches(pattern) {
60+
return true
61+
}
62+
}
63+
return false
64+
}
65+
66+
func isEmpty(e any) bool {
67+
if e == nil {
68+
return true
69+
}
70+
if v, ok := e.(string); ok && v == "" {
71+
return true
72+
}
73+
return false
74+
}

transform/canonical.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func init() {
3333
transformers["services.*.extends"] = transformExtends
3434
transformers["services.*.networks"] = transformServiceNetworks
3535
transformers["services.*.volumes.*"] = transformVolumeMount
36+
transformers["services.*.dns"] = transformStringOrList
3637
transformers["services.*.devices.*"] = transformDeviceMapping
3738
transformers["services.*.secrets.*"] = transformFileMount
3839
transformers["services.*.configs.*"] = transformFileMount
@@ -48,6 +49,15 @@ func init() {
4849
transformers["include.*"] = transformInclude
4950
}
5051

52+
func transformStringOrList(data any, _ tree.Path, _ bool) (any, error) {
53+
switch t := data.(type) {
54+
case string:
55+
return []any{t}, nil
56+
default:
57+
return data, nil
58+
}
59+
}
60+
5161
// Canonical transforms a compose model into canonical syntax
5262
func Canonical(yaml map[string]any, ignoreParseError bool) (map[string]any, error) {
5363
canonical, err := transform(yaml, tree.NewPath(), ignoreParseError)

0 commit comments

Comments
 (0)