From aaf67687d2481cbf78fdbad28e33dfd813003266 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Fri, 4 Sep 2026 02:40:03 +0200 Subject: [PATCH] feat: validate remaining unsupported items of the support-matrix (#429) * feat: validate remaining unsupported items of the support-matrix Validate unsupported compose features: external configs, container_name, and placement. This makes the code match the support-matrix as of now (added comment to that effect as well). Signed-off-by: Miek Gieben * support matrix: some deploy modes are supported Add tests for the various non-supported deploy modes. Prefix the warnings with `deploy ` to signal the section, and to differentiate between top level 'labels' and 'deploy labels'. Signed-off-by: Miek Gieben --------- Signed-off-by: Miek Gieben --- pkg/client/compose/project_test.go | 73 ++++++++++++++++++++++++++++++ pkg/client/compose/service.go | 45 ++++++++++++++++-- 2 files changed, 113 insertions(+), 5 deletions(-) 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 {