bump compose-go to version v2.13.0
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,7 @@ require (
|
|||||||
github.com/Microsoft/go-winio v0.6.2
|
github.com/Microsoft/go-winio v0.6.2
|
||||||
github.com/ProtonMail/go-crypto v1.3.0
|
github.com/ProtonMail/go-crypto v1.3.0
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.24
|
github.com/aws/aws-sdk-go-v2/config v1.32.24
|
||||||
github.com/compose-spec/compose-go/v2 v2.11.0
|
github.com/compose-spec/compose-go/v2 v2.13.0
|
||||||
github.com/containerd/console v1.0.5
|
github.com/containerd/console v1.0.5
|
||||||
github.com/containerd/containerd/v2 v2.2.5
|
github.com/containerd/containerd/v2 v2.2.5
|
||||||
github.com/containerd/continuity v0.5.0
|
github.com/containerd/continuity v0.5.0
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg
|
|||||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||||
github.com/compose-spec/compose-go/v2 v2.11.0 h1:xoq/ootgIL6TsHmbJHrkuh7+bzjhPV3NHftHRPPyVXM=
|
github.com/compose-spec/compose-go/v2 v2.13.0 h1:2+2oS3v4SrtAOBdZRAZYBsBy47D571p5EXMSCppmTtE=
|
||||||
github.com/compose-spec/compose-go/v2 v2.11.0/go.mod h1:ZU6zlcweCZKyiB7BVfCizQT9XmkEIMFE+PRZydVcsZg=
|
github.com/compose-spec/compose-go/v2 v2.13.0/go.mod h1:ZU6zlcweCZKyiB7BVfCizQT9XmkEIMFE+PRZydVcsZg=
|
||||||
github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ=
|
github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ=
|
||||||
github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
|
github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
|
||||||
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
|
||||||
|
|||||||
+18
@@ -383,6 +383,24 @@ func WithoutEnvironmentResolution(o *ProjectOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSelectedServices restricts the loaded project to the given services and their
|
||||||
|
// dependencies. An empty list means "all services". When set, services not in the
|
||||||
|
// list are dropped from the project before environment resolution, so their
|
||||||
|
// `env_file` / `label_file` entries are not loaded from disk.
|
||||||
|
func WithSelectedServices(services ...string) ProjectOptionsFn {
|
||||||
|
return func(o *ProjectOptions) error {
|
||||||
|
o.loadOptions = append(o.loadOptions, loader.WithSelectedServices(services))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutUnnecessaryResources drops networks/volumes/secrets/configs/models that
|
||||||
|
// are not referenced by services remaining after selection.
|
||||||
|
func WithoutUnnecessaryResources(o *ProjectOptions) error {
|
||||||
|
o.loadOptions = append(o.loadOptions, loader.WithoutUnnecessaryResources)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultFileNames defines the Compose file names for auto-discovery (in order of preference)
|
// DefaultFileNames defines the Compose file names for auto-discovery (in order of preference)
|
||||||
var DefaultFileNames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"}
|
var DefaultFileNames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"}
|
||||||
|
|
||||||
|
|||||||
+44
@@ -78,6 +78,14 @@ type Options struct {
|
|||||||
projectNameImperativelySet bool
|
projectNameImperativelySet bool
|
||||||
// Profiles set profiles to enable
|
// Profiles set profiles to enable
|
||||||
Profiles []string
|
Profiles []string
|
||||||
|
// SelectedServices restricts the project model to these services (and their dependencies)
|
||||||
|
// after parsing. An empty slice means "all services". When set, services not in the list
|
||||||
|
// are dropped from the project before environment resolution, so their env_file / label_file
|
||||||
|
// entries are not loaded.
|
||||||
|
SelectedServices []string
|
||||||
|
// PruneUnnecessaryResources drops networks/volumes/secrets/configs/models that are not
|
||||||
|
// referenced by active services after service selection.
|
||||||
|
PruneUnnecessaryResources bool
|
||||||
// ResourceLoaders manages support for remote resources
|
// ResourceLoaders manages support for remote resources
|
||||||
ResourceLoaders []ResourceLoader
|
ResourceLoaders []ResourceLoader
|
||||||
// KnownExtensions manages x-* attribute we know and the corresponding go structs
|
// KnownExtensions manages x-* attribute we know and the corresponding go structs
|
||||||
@@ -187,6 +195,8 @@ func (o *Options) clone() *Options {
|
|||||||
projectName: o.projectName,
|
projectName: o.projectName,
|
||||||
projectNameImperativelySet: o.projectNameImperativelySet,
|
projectNameImperativelySet: o.projectNameImperativelySet,
|
||||||
Profiles: o.Profiles,
|
Profiles: o.Profiles,
|
||||||
|
SelectedServices: o.SelectedServices,
|
||||||
|
PruneUnnecessaryResources: o.PruneUnnecessaryResources,
|
||||||
ResourceLoaders: o.ResourceLoaders,
|
ResourceLoaders: o.ResourceLoaders,
|
||||||
KnownExtensions: o.KnownExtensions,
|
KnownExtensions: o.KnownExtensions,
|
||||||
Listeners: o.Listeners,
|
Listeners: o.Listeners,
|
||||||
@@ -260,6 +270,22 @@ func WithProfiles(profiles []string) func(*Options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSelectedServices restricts the loaded project to the given services and their
|
||||||
|
// dependencies. An empty slice means "all services". When set, services not in the
|
||||||
|
// list are dropped from the project before environment resolution: their `env_file`
|
||||||
|
// and `label_file` entries will not be loaded from disk.
|
||||||
|
func WithSelectedServices(services []string) func(*Options) {
|
||||||
|
return func(opts *Options) {
|
||||||
|
opts.SelectedServices = services
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutUnnecessaryResources drops networks/volumes/secrets/configs/models that
|
||||||
|
// are not referenced by services remaining after selection.
|
||||||
|
func WithoutUnnecessaryResources(opts *Options) {
|
||||||
|
opts.PruneUnnecessaryResources = true
|
||||||
|
}
|
||||||
|
|
||||||
// PostProcessor is used to tweak compose model based on metadata extracted during yaml Unmarshal phase
|
// PostProcessor is used to tweak compose model based on metadata extracted during yaml Unmarshal phase
|
||||||
// that hardly can be implemented using go-yaml and mapstructure
|
// that hardly can be implemented using go-yaml and mapstructure
|
||||||
type PostProcessor interface {
|
type PostProcessor interface {
|
||||||
@@ -603,6 +629,24 @@ func ModelToProject(dict map[string]interface{}, opts *Options, configDetails ty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(opts.SelectedServices) > 0 {
|
||||||
|
// WithServicesEnabled must precede WithSelectedServices: the latter walks
|
||||||
|
// only active services, so any selected service currently sitting in
|
||||||
|
// DisabledServices (e.g. gated by a profile) would otherwise be invisible.
|
||||||
|
project, err = project.WithServicesEnabled(opts.SelectedServices...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
project, err = project.WithSelectedServices(opts.SelectedServices)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.PruneUnnecessaryResources {
|
||||||
|
project = project.WithoutUnnecessaryResources()
|
||||||
|
}
|
||||||
|
|
||||||
if !opts.SkipResolveEnvironment {
|
if !opts.SkipResolveEnvironment {
|
||||||
project, err = project.WithServicesEnvironmentResolved(opts.discardEnvFiles)
|
project, err = project.WithServicesEnvironmentResolved(opts.discardEnvFiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+22
@@ -133,6 +133,9 @@ func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) {
|
|||||||
if len(dependsOn) > 0 {
|
if len(dependsOn) > 0 {
|
||||||
service["depends_on"] = dependsOn
|
service["depends_on"] = dependsOn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inheritPreStartImage(service)
|
||||||
|
|
||||||
services[name] = service
|
services[name] = service
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +146,25 @@ func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) {
|
|||||||
return dict, nil
|
return dict, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inheritPreStartImage propagates the parent service's image to any pre_start
|
||||||
|
// hook that does not declare its own, per compose-spec PR #647.
|
||||||
|
func inheritPreStartImage(service map[string]any) {
|
||||||
|
hooks, ok := service["pre_start"].([]any)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
image, ok := service["image"].(string)
|
||||||
|
if !ok || image == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, h := range hooks {
|
||||||
|
hook := h.(map[string]any)
|
||||||
|
if _, set := hook["image"]; !set {
|
||||||
|
hook["image"] = image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func normalizeNetworks(dict map[string]any) {
|
func normalizeNetworks(dict map[string]any) {
|
||||||
var networks map[string]any
|
var networks map[string]any
|
||||||
if n, ok := dict["networks"]; ok {
|
if n, ok := dict["networks"]; ok {
|
||||||
|
|||||||
+24
-14
@@ -40,7 +40,7 @@ type nodeCache struct {
|
|||||||
type ResetProcessor struct {
|
type ResetProcessor struct {
|
||||||
target any
|
target any
|
||||||
paths []tree.Path
|
paths []tree.Path
|
||||||
visitedNodes map[*yaml.Node][]string
|
visitedNodes map[*yaml.Node][]tree.Path
|
||||||
resolvedNodes map[*yaml.Node]nodeCache
|
resolvedNodes map[*yaml.Node]nodeCache
|
||||||
visitCount int
|
visitCount int
|
||||||
// maxNodeVisits is the per-document cap; when zero, defaultMaxNodeVisits is used.
|
// maxNodeVisits is the per-document cap; when zero, defaultMaxNodeVisits is used.
|
||||||
@@ -49,7 +49,7 @@ type ResetProcessor struct {
|
|||||||
|
|
||||||
// UnmarshalYAML implement yaml.Unmarshaler
|
// UnmarshalYAML implement yaml.Unmarshaler
|
||||||
func (p *ResetProcessor) UnmarshalYAML(value *yaml.Node) error {
|
func (p *ResetProcessor) UnmarshalYAML(value *yaml.Node) error {
|
||||||
p.visitedNodes = make(map[*yaml.Node][]string)
|
p.visitedNodes = make(map[*yaml.Node][]tree.Path)
|
||||||
p.resolvedNodes = make(map[*yaml.Node]nodeCache)
|
p.resolvedNodes = make(map[*yaml.Node]nodeCache)
|
||||||
p.visitCount = 0
|
p.visitCount = 0
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -190,7 +190,12 @@ func (p *ResetProcessor) resolveContainer(node *yaml.Node, path tree.Path) (*yam
|
|||||||
if resolved == nil {
|
if resolved == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if v.Kind == yaml.AliasNode {
|
// Under the merge key `<<`, the YAML library only accepts an
|
||||||
|
// AliasNode value when its target is a MappingNode. An alias to a
|
||||||
|
// SequenceNode (the spec-allowed "sequence of mappings" form via an
|
||||||
|
// anchor) is rejected. Substitute the resolved target so the YAML
|
||||||
|
// library sees the underlying node directly for merge keys.
|
||||||
|
if v.Kind == yaml.AliasNode && key != "<<" {
|
||||||
nodes = append(nodes, node.Content[idx-1], v)
|
nodes = append(nodes, node.Content[idx-1], v)
|
||||||
} else {
|
} else {
|
||||||
nodes = append(nodes, node.Content[idx-1], resolved)
|
nodes = append(nodes, node.Content[idx-1], resolved)
|
||||||
@@ -278,36 +283,41 @@ func (p *ResetProcessor) applyNullOverrides(target any, path tree.Path) error {
|
|||||||
|
|
||||||
func (p *ResetProcessor) checkForCycle(node *yaml.Node, path tree.Path) error {
|
func (p *ResetProcessor) checkForCycle(node *yaml.Node, path tree.Path) error {
|
||||||
paths := p.visitedNodes[node]
|
paths := p.visitedNodes[node]
|
||||||
pathStr := path.String()
|
|
||||||
|
|
||||||
for _, prevPath := range paths {
|
for _, prevPath := range paths {
|
||||||
// If we're visiting the exact same path, it's not a cycle
|
// If we're visiting the exact same path, it's not a cycle
|
||||||
if pathStr == prevPath {
|
if path == prevPath {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare on the raw form so dots inside escaped segment names (e.g.
|
||||||
|
// service names containing ".") aren't conflated with path separators.
|
||||||
|
pathStr := string(path)
|
||||||
|
prevStr := string(prevPath)
|
||||||
|
|
||||||
// If either path is using a merge key, it's legitimate YAML merging
|
// If either path is using a merge key, it's legitimate YAML merging
|
||||||
if strings.Contains(prevPath, "<<") || strings.Contains(pathStr, "<<") {
|
if strings.Contains(prevStr, "<<") || strings.Contains(pathStr, "<<") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only consider it a cycle if one path is contained within the other
|
// Only consider it a cycle if one path is contained within the other
|
||||||
// and they're not in different service definitions
|
// and they're not in different service definitions
|
||||||
if (strings.HasPrefix(pathStr, prevPath+".") ||
|
if (strings.HasPrefix(pathStr, prevStr+".") ||
|
||||||
strings.HasPrefix(prevPath, pathStr+".")) &&
|
strings.HasPrefix(prevStr, pathStr+".")) &&
|
||||||
!areInDifferentServices(pathStr, prevPath) {
|
!areInDifferentServices(path, prevPath) {
|
||||||
return fmt.Errorf("cycle detected: node at path %s references node at path %s", pathStr, prevPath)
|
return fmt.Errorf("cycle detected: node at path %s references node at path %s",
|
||||||
|
path.String(), prevPath.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.visitedNodes[node] = append(paths, pathStr)
|
p.visitedNodes[node] = append(paths, path)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// areInDifferentServices checks if two paths are in different service definitions
|
// areInDifferentServices checks if two paths are in different service definitions
|
||||||
func areInDifferentServices(path1, path2 string) bool {
|
func areInDifferentServices(path1, path2 tree.Path) bool {
|
||||||
parts1 := strings.Split(path1, ".")
|
parts1 := path1.Parts()
|
||||||
parts2 := strings.Split(path2, ".")
|
parts2 := path2.Parts()
|
||||||
for i := 0; i < len(parts1) && i < len(parts2); i++ {
|
for i := 0; i < len(parts1) && i < len(parts2); i++ {
|
||||||
if parts1[i] == "services" && i+1 < len(parts1) &&
|
if parts1[i] == "services" && i+1 < len(parts1) &&
|
||||||
parts2[i] == "services" && i+1 < len(parts2) {
|
parts2[i] == "services" && i+1 < len(parts2) {
|
||||||
|
|||||||
+42
@@ -688,6 +688,11 @@
|
|||||||
},
|
},
|
||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
|
"pre_start": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/pre_start_hook"},
|
||||||
|
"description": "Init containers to run to completion before the service container is started. Each step runs in its own ephemeral container, in declared order; a non-zero exit fails the bring-up of the service and its dependents."
|
||||||
|
},
|
||||||
"post_start": {
|
"post_start": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/$defs/service_hook"},
|
"items": {"$ref": "#/$defs/service_hook"},
|
||||||
@@ -1657,6 +1662,43 @@
|
|||||||
"required": ["command"]
|
"required": ["command"]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"pre_start_hook": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Configuration for a pre_start init container, run to completion before the service container starts.",
|
||||||
|
"properties": {
|
||||||
|
"command": {
|
||||||
|
"$ref": "#/$defs/command",
|
||||||
|
"description": "Command to execute. Optional when the chosen image's entrypoint already runs the intended command."
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Image used for the ephemeral container. If omitted, the parent service's image is used."
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "User to run the command as. Defaults to the user declared in image (or to the service's user when image is omitted)."
|
||||||
|
},
|
||||||
|
"privileged": {
|
||||||
|
"type": ["boolean", "string"],
|
||||||
|
"description": "Whether to run the command with extended privileges."
|
||||||
|
},
|
||||||
|
"working_dir": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Working directory for the command. Defaults to the service's working directory."
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"$ref": "#/$defs/list_or_dict",
|
||||||
|
"description": "Environment variables for the command. Appended to or overriding the service environment."
|
||||||
|
},
|
||||||
|
"per_replica": {
|
||||||
|
"type": ["boolean", "string"],
|
||||||
|
"description": "Whether the hook runs once per service replica (true), or once for the service as a whole before any replica starts (false, the default)."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {"^x-": {}}
|
||||||
|
},
|
||||||
|
|
||||||
"env_file": {
|
"env_file": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
+35
-11
@@ -24,6 +24,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/santhosh-tekuri/jsonschema/v6"
|
"github.com/santhosh-tekuri/jsonschema/v6"
|
||||||
@@ -46,22 +47,45 @@ func durationFormatChecker(input any) error {
|
|||||||
//go:embed compose-spec.json
|
//go:embed compose-spec.json
|
||||||
var Schema string
|
var Schema string
|
||||||
|
|
||||||
|
// compiledSchema is the compose-spec schema compiled once and reused across
|
||||||
|
// every Validate call. Compiling the schema (which loads and resolves the
|
||||||
|
// draft 2020-12 meta-schema) is expensive and was previously redone on every
|
||||||
|
// call; doing it once behind a sync.Once keeps Validate cheap and makes it
|
||||||
|
// safe to call from several goroutines without sharing any mutable compiler
|
||||||
|
// state. see https://github.com/docker/compose/issues/13866
|
||||||
|
var (
|
||||||
|
compiledSchema *jsonschema.Schema
|
||||||
|
compiledSchemaErr error
|
||||||
|
compiledSchemaOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func compileSchema() (*jsonschema.Schema, error) {
|
||||||
|
compiledSchemaOnce.Do(func() {
|
||||||
|
compiler := jsonschema.NewCompiler()
|
||||||
|
shema, err := jsonschema.UnmarshalJSON(strings.NewReader(Schema))
|
||||||
|
if err != nil {
|
||||||
|
compiledSchemaErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := compiler.AddResource("compose-spec.json", shema); err != nil {
|
||||||
|
compiledSchemaErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
compiler.RegisterFormat(&jsonschema.Format{
|
||||||
|
Name: "duration",
|
||||||
|
Validate: durationFormatChecker,
|
||||||
|
})
|
||||||
|
compiledSchema, compiledSchemaErr = compiler.Compile("compose-spec.json")
|
||||||
|
})
|
||||||
|
return compiledSchema, compiledSchemaErr
|
||||||
|
}
|
||||||
|
|
||||||
// Validate uses the jsonschema to validate the configuration
|
// Validate uses the jsonschema to validate the configuration
|
||||||
func Validate(config map[string]interface{}) error {
|
func Validate(config map[string]interface{}) error {
|
||||||
compiler := jsonschema.NewCompiler()
|
schema, err := compileSchema()
|
||||||
shema, err := jsonschema.UnmarshalJSON(strings.NewReader(Schema))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = compiler.AddResource("compose-spec.json", shema)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
compiler.RegisterFormat(&jsonschema.Format{
|
|
||||||
Name: "duration",
|
|
||||||
Validate: durationFormatChecker,
|
|
||||||
})
|
|
||||||
schema := compiler.MustCompile("compose-spec.json")
|
|
||||||
|
|
||||||
// santhosh-tekuri doesn't allow derived types
|
// santhosh-tekuri doesn't allow derived types
|
||||||
// see https://github.com/santhosh-tekuri/jsonschema/pull/240
|
// see https://github.com/santhosh-tekuri/jsonschema/pull/240
|
||||||
|
|||||||
+26
@@ -729,6 +729,24 @@ func deriveDeepCopyService(dst, src *ServiceConfig) {
|
|||||||
copy(dst.VolumesFrom, src.VolumesFrom)
|
copy(dst.VolumesFrom, src.VolumesFrom)
|
||||||
}
|
}
|
||||||
dst.WorkingDir = src.WorkingDir
|
dst.WorkingDir = src.WorkingDir
|
||||||
|
if src.PreStart == nil {
|
||||||
|
dst.PreStart = nil
|
||||||
|
} else {
|
||||||
|
if dst.PreStart != nil {
|
||||||
|
if len(src.PreStart) > len(dst.PreStart) {
|
||||||
|
if cap(dst.PreStart) >= len(src.PreStart) {
|
||||||
|
dst.PreStart = (dst.PreStart)[:len(src.PreStart)]
|
||||||
|
} else {
|
||||||
|
dst.PreStart = make([]ServiceHook, len(src.PreStart))
|
||||||
|
}
|
||||||
|
} else if len(src.PreStart) < len(dst.PreStart) {
|
||||||
|
dst.PreStart = (dst.PreStart)[:len(src.PreStart)]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dst.PreStart = make([]ServiceHook, len(src.PreStart))
|
||||||
|
}
|
||||||
|
deriveDeepCopy_26(dst.PreStart, src.PreStart)
|
||||||
|
}
|
||||||
if src.PostStart == nil {
|
if src.PostStart == nil {
|
||||||
dst.PostStart = nil
|
dst.PostStart = nil
|
||||||
} else {
|
} else {
|
||||||
@@ -2101,6 +2119,7 @@ func deriveDeepCopy_49(dst, src *ServiceHook) {
|
|||||||
}
|
}
|
||||||
copy(dst.Command, src.Command)
|
copy(dst.Command, src.Command)
|
||||||
}
|
}
|
||||||
|
dst.Image = src.Image
|
||||||
dst.User = src.User
|
dst.User = src.User
|
||||||
dst.Privileged = src.Privileged
|
dst.Privileged = src.Privileged
|
||||||
dst.WorkingDir = src.WorkingDir
|
dst.WorkingDir = src.WorkingDir
|
||||||
@@ -2110,6 +2129,7 @@ func deriveDeepCopy_49(dst, src *ServiceHook) {
|
|||||||
} else {
|
} else {
|
||||||
dst.Environment = nil
|
dst.Environment = nil
|
||||||
}
|
}
|
||||||
|
dst.PerReplica = src.PerReplica
|
||||||
if src.Extensions != nil {
|
if src.Extensions != nil {
|
||||||
dst.Extensions = make(map[string]any, len(src.Extensions))
|
dst.Extensions = make(map[string]any, len(src.Extensions))
|
||||||
src.Extensions.DeepCopy(dst.Extensions)
|
src.Extensions.DeepCopy(dst.Extensions)
|
||||||
@@ -2139,6 +2159,12 @@ func deriveDeepCopy_50(dst, src *IPAMConfig) {
|
|||||||
}
|
}
|
||||||
deriveDeepCopy_60(dst.Config, src.Config)
|
deriveDeepCopy_60(dst.Config, src.Config)
|
||||||
}
|
}
|
||||||
|
if src.Options != nil {
|
||||||
|
dst.Options = make(map[string]string, len(src.Options))
|
||||||
|
deriveDeepCopy_5(dst.Options, src.Options)
|
||||||
|
} else {
|
||||||
|
dst.Options = nil
|
||||||
|
}
|
||||||
if src.Extensions != nil {
|
if src.Extensions != nil {
|
||||||
dst.Extensions = make(map[string]any, len(src.Extensions))
|
dst.Extensions = make(map[string]any, len(src.Extensions))
|
||||||
src.Extensions.DeepCopy(dst.Extensions)
|
src.Extensions.DeepCopy(dst.Extensions)
|
||||||
|
|||||||
+5
-1
@@ -16,13 +16,17 @@
|
|||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
// ServiceHook is a command to exec inside container by some lifecycle events
|
// ServiceHook is a hook executed at a service lifecycle event: a command exec'd
|
||||||
|
// inside the service container for post_start/pre_stop, or an ephemeral
|
||||||
|
// container run before the service starts for pre_start.
|
||||||
type ServiceHook struct {
|
type ServiceHook struct {
|
||||||
Command ShellCommand `yaml:"command,omitempty" json:"command"`
|
Command ShellCommand `yaml:"command,omitempty" json:"command"`
|
||||||
|
Image string `yaml:"image,omitempty" json:"image,omitempty"`
|
||||||
User string `yaml:"user,omitempty" json:"user,omitempty"`
|
User string `yaml:"user,omitempty" json:"user,omitempty"`
|
||||||
Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty"`
|
Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty"`
|
||||||
WorkingDir string `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
|
WorkingDir string `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
|
||||||
Environment MappingWithEquals `yaml:"environment,omitempty" json:"environment,omitempty"`
|
Environment MappingWithEquals `yaml:"environment,omitempty" json:"environment,omitempty"`
|
||||||
|
PerReplica bool `yaml:"per_replica,omitempty" json:"per_replica,omitempty"`
|
||||||
|
|
||||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -138,6 +138,7 @@ type ServiceConfig struct {
|
|||||||
Volumes []ServiceVolumeConfig `yaml:"volumes,omitempty" json:"volumes,omitempty"`
|
Volumes []ServiceVolumeConfig `yaml:"volumes,omitempty" json:"volumes,omitempty"`
|
||||||
VolumesFrom []string `yaml:"volumes_from,omitempty" json:"volumes_from,omitempty"`
|
VolumesFrom []string `yaml:"volumes_from,omitempty" json:"volumes_from,omitempty"`
|
||||||
WorkingDir string `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
|
WorkingDir string `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
|
||||||
|
PreStart []ServiceHook `yaml:"pre_start,omitempty" json:"pre_start,omitempty"`
|
||||||
PostStart []ServiceHook `yaml:"post_start,omitempty" json:"post_start,omitempty"`
|
PostStart []ServiceHook `yaml:"post_start,omitempty" json:"post_start,omitempty"`
|
||||||
PreStop []ServiceHook `yaml:"pre_stop,omitempty" json:"pre_stop,omitempty"`
|
PreStop []ServiceHook `yaml:"pre_stop,omitempty" json:"pre_stop,omitempty"`
|
||||||
|
|
||||||
@@ -753,6 +754,7 @@ type NetworkConfig struct {
|
|||||||
type IPAMConfig struct {
|
type IPAMConfig struct {
|
||||||
Driver string `yaml:"driver,omitempty" json:"driver,omitempty"`
|
Driver string `yaml:"driver,omitempty" json:"driver,omitempty"`
|
||||||
Config []*IPAMPool `yaml:"config,omitempty" json:"config,omitempty"`
|
Config []*IPAMPool `yaml:"config,omitempty" json:"config,omitempty"`
|
||||||
|
Options Options `yaml:"options,omitempty" json:"options,omitempty"`
|
||||||
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
Extensions Extensions `yaml:"#extensions,inline,omitempty" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -190,7 +190,7 @@ github.com/cloudflare/circl/math/mlsbset
|
|||||||
github.com/cloudflare/circl/sign
|
github.com/cloudflare/circl/sign
|
||||||
github.com/cloudflare/circl/sign/ed25519
|
github.com/cloudflare/circl/sign/ed25519
|
||||||
github.com/cloudflare/circl/sign/ed448
|
github.com/cloudflare/circl/sign/ed448
|
||||||
# github.com/compose-spec/compose-go/v2 v2.11.0
|
# github.com/compose-spec/compose-go/v2 v2.13.0
|
||||||
## explicit; go 1.24
|
## explicit; go 1.24
|
||||||
github.com/compose-spec/compose-go/v2/cli
|
github.com/compose-spec/compose-go/v2/cli
|
||||||
github.com/compose-spec/compose-go/v2/consts
|
github.com/compose-spec/compose-go/v2/consts
|
||||||
|
|||||||
Reference in New Issue
Block a user