Skip to content

Commit 7fb08db

Browse files
authored
Merge branch 'main' into issue-12709
2 parents 61de0c4 + 31e3c28 commit 7fb08db

File tree

8 files changed

+134
-29
lines changed

8 files changed

+134
-29
lines changed

loader/loader_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ services:
597597
"GA": strPtr("2.5"),
598598
"BU": strPtr(""),
599599
"MEU": strPtr("Shadoks"),
600+
"ZO": nil,
600601
}
601602

602603
assert.Check(t, is.Equal(2, len(config.Services)))
@@ -3777,3 +3778,24 @@ services:
37773778
Options: map[string]string{"bar": "zot"},
37783779
})
37793780
}
3781+
3782+
func TestImageVolume(t *testing.T) {
3783+
p, err := loadYAML(`
3784+
name: imageVolume
3785+
services:
3786+
test:
3787+
volumes:
3788+
- type: image
3789+
source: app/image
3790+
target: /mnt/image
3791+
image:
3792+
subpath: /foo
3793+
`)
3794+
assert.NilError(t, err)
3795+
assert.DeepEqual(t, p.Services["test"].Volumes[0], types.ServiceVolumeConfig{
3796+
Type: "image",
3797+
Source: "app/image",
3798+
Target: "/mnt/image",
3799+
Image: &types.ServiceVolumeImage{SubPath: "/foo"},
3800+
})
3801+
}

loader/loader_yaml_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ func TestParseYAMLFiles(t *testing.T) {
3030
{
3131
Filename: "test.yaml",
3232
Content: []byte(`
33+
x-extension:
34+
test1: first
35+
3336
services:
3437
test:
3538
image: foo
@@ -40,6 +43,9 @@ services:
4043
{
4144
Filename: "override.yaml",
4245
Content: []byte(`
46+
x-extension:
47+
test2: second
48+
4349
services:
4450
test:
4551
image: bar
@@ -58,6 +64,10 @@ services:
5864
"init": false,
5965
},
6066
},
67+
"x-extension": map[string]interface{}{
68+
"test1": "first",
69+
"test2": "second",
70+
},
6171
})
6272
}
6373

override/merge.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"cmp"
2121
"fmt"
2222
"slices"
23-
"strings"
2423

