bumpo compose-go to v2.1.0

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
This commit is contained in:
Guillaume Lours
2024-04-23 10:28:28 +02:00
parent 699fa43f7f
commit e3e16ad088
29 changed files with 146 additions and 66 deletions

View File

@ -29,12 +29,15 @@ import (
"github.com/compose-spec/compose-go/v2/types"
)
// loadIncludeConfig parse the require config from raw yaml
// loadIncludeConfig parse the required config from raw yaml
func loadIncludeConfig(source any) ([]types.IncludeConfig, error) {
if source == nil {
return nil, nil
}
configs := source.([]any)
configs, ok := source.([]any)
if !ok {
return nil, fmt.Errorf("`include` must be a list, got %s", source)
}
for i, config := range configs {
if v, ok := config.(string); ok {
configs[i] = map[string]any{
@ -73,11 +76,19 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
p = path
if i == 0 { // This is the "main" file, used to define project-directory. Others are overrides
relworkingdir = loader.Dir(path)
if r.ProjectDirectory == "" {
r.ProjectDirectory = filepath.Dir(path)
}
switch {
case r.ProjectDirectory == "":
relworkingdir = loader.Dir(path)
r.ProjectDirectory = filepath.Dir(path)
case !filepath.IsAbs(r.ProjectDirectory):
relworkingdir = loader.Dir(r.ProjectDirectory)
r.ProjectDirectory = filepath.Join(configDetails.WorkingDir, r.ProjectDirectory)
default:
relworkingdir = r.ProjectDirectory
}
for _, f := range included {
if f == path {
included = append(included, path)

View File

@ -148,8 +148,11 @@ func (l localResourceLoader) Load(_ context.Context, p string) (string, error) {
return l.abs(p), nil
}
func (l localResourceLoader) Dir(path string) string {
path = l.abs(filepath.Dir(path))
func (l localResourceLoader) Dir(originalPath string) string {
path := l.abs(originalPath)
if !l.isDir(path) {
path = l.abs(filepath.Dir(originalPath))
}
rel, err := filepath.Rel(l.WorkingDir, path)
if err != nil {
return path
@ -157,6 +160,14 @@ func (l localResourceLoader) Dir(path string) string {
return rel
}
func (l localResourceLoader) isDir(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
}
return fileInfo.IsDir()
}
func (o *Options) clone() *Options {
return &Options{
SkipValidation: o.SkipValidation,
@ -452,7 +463,7 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
}
}
dict, err = transform.Canonical(dict)
dict, err = transform.Canonical(dict, opts.SkipInterpolation)
if err != nil {
return nil, err
}

View File

@ -26,18 +26,12 @@ import (
// Normalize compose project by moving deprecated attributes to their canonical position and injecting implicit defaults
func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) {
dict["networks"] = normalizeNetworks(dict)
normalizeNetworks(dict)
if d, ok := dict["services"]; ok {
services := d.(map[string]any)
for name, s := range services {
service := s.(map[string]any)
_, hasNetworks := service["networks"]
_, hasNetworkMode := service["network_mode"]
if !hasNetworks && !hasNetworkMode {
// Service without explicit network attachment are implicitly exposed on default network
service["networks"] = map[string]any{"default": nil}
}
if service["pull_policy"] == types.PullPolicyIfNotPresent {
service["pull_policy"] = types.PullPolicyMissing
@ -137,18 +131,51 @@ func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) {
return dict, nil
}
func normalizeNetworks(dict map[string]any) map[string]any {
func normalizeNetworks(dict map[string]any) {
var networks map[string]any
if n, ok := dict["networks"]; ok {
networks = n.(map[string]any)
} else {
networks = map[string]any{}
}
if _, ok := networks["default"]; !ok {
// implicit `default` network must be introduced only if actually used by some service
usesDefaultNetwork := false
if s, ok := dict["services"]; ok {
services := s.(map[string]any)
for name, se := range services {
service := se.(map[string]any)
if _, ok := service["network_mode"]; ok {
continue
}
if n, ok := service["networks"]; !ok {
// If none explicitly declared, service is connected to default network
service["networks"] = map[string]any{"default": nil}
usesDefaultNetwork = true
} else {
net := n.(map[string]any)
if len(net) == 0 {
// networks section declared but empty (corner case)
service["networks"] = map[string]any{"default": nil}
usesDefaultNetwork = true
} else if _, ok := net["default"]; ok {
usesDefaultNetwork = true
}
}
services[name] = service
}
dict["services"] = services
}
if _, ok := networks["default"]; !ok && usesDefaultNetwork {
// If not declared explicitly, Compose model involves an implicit "default" network
networks["default"] = nil
}
return networks
if len(networks) > 0 {
dict["networks"] = networks
}
}
func resolve(a any, fn func(s string) (string, bool)) (any, bool) {

View File

@ -19,6 +19,7 @@ package loader
import (
"fmt"
"strconv"
"strings"
"github.com/compose-spec/compose-go/v2/tree"
"gopkg.in/yaml.v3"
@ -40,6 +41,15 @@ func (p *ResetProcessor) UnmarshalYAML(value *yaml.Node) error {
// resolveReset detects `!reset` tag being set on yaml nodes and record position in the yaml tree
func (p *ResetProcessor) resolveReset(node *yaml.Node, path tree.Path) (*yaml.Node, error) {
// If the path contains "<<", removing the "<<" element and merging the path
if strings.Contains(path.String(), ".<<") {
path = tree.NewPath(strings.Replace(path.String(), ".<<", "", 1))
}
// If the node is an alias, We need to process the alias field in order to consider the !override and !reset tags
if node.Kind == yaml.AliasNode {
return p.resolveReset(node.Alias, path)
}
if node.Tag == "!reset" {
p.paths = append(p.paths, path)
return nil, nil

View File

@ -28,7 +28,6 @@ import (
// checkConsistency validate a compose model is consistent
func checkConsistency(project *types.Project) error {
containerNames := map[string]string{}
for _, s := range project.Services {
if s.Build == nil && s.Image == "" {
return fmt.Errorf("service %q has neither an image nor a build context specified: %w", s.Name, errdefs.ErrInvalid)
@ -145,13 +144,6 @@ func checkConsistency(project *types.Project) error {
}
}
if s.ContainerName != "" {
if existing, ok := containerNames[s.ContainerName]; ok {
return fmt.Errorf(`"services.%s": container name "%s" is already in use by "services.%s": %w`, s.Name, s.ContainerName, existing, errdefs.ErrInvalid)
}
containerNames[s.ContainerName] = s.Name
}
if s.GetScale() > 1 && s.ContainerName != "" {
attr := "scale"
if s.Scale == nil {