Repository: compose-spec/compose-go
Pull request #58 (2020-07-07 10:30:20)

Introduce ErrIncompatible
Define a higher-severity error type for compatibility checker to report incompatible attributes or values that make the compose file impossible to be used by Compose implementation. ErrUnsupported will result into ignored attributes.


Patch:
diff --git a/compatibility/allowlist.go b/compatibility/allowlist.go
index 651033b..5ed9691 100644
--- a/compatibility/allowlist.go
+++ b/compatibility/allowlist.go
@@ -45,6 +45,10 @@ func (c *AllowList) supported(attributes ...string) bool {
 	return false
 }
 
-func (c *AllowList) Error(message string, args ...interface{}) {
+func (c *AllowList) Unsupported(message string, args ...interface{}) {
 	c.errors = append(c.errors, errors.Wrap(errdefs.ErrUnsupported, fmt.Sprintf(message, args...)))
 }
+
+func (c *AllowList) Incompatible(message string, args ...interface{}) {
+	c.errors = append(c.errors, errors.Wrap(errdefs.ErrIncompatible, fmt.Sprintf(message, args...)))
+}
diff --git a/compatibility/build.go b/compatibility/build.go
index 01ec4c1..50d606f 100644
--- a/compatibility/build.go
+++ b/compatibility/build.go
@@ -21,7 +21,7 @@ import "github.com/compose-spec/compose-go/types"
 func (c *AllowList) CheckBuild(service *types.ServiceConfig) bool {
 	if !c.supported("services.build") && service.Build != nil {
 		service.Build = nil
-		c.Error("services.build")
+		c.Unsupported("services.build")
 		return false
 	}
 	return true
@@ -30,48 +30,48 @@ func (c *AllowList) CheckBuild(service *types.ServiceConfig) bool {
 func (c *AllowList) CheckBuildArgs(build *types.BuildConfig) {
 	if !c.supported("services.build.args") && len(build.Args) != 0 {
 		build.Args = nil
-		c.Error("services.build.args")
+		c.Unsupported("services.build.args")
 	}
 }
 
 func (c *AllowList) CheckBuildLabels(build *types.BuildConfig) {
 	if !c.supported("services.build.labels") && len(build.Labels) != 0 {
 		build.Labels = nil
-		c.Error("services.build.labels")
+		c.Unsupported("services.build.labels")
 	}
 }
 
 func (c *AllowList) CheckBuildCacheFrom(build *types.BuildConfig) {
 	if !c.supported("services.build.cache_from") && len(build.CacheFrom) != 0 {
 		build.CacheFrom = nil
-		c.Error("services.build.cache_from")
+		c.Unsupported("services.build.cache_from")
 	}
 }
 
 func (c *AllowList) CheckBuildExtraHosts(build *types.BuildConfig) {
 	if !c.supported("services.build.extra_hosts") && len(build.ExtraHosts) != 0 {
 		build.ExtraHosts = nil
-		c.Error("services.build.extra_hosts")
+		c.Unsupported("services.build.extra_hosts")
 	}
 }
 
 func (c *AllowList) CheckBuildIsolation(build *types.BuildConfig) {
 	if !c.supported("services.build.isolation") && build.Isolation != "" {
 		build.Isolation = ""
-		c.Error("services.build.isolation")
+		c.Unsupported("services.build.isolation")
 	}
 }
 
 func (c *AllowList) CheckBuildNetwork(build *types.BuildConfig) {
 	if !c.supported("services.build.network") && build.Network != "" {
 		build.Network = ""
-		c.Error("services.build.network")
+		c.Unsupported("services.build.network")
 	}
 }
 
 func (c *AllowList) CheckBuildTarget(build *types.BuildConfig) {
 	if !c.supported("services.build.target") && build.Target != "" {
 		build.Target = ""
-		c.Error("services.build.target")
+		c.Unsupported("services.build.target")
 	}
 }
diff --git a/compatibility/checker.go b/compatibility/checker.go
index ecc7a49..4068a09 100644
--- a/compatibility/checker.go
+++ b/compatibility/checker.go
@@ -16,7 +16,10 @@
 
 package compatibility
 
-import "github.com/compose-spec/compose-go/types"
+import (
+	"github.com/compose-spec/compose-go/errdefs"
+	"github.com/compose-spec/compose-go/types"
+)
 
 type Checker interface {
 	Errors() []error
@@ -201,6 +204,16 @@ func Check(project *types.Project, c Checker) {
 	}
 }
 
+// IsCompatible return true if the checker didn't reported any incompatibility error
+func IsCompatible(c Checker) bool {
+	for _, err := range c.Errors() {
+		if errdefs.IsIncompatibleError(err) {
+			return false
+		}
+	}
+	return true
+}
+
 func CheckServiceConfig(service *types.ServiceConfig, c Checker) {
 	c.CheckBlkioConfig(service)
 	if service.Build != nil && c.CheckBuild(service) {
diff --git a/compatibility/configs.go b/compatibility/configs.go
index ff64f24..25a114a 100644
--- a/compatibility/configs.go
+++ b/compatibility/configs.go
@@ -26,7 +26,7 @@ func (c *AllowList) CheckFileObjectConfigFile(s string, config *types.FileObject
 	k := fmt.Sprintf("%s.file", s)
 	if !c.supported(k) && config.File != "" {
 		config.File = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -34,7 +34,7 @@ func (c *AllowList) CheckFileObjectConfigExternal(s string, config *types.FileOb
 	k := fmt.Sprintf("%s.external", s)
 	if !c.supported(k) && config.External.External {
 		config.External.External = false
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -42,7 +42,7 @@ func (c *AllowList) CheckFileObjectConfigLabels(s string, config *types.FileObje
 	k := fmt.Sprintf("%s.labels", s)
 	if !c.supported(k) && len(config.Labels) != 0 {
 		config.Labels = nil
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -50,7 +50,7 @@ func (c *AllowList) CheckFileObjectConfigDriver(s string, config *types.FileObje
 	k := fmt.Sprintf("%s.driver", s)
 	if !c.supported(k) && config.Driver != "" {
 		config.Driver = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -58,7 +58,7 @@ func (c *AllowList) CheckFileObjectConfigDriverOpts(s string, config *types.File
 	k := fmt.Sprintf("%s.driver_opts", s)
 	if !c.supported(k) && len(config.DriverOpts) != 0 {
 		config.DriverOpts = nil
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -66,6 +66,6 @@ func (c *AllowList) CheckFileObjectConfigTemplateDriver(s string, config *types.
 	k := fmt.Sprintf("%s.template_driver", s)
 	if !c.supported(k) && config.TemplateDriver != "" {
 		config.TemplateDriver = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
diff --git a/compatibility/deploy.go b/compatibility/deploy.go
index 49d906e..e0fb59f 100644
--- a/compatibility/deploy.go
+++ b/compatibility/deploy.go
@@ -25,7 +25,7 @@ import (
 func (c *AllowList) CheckDeploy(service *types.ServiceConfig) bool {
 	if !c.supported("deploy") && service.Deploy != nil {
 		service.Deploy = nil
-		c.Error("deploy")
+		c.Unsupported("deploy")
 		return false
 	}
 	return true
@@ -34,19 +34,19 @@ func (c *AllowList) CheckDeploy(service *types.ServiceConfig) bool {
 func (c *AllowList) CheckDeployMode(config *types.DeployConfig) {
 	if !c.supported("services.deploy.mode") && config.Mode != "" {
 		config.Mode = ""
-		c.Error("services.deploy.mode")
+		c.Unsupported("services.deploy.mode")
 	}
 }
 func (c *AllowList) CheckDeployReplicas(config *types.DeployConfig) {
 	if !c.supported("services.deploy.replicas") && config.Replicas != nil {
 		config.Replicas = nil
-		c.Error("services.deploy.replicas")
+		c.Unsupported("services.deploy.replicas")
 	}
 }
 func (c *AllowList) CheckDeployLabels(config *types.DeployConfig) {
 	if !c.supported("services.deploy.labels") && len(config.Labels) != 0 {
 		config.Labels = nil
-		c.Error("services.deploy.labels")
+		c.Unsupported("services.deploy.labels")
 	}
 }
 
@@ -58,7 +58,7 @@ const (
 func (c *AllowList) CheckDeployUpdateConfig(config *types.DeployConfig) bool {
 	if !c.supported("services.deploy.update_config") {
 		config.UpdateConfig = nil
-		c.Error("services.deploy.update_config")
+		c.Unsupported("services.deploy.update_config")
 		return false
 	}
 	return true
@@ -67,7 +67,7 @@ func (c *AllowList) CheckDeployUpdateConfig(config *types.DeployConfig) bool {
 func (c *AllowList) CheckDeployRollbackConfig(config *types.DeployConfig) bool {
 	if !c.supported("services.deploy.rollback_config") {
 		config.RollbackConfig = nil
-		c.Error("services.deploy.rollback_config")
+		c.Unsupported("services.deploy.rollback_config")
 		return false
 	}
 	return true
@@ -77,42 +77,42 @@ func (c *AllowList) CheckUpdateConfigParallelism(s string, config *types.UpdateC
 	k := fmt.Sprintf("services.deploy.%s.parallelism", s)
 	if !c.supported(k) && config.Parallelism != nil {
 		config.Parallelism = nil
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 func (c *AllowList) CheckUpdateConfigDelay(s string, config *types.UpdateConfig) {
 	k := fmt.Sprintf("services.deploy.%s.delay", s)
 	if !c.supported(k) && config.Delay != 0 {
 		config.Delay = 0
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 func (c *AllowList) CheckUpdateConfigFailureAction(s string, config *types.UpdateConfig) {
 	k := fmt.Sprintf("services.deploy.%s.failure_action", s)
 	if !c.supported(k) && config.FailureAction != "" {
 		config.FailureAction = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 func (c *AllowList) CheckUpdateConfigMonitor(s string, config *types.UpdateConfig) {
 	k := fmt.Sprintf("services.deploy.%s.monitor", s)
 	if !c.supported(k) && config.Monitor != 0 {
 		config.Monitor = 0
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 func (c *AllowList) CheckUpdateConfigMaxFailureRatio(s string, config *types.UpdateConfig) {
 	k := fmt.Sprintf("services.deploy.%s.max_failure_ratio", s)
 	if !c.supported(k) && config.MaxFailureRatio != 0 {
 		config.MaxFailureRatio = 0
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 func (c *AllowList) CheckUpdateConfigOrder(s string, config *types.UpdateConfig) {
 	k := fmt.Sprintf("services.deploy.%s.order", s)
 	if !c.supported(k) && config.Order != "" {
 		config.Order = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -124,7 +124,7 @@ const (
 func (c *AllowList) CheckDeployResourcesLimits(config *types.DeployConfig) bool {
 	if !c.supported("services.deploy.resources.limits") {
 		config.Resources.Limits = nil
-		c.Error("services.deploy.resources.limits")
+		c.Unsupported("services.deploy.resources.limits")
 		return false
 	}
 	return true
@@ -133,7 +133,7 @@ func (c *AllowList) CheckDeployResourcesLimits(config *types.DeployConfig) bool
 func (c *AllowList) CheckDeployResourcesReservations(config *types.DeployConfig) bool {
 	if !c.supported("services.deploy.resources.reservations") {
 		config.Resources.Reservations = nil
-		c.Error("services.deploy.resources.reservations")
+		c.Unsupported("services.deploy.resources.reservations")
 		return false
 	}
 	return true
@@ -143,28 +143,28 @@ func (c *AllowList) CheckDeployResourcesNanoCPUs(s string, r *types.Resource) {
 	k := fmt.Sprintf("services.deploy.resources.%s.cpus", s)
 	if !c.supported(k) && r.NanoCPUs != "" {
 		r.NanoCPUs = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 func (c *AllowList) CheckDeployResourcesMemoryBytes(s string, r *types.Resource) {
 	k := fmt.Sprintf("services.deploy.resources.%s.memory", s)
 	if !c.supported(k) && r.MemoryBytes != 0 {
 		r.MemoryBytes = 0
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 func (c *AllowList) CheckDeployResourcesGenericResources(s string, r *types.Resource) {
 	k := fmt.Sprintf("services.deploy.resources.%s.generic_resources", s)
 	if !c.supported(k) && len(r.GenericResources) != 0 {
 		r.GenericResources = nil
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
 func (c *AllowList) CheckDeployRestartPolicy(config *types.DeployConfig) bool {
 	if !c.supported("services.deploy.restart_policy") {
 		config.RestartPolicy = nil
-		c.Error("services.deploy.restart_policy")
+		c.Unsupported("services.deploy.restart_policy")
 		return false
 	}
 	return true
@@ -173,52 +173,52 @@ func (c *AllowList) CheckDeployRestartPolicy(config *types.DeployConfig) bool {
 func (c *AllowList) CheckRestartPolicyCondition(p *types.RestartPolicy) {
 	if !c.supported("services.deploy.restart_policy.condition") && p.Condition != "" {
 		p.Condition = ""
-		c.Error("services.deploy.restart_policy.condition")
+		c.Unsupported("services.deploy.restart_policy.condition")
 	}
 }
 func (c *AllowList) CheckRestartPolicyDelay(p *types.RestartPolicy) {
 	if !c.supported("services.deploy.restart_policy.delay") && p.Delay != nil {
 		p.Delay = nil
-		c.Error("services.deploy.restart_policy.delay")
+		c.Unsupported("services.deploy.restart_policy.delay")
 	}
 }
 func (c *AllowList) CheckRestartPolicyMaxAttempts(p *types.RestartPolicy) {
 	if !c.supported("services.deploy.restart_policy.max_attempts") && p.MaxAttempts != nil {
 		p.MaxAttempts = nil
-		c.Error("services.deploy.restart_policy.max_attempts")
+		c.Unsupported("services.deploy.restart_policy.max_attempts")
 	}
 }
 func (c *AllowList) CheckRestartPolicyWindow(p *types.RestartPolicy) {
 	if !c.supported("services.deploy.restart_policy.window") && p.Window != nil {
 		p.Window = nil
-		c.Error("services.deploy.restart_policy.window")
+		c.Unsupported("services.deploy.restart_policy.window")
 	}
 }
 
 func (c *AllowList) CheckPlacementConstraints(p *types.Placement) {
 	if !c.supported("services.deploy.placement", "services.deploy.placement.constraints") && len(p.Constraints) != 0 {
 		p.Constraints = nil
-		c.Error("services.deploy.restart_policy.constraints")
+		c.Unsupported("services.deploy.restart_policy.constraints")
 	}
 }
 
 func (c *AllowList) CheckPlacementPreferences(p *types.Placement) {
 	if !c.supported("services.deploy.placement", "services.deploy.placement.preferences") && p.Preferences != nil {
 		p.Preferences = nil
-		c.Error("services.deploy.restart_policy.preferences")
+		c.Unsupported("services.deploy.restart_policy.preferences")
 	}
 }
 
 func (c *AllowList) CheckPlacementMaxReplicas(p *types.Placement) {
 	if !c.supported("services.deploy.placement", "services.deploy.placement.max_replicas_per_node") && p.MaxReplicas != 0 {
 		p.MaxReplicas = 0
-		c.Error("services.deploy.restart_policy.max_replicas_per_node")
+		c.Unsupported("services.deploy.restart_policy.max_replicas_per_node")
 	}
 }
 
 func (c *AllowList) CheckDeployEndpointMode(config *types.DeployConfig) {
 	if !c.supported("services.deploy.endpoint_mode") && config.EndpointMode != "" {
 		config.EndpointMode = ""
-		c.Error("services.deploy.endpoint_mode")
+		c.Unsupported("services.deploy.endpoint_mode")
 	}
 }
diff --git a/compatibility/networks.go b/compatibility/networks.go
index 7322c34..df87875 100644
--- a/compatibility/networks.go
+++ b/compatibility/networks.go
@@ -31,14 +31,14 @@ func (c *AllowList) CheckNetworkConfig(network *types.NetworkConfig) {
 func (c *AllowList) CheckNetworkConfigDriver(config *types.NetworkConfig) {
 	if !c.supported("networks.driver") && config.Driver != "" {
 		config.Driver = ""
-		c.Error("networks.driver")
+		c.Unsupported("networks.driver")
 	}
 }
 
 func (c *AllowList) CheckNetworkConfigDriverOpts(config *types.NetworkConfig) {
 	if !c.supported("networks.driver_opts") && len(config.DriverOpts) != 0 {
 		config.DriverOpts = nil
-		c.Error("networks.driver_opts")
+		c.Unsupported("networks.driver_opts")
 	}
 }
 
@@ -46,7 +46,7 @@ func (c *AllowList) CheckNetworkConfigIpam(config *types.NetworkConfig) {
 	c.CheckNetworkConfigIpamDriver(&config.Ipam)
 	if len(config.Ipam.Config) != 0 {
 		if !c.supported("networks.ipam.config") {
-			c.Error("networks.ipam.config")
+			c.Unsupported("networks.ipam.config")
 			return
 		}
 		for _, p := range config.Ipam.Config {
@@ -58,14 +58,14 @@ func (c *AllowList) CheckNetworkConfigIpam(config *types.NetworkConfig) {
 func (c *AllowList) CheckNetworkConfigIpamDriver(config *types.IPAMConfig) {
 	if !c.supported("networks.ipam.driver") && config.Driver != "" {
 		config.Driver = ""
-		c.Error("networks.ipam.driver")
+		c.Unsupported("networks.ipam.driver")
 	}
 }
 
 func (c *AllowList) CheckNetworkConfigIpamSubnet(config *types.IPAMPool) {
 	if !c.supported("networks.ipam.config.subnet") && config.Subnet != "" {
 		config.Subnet = ""
-		c.Error("networks.ipam.config.subnet")
+		c.Unsupported("networks.ipam.config.subnet")
 	}
 
 }
@@ -73,27 +73,27 @@ func (c *AllowList) CheckNetworkConfigIpamSubnet(config *types.IPAMPool) {
 func (c *AllowList) CheckNetworkConfigExternal(config *types.NetworkConfig) {
 	if !c.supported("networks.external") && config.External.External {
 		config.External.External = false
-		c.Error("networks.external")
+		c.Unsupported("networks.external")
 	}
 }
 
 func (c *AllowList) CheckNetworkConfigInternal(config *types.NetworkConfig) {
 	if !c.supported("networks.internal") && config.Internal {
 		config.Internal = false
-		c.Error("networks.internal")
+		c.Unsupported("networks.internal")
 	}
 }
 
 func (c *AllowList) CheckNetworkConfigAttachable(config *types.NetworkConfig) {
 	if !c.supported("networks.attachable") && config.Attachable {
 		config.Attachable = false
-		c.Error("networks.attachable")
+		c.Unsupported("networks.attachable")
 	}
 }
 
 func (c *AllowList) CheckNetworkConfigLabels(config *types.NetworkConfig) {
 	if !c.supported("networks.labels") && len(config.Labels) != 0 {
 		config.Labels = nil
-		c.Error("networks.labels")
+		c.Unsupported("networks.labels")
 	}
 }
diff --git a/compatibility/services.go b/compatibility/services.go
index a706308..096a7f3 100644
--- a/compatibility/services.go
+++ b/compatibility/services.go
@@ -25,98 +25,98 @@ import (
 func (c *AllowList) CheckBlkioConfig(service *types.ServiceConfig) {
 	if !c.supported("services.blkio_config") && service.BlkioConfig != "" {
 		service.BlkioConfig = ""
-		c.Error("services.blkio_config")
+		c.Unsupported("services.blkio_config")
 	}
 }
 
 func (c *AllowList) CheckCapAdd(service *types.ServiceConfig) {
 	if !c.supported("services.cap_add") && len(service.CapAdd) != 0 {
 		service.CapAdd = nil
-		c.Error("services.cap_add")
+		c.Unsupported("services.cap_add")
 	}
 }
 
 func (c *AllowList) CheckCapDrop(service *types.ServiceConfig) {
 	if !c.supported("services.cap_drop") && len(service.CapDrop) != 0 {
 		service.CapDrop = nil
-		c.Error("services.cap_drop")
+		c.Unsupported("services.cap_drop")
 	}
 }
 
 func (c *AllowList) CheckCgroupParent(service *types.ServiceConfig) {
 	if !c.supported("services.cgroup_parent") && service.CgroupParent != "" {
 		service.CgroupParent = ""
-		c.Error("services.cgroup_parent")
+		c.Unsupported("services.cgroup_parent")
 	}
 }
 
 func (c *AllowList) CheckCPUQuota(service *types.ServiceConfig) {
 	if !c.supported("services.cpu_quota") && service.CPUQuota != 0 {
 		service.CPUQuota = 0
-		c.Error("services.cpu_quota")
+		c.Unsupported("services.cpu_quota")
 	}
 }
 
 func (c *AllowList) CheckCPUCount(service *types.ServiceConfig) {
 	if !c.supported("services.cpu_count") && service.CPUCount != 0 {
 		service.CPUCount = 0
-		c.Error("services.cpu_count")
+		c.Unsupported("services.cpu_count")
 	}
 }
 
 func (c *AllowList) CheckCPUPercent(service *types.ServiceConfig) {
 	if !c.supported("services.cpu_percent") && service.CPUPercent != 0 {
 		service.CPUPercent = 0
-		c.Error("services.cpu_percent")
+		c.Unsupported("services.cpu_percent")
 	}
 }
 
 func (c *AllowList) CheckCPUPeriod(service *types.ServiceConfig) {
 	if !c.supported("services.cpu_period") && service.CPUPeriod != 0 {
 		service.CPUPeriod = 0
-		c.Error("services.cpu_period")
+		c.Unsupported("services.cpu_period")
 	}
 }
 
 func (c *AllowList) CheckCPURTRuntime(service *types.ServiceConfig) {
 	if !c.supported("services.cpu_rt_runtime") && service.CPURTRuntime != 0 {
 		service.CPURTRuntime = 0
-		c.Error("services.cpu_rt_period")
+		c.Unsupported("services.cpu_rt_period")
 	}
 }
 
 func (c *AllowList) CheckCPURTPeriod(service *types.ServiceConfig) {
 	if !c.supported("services.cpu_rt_period") && service.CPURTPeriod != 0 {
 		service.CPURTPeriod = 0
-		c.Error("services.cpu_rt_period")
+		c.Unsupported("services.cpu_rt_period")
 	}
 }
 
 func (c *AllowList) CheckCPUs(service *types.ServiceConfig) {
 	if !c.supported("services.cpus") && service.CPUS != 0 {
 		service.CPUS = 0
-		c.Error("services.cpus")
+		c.Unsupported("services.cpus")
 	}
 }
 
 func (c *AllowList) CheckCPUSet(service *types.ServiceConfig) {
 	if !c.supported("services.cpuset") && service.CPUSet != "" {
 		service.CPUSet = ""
-		c.Error("services.cpuset")
+		c.Unsupported("services.cpuset")
 	}
 }
 
 func (c *AllowList) CheckCPUShares(service *types.ServiceConfig) {
 	if !c.supported("services.cpu_shares") && service.CPUShares != 0 {
 		service.CPUShares = 0
-		c.Error("services.cpu_shares")
+		c.Unsupported("services.cpu_shares")
 	}
 }
 
 func (c *AllowList) CheckCommand(service *types.ServiceConfig) {
 	if !c.supported("services.command") && len(service.Command) != 0 {
 		service.Command = nil
-		c.Error("services.command")
+		c.Unsupported("services.command")
 	}
 }
 
@@ -124,7 +124,7 @@ func (c *AllowList) CheckConfigs(service *types.ServiceConfig) {
 	if len(service.Configs) != 0 {
 		if !c.supported("services.configs") {
 			service.Configs = nil
-			c.Error("services.configs")
+			c.Unsupported("services.configs")
 			return
 		}
 		for i, s := range service.Secrets {
@@ -138,126 +138,126 @@ func (c *AllowList) CheckConfigs(service *types.ServiceConfig) {
 func (c *AllowList) CheckContainerName(service *types.ServiceConfig) {
 	if !c.supported("services.container_name") && service.ContainerName != "" {
 		service.ContainerName = ""
-		c.Error("services.container_name")
+		c.Unsupported("services.container_name")
 	}
 }
 
 func (c *AllowList) CheckCredentialSpec(service *types.ServiceConfig) {
 	if !c.supported("services.credential_spec") && service.CredentialSpec != nil {
 		service.CredentialSpec = nil
-		c.Error("services.credential_spec")
+		c.Unsupported("services.credential_spec")
 	}
 }
 
 func (c *AllowList) CheckDependsOn(service *types.ServiceConfig) {
 	if !c.supported("services.depends_on") && len(service.DependsOn) != 0 {
 		service.DependsOn = nil
-		c.Error("services.depends_on")
+		c.Unsupported("services.depends_on")
 	}
 }
 
 func (c *AllowList) CheckDevices(service *types.ServiceConfig) {
 	if !c.supported("services.devices") && len(service.Devices) != 0 {
 		service.Devices = nil
-		c.Error("services.devices")
+		c.Unsupported("services.devices")
 	}
 }
 
 func (c *AllowList) CheckDNS(service *types.ServiceConfig) {
 	if !c.supported("services.dns") && service.DNS != nil {
 		service.DNS = nil
-		c.Error("services.dns")
+		c.Unsupported("services.dns")
 	}
 }
 
 func (c *AllowList) CheckDNSOpts(service *types.ServiceConfig) {
 	if !c.supported("services.dns_opt") && len(service.DNSOpts) != 0 {
 		service.DNSOpts = nil
-		c.Error("services.dns_opt")
+		c.Unsupported("services.dns_opt")
 	}
 }
 
 func (c *AllowList) CheckDNSSearch(service *types.ServiceConfig) {
 	if !c.supported("services.dns_search") && len(service.DNSSearch) != 0 {
 		service.DNSSearch = nil
-		c.Error("services.dns_search")
+		c.Unsupported("services.dns_search")
 	}
 }
 
 func (c *AllowList) CheckDomainName(service *types.ServiceConfig) {
 	if !c.supported("services.domainname") && service.DomainName != "" {
 		service.DomainName = ""
-		c.Error("services.domainname")
+		c.Unsupported("services.domainname")
 	}
 }
 
 func (c *AllowList) CheckEntrypoint(service *types.ServiceConfig) {
 	if !c.supported("services.entrypoint") && len(service.Entrypoint) != 0 {
 		service.Entrypoint = nil
-		c.Error("services.entrypoint")
+		c.Unsupported("services.entrypoint")
 	}
 }
 
 func (c *AllowList) CheckEnvironment(service *types.ServiceConfig) {
 	if !c.supported("services.environment") && len(service.Environment) != 0 {
 		service.Environment = nil
-		c.Error("services.environment")
+		c.Unsupported("services.environment")
 	}
 }
 
 func (c *AllowList) CheckEnvFile(service *types.ServiceConfig) {
 	if !c.supported("services.env_file") && len(service.EnvFile) != 0 {
 		service.EnvFile = nil
-		c.Error("services.env_file")
+		c.Unsupported("services.env_file")
 	}
 }
 
 func (c *AllowList) CheckExpose(service *types.ServiceConfig) {
 	if !c.supported("services.expose") && len(service.Expose) != 0 {
 		service.Expose = nil
-		c.Error("services.expose")
+		c.Unsupported("services.expose")
 	}
 }
 
 func (c *AllowList) CheckExtends(service *types.ServiceConfig) {
 	if !c.supported("services.extends") && len(service.Extends) != 0 {
 		service.Extends = nil
-		c.Error("services.extends")
+		c.Unsupported("services.extends")
 	}
 }
 
 func (c *AllowList) CheckExternalLinks(service *types.ServiceConfig) {
 	if !c.supported("services.external_links") && len(service.ExternalLinks) != 0 {
 		service.ExternalLinks = nil
-		c.Error("services.external_links")
+		c.Unsupported("services.external_links")
 	}
 }
 
 func (c *AllowList) CheckExtraHosts(service *types.ServiceConfig) {
 	if !c.supported("services.extra_hosts") && len(service.ExtraHosts) != 0 {
 		service.ExtraHosts = nil
-		c.Error("services.extra_hosts")
+		c.Unsupported("services.extra_hosts")
 	}
 }
 
 func (c *AllowList) CheckGroupAdd(service *types.ServiceConfig) {
 	if !c.supported("services.group_app") && len(service.GroupAdd) != 0 {
 		service.GroupAdd = nil
-		c.Error("services.group_app")
+		c.Unsupported("services.group_app")
 	}
 }
 
 func (c *AllowList) CheckHostname(service *types.ServiceConfig) {
 	if !c.supported("services.hostname") && service.Hostname != "" {
 		service.Hostname = ""
-		c.Error("services.hostname")
+		c.Unsupported("services.hostname")
 	}
 }
 
 func (c *AllowList) CheckHealthCheck(service *types.ServiceConfig) bool {
 	if !c.supported("services.healthcheck") {
 		service.HealthCheck = nil
-		c.Error("services.healthcheck")
+		c.Unsupported("services.healthcheck")
 		return false
 	}
 	return true
@@ -266,77 +266,77 @@ func (c *AllowList) CheckHealthCheck(service *types.ServiceConfig) bool {
 func (c *AllowList) CheckHealthCheckTest(h *types.HealthCheckConfig) {
 	if !c.supported("services.healthcheck.test") && len(h.Test) != 0 {
 		h.Test = nil
-		c.Error("services.healthcheck.test")
+		c.Unsupported("services.healthcheck.test")
 	}
 }
 
 func (c *AllowList) CheckHealthCheckTimeout(h *types.HealthCheckConfig) {
 	if !c.supported("services.healthcheck.timeout") && h.Timeout != nil {
 		h.Timeout = nil
-		c.Error("services.healthcheck.timeout")
+		c.Unsupported("services.healthcheck.timeout")
 	}
 }
 
 func (c *AllowList) CheckHealthCheckInterval(h *types.HealthCheckConfig) {
 	if !c.supported("services.healthcheck.interval") && h.Interval != nil {
 		h.Interval = nil
-		c.Error("services.healthcheck.interval")
+		c.Unsupported("services.healthcheck.interval")
 	}
 }
 
 func (c *AllowList) CheckHealthCheckRetries(h *types.HealthCheckConfig) {
 	if !c.supported("services.healthcheck.retries") && h.Retries != nil {
 		h.Retries = nil
-		c.Error("services.healthcheck.retries")
+		c.Unsupported("services.healthcheck.retries")
 	}
 }
 
 func (c *AllowList) CheckHealthCheckStartPeriod(h *types.HealthCheckConfig) {
 	if !c.supported("services.healthcheck.start_period") && h.StartPeriod != nil {
 		h.StartPeriod = nil
-		c.Error("services.healthcheck.start_period")
+		c.Unsupported("services.healthcheck.start_period")
 	}
 }
 
 func (c *AllowList) CheckInit(service *types.ServiceConfig) {
 	if !c.supported("services.init") && service.Init != nil {
 		service.Init = nil
-		c.Error("services.init")
+		c.Unsupported("services.init")
 	}
 }
 
 func (c *AllowList) CheckIpc(service *types.ServiceConfig) {
 	if !c.supported("services.ipc") && service.Ipc != "" {
 		service.Ipc = ""
-		c.Error("services.ipc")
+		c.Unsupported("services.ipc")
 	}
 }
 
 func (c *AllowList) CheckIsolation(service *types.ServiceConfig) {
 	if !c.supported("services.isolation") && service.Isolation != "" {
 		service.Isolation = ""
-		c.Error("services.isolation")
+		c.Unsupported("services.isolation")
 	}
 }
 
 func (c *AllowList) CheckLabels(service *types.ServiceConfig) {
 	if !c.supported("services.labels") && len(service.Labels) != 0 {
 		service.Labels = nil
-		c.Error("services.labels")
+		c.Unsupported("services.labels")
 	}
 }
 
 func (c *AllowList) CheckLinks(service *types.ServiceConfig) {
 	if !c.supported("services.links") && len(service.Links) != 0 {
 		service.Links = nil
-		c.Error("services.links")
+		c.Unsupported("services.links")
 	}
 }
 
 func (c *AllowList) CheckLogging(service *types.ServiceConfig) bool {
 	if !c.supported("services.logging") {
 		service.Logging = nil
-		c.Error("services.logging")
+		c.Unsupported("services.logging")
 		return false
 	}
 	return true
@@ -345,70 +345,70 @@ func (c *AllowList) CheckLogging(service *types.ServiceConfig) bool {
 func (c *AllowList) CheckLoggingDriver(logging *types.LoggingConfig) {
 	if !c.supported("services.logging.driver") && logging.Driver != "" {
 		logging.Driver = ""
-		c.Error("services.logging.driver")
+		c.Unsupported("services.logging.driver")
 	}
 }
 
 func (c *AllowList) CheckLoggingOptions(logging *types.LoggingConfig) {
 	if !c.supported("services.logging.options") && len(logging.Options) != 0 {
 		logging.Options = nil
-		c.Error("services.logging.options")
+		c.Unsupported("services.logging.options")
 	}
 }
 
 func (c *AllowList) CheckMemLimit(service *types.ServiceConfig) {
 	if !c.supported("services.mem_limit") && service.MemLimit != 0 {
 		service.MemLimit = 0
-		c.Error("services.mem_limit")
+		c.Unsupported("services.mem_limit")
 	}
 }
 
 func (c *AllowList) CheckMemReservation(service *types.ServiceConfig) {
 	if !c.supported("services.mem_reservation") && service.MemReservation != 0 {
 		service.MemReservation = 0
-		c.Error("services.mem_reservation")
+		c.Unsupported("services.mem_reservation")
 	}
 }
 
 func (c *AllowList) CheckMemSwapLimit(service *types.ServiceConfig) {
 	if !c.supported("services.memswap_limit") && service.MemSwapLimit != 0 {
 		service.MemSwapLimit = 0
-		c.Error("services.memswap_limit")
+		c.Unsupported("services.memswap_limit")
 	}
 }
 
 func (c *AllowList) CheckMemSwappiness(service *types.ServiceConfig) {
 	if !c.supported("services.mem_swappiness") && service.MemSwappiness != 0 {
 		service.MemSwappiness = 0
-		c.Error("services.mem_swappiness")
+		c.Unsupported("services.mem_swappiness")
 	}
 }
 
 func (c *AllowList) CheckMacAddress(service *types.ServiceConfig) {
 	if !c.supported("services.mac_address") && service.MacAddress != "" {
 		service.MacAddress = ""
-		c.Error("services.mac_address")
+		c.Unsupported("services.mac_address")
 	}
 }
 
 func (c *AllowList) CheckNet(service *types.ServiceConfig) {
 	if !c.supported("services.net") && service.Net != "" {
 		service.Net = ""
-		c.Error("services.net")
+		c.Unsupported("services.net")
 	}
 }
 
 func (c *AllowList) CheckNetworkMode(service *types.ServiceConfig) {
 	if !c.supported("services.network_mode") && service.NetworkMode != "" {
 		service.NetworkMode = ""
-		c.Error("services.network_mode")
+		c.Unsupported("services.network_mode")
 	}
 }
 
 func (c *AllowList) CheckNetworks(service *types.ServiceConfig) bool {
 	if !c.supported("services.networks") {
 		service.Networks = nil
-		c.Error("services.networks")
+		c.Unsupported("services.networks")
 		return false
 	}
 	return true
@@ -417,63 +417,63 @@ func (c *AllowList) CheckNetworks(service *types.ServiceConfig) bool {
 func (c *AllowList) CheckNetworkAliases(n *types.ServiceNetworkConfig) {
 	if !c.supported("services.networks.aliases") && len(n.Aliases) != 0 {
 		n.Aliases = nil
-		c.Error("services.networks.aliases")
+		c.Unsupported("services.networks.aliases")
 	}
 }
 
 func (c *AllowList) CheckNetworkIpv4Address(n *types.ServiceNetworkConfig) {
 	if !c.supported("services.networks.ipv4_address") && n.Ipv4Address != "" {
 		n.Ipv4Address = ""
-		c.Error("services.networks.ipv4_address")
+		c.Unsupported("services.networks.ipv4_address")
 	}
 }
 
 func (c *AllowList) CheckNetworkIpv6Address(n *types.ServiceNetworkConfig) {
 	if !c.supported("services.networks.ipv6_address") && n.Ipv6Address != "" {
 		n.Ipv6Address = ""
-		c.Error("services.networks.ipv6_address")
+		c.Unsupported("services.networks.ipv6_address")
 	}
 }
 
 func (c *AllowList) CheckOomKillDisable(service *types.ServiceConfig) {
 	if !c.supported("services.oom_kill_disable") && service.OomKillDisable {
 		service.OomKillDisable = false
-		c.Error("services.oom_kill_disable")
+		c.Unsupported("services.oom_kill_disable")
 	}
 }
 
 func (c *AllowList) CheckOomScoreAdj(service *types.ServiceConfig) {
 	if !c.supported("services.oom_score_adj") && service.OomScoreAdj != 0 {
 		service.OomScoreAdj = 0
-		c.Error("services.oom_score_adj")
+		c.Unsupported("services.oom_score_adj")
 	}
 }
 
 func (c *AllowList) CheckPid(service *types.ServiceConfig) {
 	if !c.supported("services.pid") && service.Pid != "" {
 		service.Pid = ""
-		c.Error("services.pid")
+		c.Unsupported("services.pid")
 	}
 }
 
 func (c *AllowList) CheckPidLimit(service *types.ServiceConfig) {
 	if !c.supported("services.pid_limit") && service.PidLimit != 0 {
 		service.PidLimit = 0
-		c.Error("services.pid_limit")
+		c.Unsupported("services.pid_limit")
 	}
 }
 
 func (c *AllowList) CheckPlatform(service *types.ServiceConfig) {
 	if !c.supported("services.platform") && service.Platform != "" {
 		service.Platform = ""
-		c.Error("services.platform")
+		c.Unsupported("services.platform")
 	}
 }
 
 func (c *AllowList) CheckPorts(service *types.ServiceConfig) bool {
 	if !c.supported("services.ports") {
 		service.Ports = nil
-		c.Error("services.ports")
+		c.Unsupported("services.ports")
 		return false
 	}
 	return true
@@ -482,63 +482,63 @@ func (c *AllowList) CheckPorts(service *types.ServiceConfig) bool {
 func (c *AllowList) CheckPortsMode(p *types.ServicePortConfig) {
 	if !c.supported("services.ports.mode") && p.Mode != "" {
 		p.Mode = ""
-		c.Error("services.ports.mode")
+		c.Unsupported("services.ports.mode")
 	}
 }
 
 func (c *AllowList) CheckPortsTarget(p *types.ServicePortConfig) {
 	if !c.supported("services.ports.target") && p.Target != 0 {
 		p.Target = 0
-		c.Error("services.ports.target")
+		c.Unsupported("services.ports.target")
 	}
 }
 
 func (c *AllowList) CheckPortsPublished(p *types.ServicePortConfig) {
 	if !c.supported("services.ports.published") && p.Published != 0 {
 		p.Published = 0
-		c.Error("services.ports.published")
+		c.Unsupported("services.ports.published")
 	}
 }
 
 func (c *AllowList) CheckPortsProtocol(p *types.ServicePortConfig) {
 	if !c.supported("services.ports.protocol") && p.Protocol != "" {
 		p.Protocol = ""
-		c.Error("services.ports.protocol")
+		c.Unsupported("services.ports.protocol")
 	}
 }
 
 func (c *AllowList) CheckPrivileged(service *types.ServiceConfig) {
 	if !c.supported("services.privileged") && service.Privileged {
 		service.Privileged = false
-		c.Error("services.privileged")
+		c.Unsupported("services.privileged")
 	}
 }
 
 func (c *AllowList) CheckReadOnly(service *types.ServiceConfig) {
 	if !c.supported("services.read_only") && service.ReadOnly {
 		service.ReadOnly = false
-		c.Error("services.read_only")
+		c.Unsupported("services.read_only")
 	}
 }
 
 func (c *AllowList) CheckRestart(service *types.ServiceConfig) {
 	if !c.supported("services.restart") && service.Restart != "" {
 		service.Restart = ""
-		c.Error("services.restart")
+		c.Unsupported("services.restart")
 	}
 }
 
 func (c *AllowList) CheckRuntime(service *types.ServiceConfig) {
 	if !c.supported("services.runtime") && service.Runtime != "" {
 		service.Runtime = ""
-		c.Error("services.runtime")
+		c.Unsupported("services.runtime")
 	}
 }
 
 func (c *AllowList) CheckScale(service *types.ServiceConfig) {
 	if !c.supported("services.scale") && service.Scale != 0 {
 		service.Scale = 0
-		c.Error("services.scale")
+		c.Unsupported("services.scale")
 	}
 }
 
@@ -546,7 +546,7 @@ func (c *AllowList) CheckSecrets(service *types.ServiceConfig) {
 	if len(service.Secrets) != 0 {
 		if !c.supported("services.secrets") {
 			service.Secrets = nil
-			c.Error("services.secrets")
+			c.Unsupported("services.secrets")
 		}
 		for i, s := range service.Secrets {
 			ref := types.FileReferenceConfig(s)
@@ -568,7 +568,7 @@ func (c *AllowList) CheckFileReferenceSource(s string, config *types.FileReferen
 	k := fmt.Sprintf("%s.source", s)
 	if !c.supported(k) && config.Source != "" {
 		config.Source = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -576,7 +576,7 @@ func (c *AllowList) CheckFileReferenceTarget(s string, config *types.FileReferen
 	k := fmt.Sprintf("%s.target", s)
 	if !c.supported(k) && config.Target == "" {
 		config.Target = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -584,7 +584,7 @@ func (c *AllowList) CheckFileReferenceUID(s string, config *types.FileReferenceC
 	k := fmt.Sprintf("%s.uid", s)
 	if !c.supported(k) && config.UID != "" {
 		config.UID = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -592,7 +592,7 @@ func (c *AllowList) CheckFileReferenceGID(s string, config *types.FileReferenceC
 	k := fmt.Sprintf("%s.gid", s)
 	if !c.supported(k) && config.GID != "" {
 		config.GID = ""
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
@@ -600,105 +600,105 @@ func (c *AllowList) CheckFileReferenceMode(s string, config *types.FileReference
 	k := fmt.Sprintf("%s.mode", s)
 	if !c.supported(k) && config.Mode != nil {
 		config.Mode = nil
-		c.Error(k)
+		c.Unsupported(k)
 	}
 }
 
 func (c *AllowList) CheckSecurityOpt(service *types.ServiceConfig) {
 	if !c.supported("services.security_opt") && len(service.SecurityOpt) != 0 {
 		service.SecurityOpt = nil
-		c.Error("services.security_opt")
+		c.Unsupported("services.security_opt")
 	}
 }
 
 func (c *AllowList) CheckShmSize(service *types.ServiceConfig) {
 	if !c.supported("services.shm_size") && service.ShmSize != "" {
 		service.ShmSize = ""
-		c.Error("services.shm_size")
+		c.Unsupported("services.shm_size")
 	}
 }
 
 func (c *AllowList) CheckStdinOpen(service *types.ServiceConfig) {
 	if !c.supported("services.stdin_open") && service.StdinOpen {
 		service.StdinOpen = true
-		c.Error("services.stdin_open")
+		c.Unsupported("services.stdin_open")
 	}
 }
 
 func (c *AllowList) CheckStopGracePeriod(service *types.ServiceConfig) {
 	if !c.supported("services.stop_grace_period") && service.StopGracePeriod != nil {
 		service.StopGracePeriod = nil
-		c.Error("services.stop_grace_period")
+		c.Unsupported("services.stop_grace_period")
 	}
 }
 
 func (c *AllowList) CheckStopSignal(service *types.ServiceConfig) {
 	if !c.supported("services.stop_signal") && service.StopSignal != "" {
 		service.StopSignal = ""
-		c.Error("services.stop_signal")
+		c.Unsupported("services.stop_signal")
 	}
 }
 
 func (c *AllowList) CheckSysctls(service *types.ServiceConfig) {
 	if !c.supported("services.sysctls") && len(service.Sysctls) != 0 {
 		service.Sysctls = nil
-		c.Error("services.sysctls")
+		c.Unsupported("services.sysctls")
 	}
 }
 
 func (c *AllowList) CheckTmpfs(service *types.ServiceConfig) {
 	if !c.supported("services.tmpfs") && len(service.Tmpfs) != 0 {
 		service.Tmpfs = nil
-		c.Error("services.tmpfs")
+		c.Unsupported("services.tmpfs")
 	}
 }
 
 func (c *AllowList) CheckTty(service *types.ServiceConfig) {
 	if !c.supported("services.tty") && service.Tty {
 		service.Tty = false
-		c.Error("services.tty")
+		c.Unsupported("services.tty")
 	}
 }
 
 func (c *AllowList) CheckUlimits(service *types.ServiceConfig) {
 	if !c.supported("services.ulimits") && len(service.Ulimits) != 0 {
 		service.Ulimits = nil
-		c.Error("services.ulimits")
+		c.Unsupported("services.ulimits")
 	}
 }
 
 func (c *AllowList) CheckUser(service *types.ServiceConfig) {
 	if !c.supported("services.user") && service.User != "" {
 		service.User = ""
-		c.Error("services.user")
+		c.Unsupported("services.user")
 	}
 }
 
 func (c *AllowList) CheckUserNSMode(service *types.ServiceConfig) {
 	if !c.supported("services.userns_mode") && service.UserNSMode != "" {
 		service.UserNSMode = ""
-		c.Error("services.userns_mode")
+		c.Unsupported("services.userns_mode")
 	}
 }
 
 func (c *AllowList) CheckUts(service *types.ServiceConfig) {
 	if !c.supported("services.build") && service.Uts != "" {
 		service.Uts = ""
-		c.Error("services.uts")
+		c.Unsupported("services.uts")
 	}
 }
 
 func (c *AllowList) CheckVolumeDriver(service *types.ServiceConfig) {
 	if !c.supported("services.volume_driver") && service.VolumeDriver != "" {
 		service.VolumeDriver = ""
-		c.Error("services.volume_driver")
+		c.Unsupported("services.volume_driver")
 	}
 }
 
 func (c *AllowList) CheckServiceVolumes(service *types.ServiceConfig) bool {
 	if !c.supported("services.volumes") {
 		service.Volumes = nil
-		c.Error("services.volumes")
+		c.Unsupported("services.volumes")
 		return false
 	}
 	return true
@@ -707,28 +707,28 @@ func (c *AllowList) CheckServiceVolumes(service *types.ServiceConfig) bool {
 func (c *AllowList) CheckVolumesSource(config *types.ServiceVolumeConfig) {
 	if !c.supported("services.volumes.source") && config.Source != "" {
 		config.Source = ""
-		c.Error("services.volumes.source")
+		c.Unsupported("services.volumes.source")
 	}
 }
 
 func (c *AllowList) CheckVolumesTarget(config *types.ServiceVolumeConfig) {
 	if !c.supported("services.volumes.target") && config.Target != "" {
 		config.Target = ""
-		c.Error("services.volumes.target")
+		c.Unsupported("services.volumes.target")
 	}
 }
 
 func (c *AllowList) CheckVolumesReadOnly(config *types.ServiceVolumeConfig) {
 	if !c.supported("services.volumes.read_only") && config.ReadOnly {
 		config.ReadOnly = false
-		c.Error("services.volumes.read_only")
+		c.Unsupported("services.volumes.read_only")
 	}
 }
 
 func (c *AllowList) CheckVolumesConsistency(config *types.ServiceVolumeConfig) {
 	if !c.supported("services.volumes.consistency") && config.Consistency != "" {
 		config.Consistency = ""
-		c.Error("services.volumes.consistency")
+		c.Unsupported("services.volumes.consistency")
 	}
 }
 
@@ -738,7 +738,7 @@ func (c *AllowList) CheckVolumesBind(config *types.ServiceVolumeBind) {
 	}
 	if !c.supported("services.volumes.bind.propagation") && config.Propagation != "" {
 		config.Propagation = ""
-		c.Error("services.volumes.bind.propagation")
+		c.Unsupported("services.volumes.bind.propagation")
 	}
 }
 
@@ -748,7 +748,7 @@ func (c *AllowList) CheckVolumesVolume(config *types.ServiceVolumeVolume) {
 	}
 	if !c.supported("services.volumes.nocopy") && config.NoCopy {
 		config.NoCopy = false
-		c.Error("services.volumes.nocopy")
+		c.Unsupported("services.volumes.nocopy")
 	}
 }
 
@@ -758,20 +758,20 @@ func (c *AllowList) CheckVolumesTmpfs(config *types.ServiceVolumeTmpfs) {
 	}
 	if !c.supported("services.volumes.tmpfs.size") && config.Size != 0 {
 		config.Size = 0
-		c.Error("services.volumes.tmpfs.size")
+		c.Unsupported("services.volumes.tmpfs.size")
 	}
 }
 
 func (c *AllowList) CheckVolumesFrom(service *types.ServiceConfig) {
 	if !c.supported("services.volumes_from") && len(service.VolumesFrom) != 0 {
 		service.VolumesFrom = nil
-		c.Error("services.volumes_from")
+		c.Unsupported("services.volumes_from")
 	}
 }
 
 func (c *AllowList) CheckWorkingDir(service *types.ServiceConfig) {
 	if !c.supported("services.working_dir") && service.WorkingDir != "" {
 		service.WorkingDir = ""
-		c.Error("services.working_dir")
+		c.Unsupported("services.working_dir")
 	}
 }
diff --git a/compatibility/volumes.go b/compatibility/volumes.go
index 8bf48db..e2b0d0a 100644
--- a/compatibility/volumes.go
+++ b/compatibility/volumes.go
@@ -21,27 +21,27 @@ import "github.com/compose-spec/compose-go/types"
 func (c *AllowList) CheckVolumeConfigDriver(config *types.VolumeConfig) {
 	if !c.supported("volumes.driver") && config.Driver != "" {
 		config.Driver = ""
-		c.Error("volumes.driver")
+		c.Unsupported("volumes.driver")
 	}
 }
 
 func (c *AllowList) CheckVolumeConfigDriverOpts(config *types.VolumeConfig) {
 	if !c.supported("volumes.driver_opts") && len(config.DriverOpts) != 0 {
 		config.DriverOpts = nil
-		c.Error("volumes.driver_opts")
+		c.Unsupported("volumes.driver_opts")
 	}
 }
 
 func (c *AllowList) CheckVolumeConfigExternal(config *types.VolumeConfig) {
 	if !c.supported("volumes.external") && config.External.External {
 		config.External.External = false
-		c.Error("volumes.external")
+		c.Unsupported("volumes.external")
 	}
 }
 
 func (c *AllowList) CheckVolumeConfigLabels(config *types.VolumeConfig) {
 	if !c.supported("volumes.labels") && len(config.Labels) != 0 {
 		config.Labels = nil
-		c.Error("volumes.labels")
+		c.Unsupported("volumes.labels")
 	}
 }
diff --git a/errdefs/errors.go b/errdefs/errors.go
index 04e9ea2..a544070 100644
--- a/errdefs/errors.go
+++ b/errdefs/errors.go
@@ -27,6 +27,9 @@ var (
 
 	// ErrUnsupported is returned when a compose project uses an unsupported attribute
 	ErrUnsupported = errors.New("unsupported attribute")
+
+	// ErrIncompatible is returned when a compose project uses an incompatible attribute
+	ErrIncompatible = errors.New("incompatible attribute")
 )
 
 // IsNotFoundError returns true if the unwrapped error is ErrNotFound
@@ -43,3 +46,8 @@ func IsInvalidError(err error) bool {
 func IsUnsupportedError(err error) bool {
 	return errors.Is(err, ErrUnsupported)
 }
+
+// IsUnsupportedError returns true if the unwrapped error is ErrIncompatible
+func IsIncompatibleError(err error) bool {
+	return errors.Is(err, ErrIncompatible)
+}


Test patch:
diff --git a/compatibility/allowlist_test.go b/compatibility/allowlist_test.go
index c5d36c8..663a184 100644
--- a/compatibility/allowlist_test.go
+++ b/compatibility/allowlist_test.go
@@ -73,6 +73,6 @@ type customChecker struct {
 
 func (c customChecker) CheckNetworkMode(service *types.ServiceConfig) {
 	if service.NetworkMode == "host" {
-		c.Error("services.network_mode=host")
+		c.Unsupported("services.network_mode=host")
 	}
 }