Skip to content

Commit c906e70

Browse files
committed
create_host_path must not be omitted when set to false
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent 1b2139e commit c906e70

File tree

7 files changed

+76
-53
lines changed

7 files changed

+76
-53
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
test:
2525
strategy:
2626
matrix:
27-
go-version: ['1.23', '1.24']
27+
go-version: ['1.24', '1.25']
2828
platform: [ubuntu-latest, macos-latest, windows-latest]
2929
runs-on: ${{ matrix.platform }}
3030
timeout-minutes: 10

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/compose-spec/compose-go/v2
22

3-
go 1.23
3+
go 1.24
44

55
require (
66
github.com/distribution/reference v0.5.0

loader/full-struct_test.go

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,7 @@ services:
755755
FOO: foo_from_env_file
756756
QUX: qux_from_environment
757757
env_file:
758-
- %s
758+
- path: %s
759759
- path: %s
760760
required: false
761761
expose:
@@ -949,24 +949,20 @@ services:
949949
- type: bind
950950
source: /opt/data
951951
target: /var/lib/data
952-
bind:
953-
create_host_path: true
952+
bind: {}
954953
- type: bind
955954
source: %s
956955
target: /code
957-
bind:
958-
create_host_path: true
956+
bind: {}
959957
- type: bind
960958
source: %s
961959
target: /var/www/html
962-
bind:
963-
create_host_path: true
960+
bind: {}
964961
- type: bind
965962
source: %s
966963
target: /etc/configs
967964
read_only: true
968-
bind:
969-
create_host_path: true
965+
bind: {}
970966
- type: volume
971967
source: datavolume
972968
target: /var/lib/volume
@@ -1370,7 +1366,9 @@ func fullExampleJSON(workingDir, homeDir string) string {
13701366
"QUX": "qux_from_environment"
13711367
},
13721368
"env_file": [
1373-
"%s",
1369+
{
1370+
"path": "%s"
1371+
},
13741372
{
13751373
"path": "%s",
13761374
"required": false
@@ -1640,34 +1638,26 @@ func fullExampleJSON(workingDir, homeDir string) string {
16401638
"type": "bind",
16411639
"source": "/opt/data",
16421640
"target": "/var/lib/data",
1643-
"bind": {
1644-
"create_host_path": true
1645-
}
1641+
"bind": {}
16461642
},
16471643
{
16481644
"type": "bind",
16491645
"source": "%s",
16501646
"target": "/code",
1651-
"bind": {
1652-
"create_host_path": true
1653-
}
1647+
"bind": {}
16541648
},
16551649
{
16561650
"type": "bind",
16571651
"source": "%s",
16581652
"target": "/var/www/html",
1659-
"bind": {
1660-
"create_host_path": true
1661-
}
1653+
"bind": {}
16621654
},
16631655
{
16641656
"type": "bind",
16651657
"source": "%s",
16661658
"target": "/etc/configs",
16671659
"read_only": true,
1668-
"bind": {
1669-
"create_host_path": true
1670-
}
1660+
"bind": {}
16711661
},
16721662
{
16731663
"type": "volume",

schema/schema.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package schema
1919
import (
2020
// Enable support for embedded static resources
2121
_ "embed"
22+
"encoding/json"
2223
"errors"
2324
"fmt"
2425
"slices"
@@ -48,11 +49,11 @@ var Schema string
4849
// Validate uses the jsonschema to validate the configuration
4950
func Validate(config map[string]interface{}) error {
5051
compiler := jsonschema.NewCompiler()
51-
json, err := jsonschema.UnmarshalJSON(strings.NewReader(Schema))
52+
shema, err := jsonschema.UnmarshalJSON(strings.NewReader(Schema))
5253
if err != nil {
5354
return err
5455
}
55-
err = compiler.AddResource("compose-spec.json", json)
56+
err = compiler.AddResource("compose-spec.json", shema)
5657
if err != nil {
5758
return err
5859
}
@@ -61,7 +62,21 @@ func Validate(config map[string]interface{}) error {
6162
Validate: durationFormatChecker,
6263
})
6364
schema := compiler.MustCompile("compose-spec.json")
64-
err = schema.Validate(config)
65+
66+
// santhosh-tekuri doesn't allow derived types
67+
// see https://github.com/santhosh-tekuri/jsonschema/pull/240
68+
marshaled, err := json.Marshal(config)
69+
if err != nil {
70+
return err
71+
}
72+
73+
var raw map[string]interface{}
74+
err = json.Unmarshal(marshaled, &raw)
75+
if err != nil {
76+
return err
77+
}
78+
79+
err = schema.Validate(raw)
6580
var verr *jsonschema.ValidationError
6681
if ok := errors.As(err, &verr); ok {
6782
return validationError{getMostSpecificError(verr)}

types/envfile.go

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,8 @@
1616

1717
package types
1818

19-
import (
20-
"encoding/json"
21-
)
22-
2319
type EnvFile struct {
2420
Path string `yaml:"path,omitempty" json:"path,omitempty"`
25-
Required bool `yaml:"required" json:"required"`
21+
Required OptOut `yaml:"required,omitempty" json:"required,omitzero"`
2622
Format string `yaml:"format,omitempty" json:"format,omitempty"`
2723
}
28-
29-
// MarshalYAML makes EnvFile implement yaml.Marshaler
30-
func (e EnvFile) MarshalYAML() (interface{}, error) {
31-
if e.Required {
32-
return e.Path, nil
33-
}
34-
return map[string]any{
35-
"path": e.Path,
36-
"required": e.Required,
37-
}, nil
38-
}
39-
40-
// MarshalJSON makes EnvFile implement json.Marshaler
41-
func (e *EnvFile) MarshalJSON() ([]byte, error) {
42-
if e.Required {
43-
return json.Marshal(e.Path)
44-
}
45-
// Pass as a value to avoid re-entering this method and use the default implementation
46-
return json.Marshal(*e)
47-
}

types/types.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,12 +564,20 @@ const (
564564
type ServiceVolumeBind struct {
565565
SELinux string `yaml:"selinux,omitempty" json:"selinux,omitempty"`
566566
Propagation string `yaml:"propagation,omitempty" json:"propagation,omitempty"`
567-
CreateHostPath bool `yaml:"create_host_path,omitempty" json:"create_host_path,omitempty"`
567+
CreateHostPath OptOut `yaml:"create_host_path,omitempty" json:"create_host_path,omitzero"`
568568
Recursive string `yaml:"recursive,omitempty" json:"recursive,omitempty"`
569569

570570
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
571571
}
572572

573+
// OptOut is a boolean which default value is 'true'
574+
type OptOut bool
575+
576+
func (o OptOut) IsZero() bool {
577+
// Attribute can be omitted if value is true
578+
return bool(o)
579+
}
580+
573581
// SELinux represents the SELinux re-labeling options.
574582
const (
575583
// SELinuxShared option indicates that the bind mount content is shared among multiple containers

types/types_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,37 @@ func TestMappingValues(t *testing.T) {
380380
})
381381
assert.DeepEqual(t, mapping.Values(), values)
382382
}
383+
384+
func TestMarhsall(t *testing.T) {
385+
p := Project{
386+
Services: Services{
387+
"test": ServiceConfig{
388+
Volumes: []ServiceVolumeConfig{
389+
{
390+
Type: "bind",
391+
Bind: &ServiceVolumeBind{
392+
CreateHostPath: true, // default
393+
},
394+
},
395+
{
396+
Type: "bind",
397+
Bind: &ServiceVolumeBind{
398+
CreateHostPath: false,
399+
},
400+
},
401+
},
402+
},
403+
},
404+
}
405+
marshalYAML, err := p.MarshalYAML()
406+
assert.NilError(t, err)
407+
assert.Equal(t, string(marshalYAML), `services:
408+
test:
409+
volumes:
410+
- type: bind
411+
bind: {}
412+
- type: bind
413+
bind:
414+
create_host_path: false
415+
`)
416+
}

0 commit comments

Comments
 (0)