diff --git a/pkg/client/compose/project_test.go b/pkg/client/compose/project_test.go index 2f97cbb7..fc0c45dc 100644 --- a/pkg/client/compose/project_test.go +++ b/pkg/client/compose/project_test.go @@ -322,6 +322,79 @@ volumes: `, shouldErr: true, }, + { + name: "external configs", + composeYAML: `services: + app: + image: myapp:latest +configs: + http_config: + external: true +`, + warnCount: 1, + warnContains: []string{"external"}, + }, + { + name: "container_name", + composeYAML: `services: + app: + image: myapp:latest + container_name: my_container +`, + warnCount: 1, + warnContains: []string{"container_name"}, + }, + { + name: "deploy mode", + composeYAML: `services: + app: + image: myapp:latest + deploy: + mode: replicated-job + replicas: 2 +`, + warnCount: 1, + warnContains: []string{"deploy mode must"}, + }, + { + name: "deploy mode", + composeYAML: `services: + app: + image: myapp:latest + deploy: + mode: replicated + labels: + - foo + replicas: 2 +`, + warnCount: 1, + warnContains: []string{"deploy labels"}, + }, + { + name: "deploy restart_policy", + composeYAML: `services: + app: + image: myapp:latest + deploy: + restart_policy: + condition: on-failure +`, + warnCount: 1, + warnContains: []string{"deploy restart_policy"}, + }, + { + name: "deploy placement", + composeYAML: `services: + app: + image: myapp:latest + deploy: + placement: + preferences: + - spread: foo +`, + warnCount: 1, + warnContains: []string{"deploy placement"}, + }, } for _, tt := range tests { diff --git a/pkg/client/compose/service.go b/pkg/client/compose/service.go index 492f435b..5a345d9a 100644 --- a/pkg/client/compose/service.go +++ b/pkg/client/compose/service.go @@ -454,13 +454,22 @@ func validateServicesExtensions(project *types.Project) error { // validateServicesFeatures checks services for unsupported features and returns all found. func validateServicesFeatures(project *types.Project) []error { - err := func(service, feature string) error { - return fmt.Errorf("service '%s': unsupported feature '%s', see %s", - service, feature, "https://uncloud.run/docs/compose-file-reference/support-matrix") + // XXX(miek): as of Sep 2026 these checks are in check with the support-matrix, except for a short syntax + // check. + var errs []error + const supportmatrix = "https://uncloud.run/docs/compose-file-reference/support-matrix" + + for _, config := range project.Configs { + if config.External { + errs = append(errs, fmt.Errorf("config '%s': unsupported feature '%s', see %s", + config.Name, "external", supportmatrix)) + } } - // TODO: check other commonly used but unsupported features. - var errs []error + err := func(service, feature string) error { + return fmt.Errorf("service '%s': unsupported feature '%s', see %s", + service, feature, supportmatrix) + } for _, service := range project.Services { if service.SecurityOpt != nil { errs = append(errs, err(service.Name, "security_opt")) @@ -489,6 +498,32 @@ func validateServicesFeatures(project *types.Project) []error { if service.StorageOpt != nil { errs = append(errs, err(service.Name, "storage_opt")) } + if service.ContainerName != "" { + errs = append(errs, err(service.Name, "container_name")) + } + if service.Deploy != nil { + if len(service.Deploy.Placement.Constraints) > 0 || len(service.Deploy.Placement.Preferences) > 0 { + errs = append(errs, fmt.Errorf( + "service '%s': deploy placement is not supported, "+ + "use x-machines instead: %s", service.Name, + "https://uncloud.run/docs/compose-file-reference/extensions#x-machines")) + } + + if service.Deploy.RestartPolicy != nil { + errs = append(errs, fmt.Errorf( + "service '%s': deploy restart_policy defaults to 'unless-stopped'", service.Name)) + } + if service.Deploy.Mode != "" && service.Deploy.Mode != "global" && service.Deploy.Mode != "replicated" { + errs = append(errs, fmt.Errorf( + "service '%s': deploy mode must either be 'global' or 'replicated'", service.Name)) + } + if service.Deploy.RollbackConfig != nil { + errs = append(errs, err(service.Name, "deploy rollback_config")) + } + if len(service.Deploy.Labels) != 0 { + errs = append(errs, err(service.Name, "deploy labels")) + } + } // we only allow the 'default' network, nothing else. if x := service.Networks; x != nil { if len(x) != 1 {