2524
"github.com/compose-spec/compose-go/v2/tree"
2625
)
@@ -62,6 +61,7 @@ func init() {
6261
mergeSpecials["services.*.extra_hosts"] = mergeExtraHosts
6362
mergeSpecials["services.*.healthcheck.test"] = override
6463
mergeSpecials["services.*.labels"] = mergeToSequence
64+
mergeSpecials["services.*.volumes.*.volume.labels"] = mergeToSequence
6565
mergeSpecials["services.*.logging"] = mergeLogging
6666
mergeSpecials["services.*.networks"] = mergeNetworks
6767
mergeSpecials["services.*.sysctls"] = mergeToSequence
@@ -104,7 +104,7 @@ func mergeYaml(e any, o any, p tree.Path) (any, error) {
104104
func mergeMappings(mapping map[string]any, other map[string]any, p tree.Path) (map[string]any, error) {
105105
for k, v := range other {
106106
e, ok := mapping[k]
107-
if !ok || strings.HasPrefix(k, "x-") {
107+
if !ok {
108108
mapping[k] = v
109109
continue
110110
}
@@ -234,9 +234,17 @@ func mergeIPAMConfig(c any, o any, path tree.Path) (any, error) {
234234
}
235235

236236
var ipamConfigs []any
237-
for _, original := range c.([]any) {
237+
configs, ok := c.([]any)
238+
if !ok {
239+
return o, fmt.Errorf("%s: unexpected type %T", path, c)
240+
}
241+
overrides, ok := o.([]any)
242+
if !ok {
243+
return o, fmt.Errorf("%s: unexpected type %T", path, c)
244+
}
245+
for _, original := range configs {
238246
right := convertIntoMapping(original, nil)
239-
for _, override := range o.([]any) {
247+
for _, override := range overrides {
240248
left := convertIntoMapping(override, nil)
241249
if left["subnet"] != right["subnet"] {
242250
// check if left is already in ipamConfigs, add it if not and continue with the next config

schema/compose-spec.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,9 @@
418418
"type": "object",
419419
"required": ["type"],
420420
"properties": {
421-
"type": {"type": "string"},
421+
"type": {"type": "string",
422+
"enum": ["bind", "volume", "tmpfs", "cluster", "image"]
423+
},
422424
"source": {"type": "string"},
423425
"target": {"type": "string"},
424426
"read_only": {"type": ["boolean", "string"]},
@@ -437,6 +439,7 @@
437439
"volume": {
438440
"type": "object",
439441
"properties": {
442+
"labels": {"$ref": "#/definitions/list_or_dict"},
440443
"nocopy": {"type": ["boolean", "string"]},
441444
"subpath": {"type": "string"}
442445
},
@@ -456,6 +459,14 @@
456459
},
457460
"additionalProperties": false,
458461
"patternProperties": {"^x-": {}}
462+
},
463+
"image": {
464+
"type": "object",
465+
"properties": {
466+
"subpath": {"type": "string"}
467+
},
468+
"additionalProperties": false,
469+
"patternProperties": {"^x-": {}}
459470
}
460471
},
461472
"additionalProperties": false,

types/derived.gen.go

Lines changed: 40 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

types/project.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,16 +190,25 @@ func (p *Project) getServicesByNames(names ...string) (Services, []string) {
190190
if len(names) == 0 {
191191
return p.Services, nil
192192
}
193+
193194
services := Services{}
194195
var servicesNotFound []string
195196
for _, name := range names {
196-
service, ok := p.Services[name]
197-
if !ok {
197+
matched := false
198+
199+
for serviceName, service := range p.Services {
200+
match, _ := filepath.Match(name, serviceName)
201+
if match {
202+
services[serviceName] = service
203+
matched = true
204+
}
205+
}
206+
207+
if !matched {
198208
servicesNotFound = append(servicesNotFound, name)
199-
continue
200209
}
201-
services[name] = service
202210
}
211+
203212
return services, servicesNotFound
204213
}
205214

@@ -639,7 +648,7 @@ func (p *Project) MarshalJSON(options ...func(*marshallOptions)) ([]byte, error)
639648
func (p Project) WithServicesEnvironmentResolved(discardEnvFiles bool) (*Project, error) {
640649
newProject := p.deepCopy()
641650
for i, service := range newProject.Services {
642-
service.Environment = service.Environment.Resolve(newProject.Environment.Resolve).RemoveEmpty()
651+
service.Environment = service.Environment.Resolve(newProject.Environment.Resolve)
643652

644653
environment := service.Environment.ToMapping()
645654
for _, envFile := range service.EnvFiles {

types/project_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"errors"
2222
"fmt"
2323
"slices"
24+
"sort"
2425
"strings"
2526
"testing"
2627

@@ -281,6 +282,18 @@ func TestWithServices(t *testing.T) {
281282
assert.DeepEqual(t, seen, []string{"service_1", "service_2", "service_4"})
282283
}
283284

285+
func TestWithServicesWithWildcard(t *testing.T) {
286+
p := makeProject()
287+
var seen []string
288+
err := p.ForEachService([]string{"service_*"}, func(name string, _ *ServiceConfig) error {
289+
seen = append(seen, name)
290+
return nil
291+
}, IgnoreDependencies)
292+
assert.NilError(t, err)
293+
sort.Strings(seen)
294+
assert.DeepEqual(t, seen, []string{"service_1", "service_2", "service_3", "service_4", "service_5", "service_6"})
295+
}
296+
284297
func TestServicesWithBuild(t *testing.T) {
285298
p := makeProject()
286299
assert.Equal(t, len(p.ServicesWithBuild()), 0)

types/types.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ type ServiceVolumeConfig struct {
541541
Bind *ServiceVolumeBind `yaml:"bind,omitempty" json:"bind,omitempty"`
542542
Volume *ServiceVolumeVolume `yaml:"volume,omitempty" json:"volume,omitempty"`
543543
Tmpfs *ServiceVolumeTmpfs `yaml:"tmpfs,omitempty" json:"tmpfs,omitempty"`
544+
Image *ServiceVolumeImage `yaml:"image,omitempty" json:"image,omitempty"`
544545

545546
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
546547
}
@@ -575,6 +576,8 @@ const (
575576
VolumeTypeNamedPipe = "npipe"
576577
// VolumeTypeCluster is the type for mounting container storage interface (CSI) volumes
577578
VolumeTypeCluster = "cluster"
579+
// VolumeTypeImage is the tpe for mounting an image
580+
VolumeTypeImage = "image"
578581

579582
// SElinuxShared share the volume content
580583
SElinuxShared = "z"
@@ -618,8 +621,9 @@ const (
618621

619622
// ServiceVolumeVolume are options for a service volume of type volume
620623
type ServiceVolumeVolume struct {
621-
NoCopy bool `yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
622-
Subpath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
624+
Labels Mapping `yaml:"labels,omitempty" json:"labels,omitempty"`
625+
NoCopy bool `yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
626+
Subpath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
623627

624628
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
625629
}
@@ -633,6 +637,11 @@ type ServiceVolumeTmpfs struct {
633637
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
634638
}
635639

640+
type ServiceVolumeImage struct {
641+
SubPath string `yaml:"subpath,omitempty" json:"subpath,omitempty"`
642+
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
643+
}
644+
636645
type FileMode int64
637646

638647
// FileReferenceConfig for a reference to a swarm file object

0 commit comments

Comments
 (0)