Repository: compose-spec/compose-go
Pull request #323 (2022-11-30 11:36:06)

fix network aliasses merge logic to conform to docker compose v1
closes https://github.com/docker/compose/issues/9981


Patch:
diff --git a/compatibility/allowlist.go b/compatibility/allowlist.go
new file mode 100644
index 0000000..5ed9691
--- /dev/null
+++ b/compatibility/allowlist.go
@@ -0,0 +1,54 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+import (
+	"fmt"
+
+	"github.com/compose-spec/compose-go/errdefs"
+	"github.com/pkg/errors"
+)
+
+// AllowList implements the Checker interface by rejecting all attributes that are not listed as "supported".
+type AllowList struct {
+	Supported []string
+	errors    []error
+}
+
+// Errors returns the list of errors encountered when checking against the allow list
+func (c *AllowList) Errors() []error {
+	return c.errors
+}
+
+func (c *AllowList) supported(attributes ...string) bool {
+	for _, a := range attributes {
+		for _, s := range c.Supported {
+			if s == a {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+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
new file mode 100644
index 0000000..50d606f
--- /dev/null
+++ b/compatibility/build.go
@@ -0,0 +1,77 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+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.Unsupported("services.build")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckBuildArgs(build *types.BuildConfig) {
+	if !c.supported("services.build.args") && len(build.Args) != 0 {
+		build.Args = nil
+		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.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.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.Unsupported("services.build.extra_hosts")
+	}
+}
+
+func (c *AllowList) CheckBuildIsolation(build *types.BuildConfig) {
+	if !c.supported("services.build.isolation") && build.Isolation != "" {
+		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.Unsupported("services.build.network")
+	}
+}
+
+func (c *AllowList) CheckBuildTarget(build *types.BuildConfig) {
+	if !c.supported("services.build.target") && build.Target != "" {
+		build.Target = ""
+		c.Unsupported("services.build.target")
+	}
+}
diff --git a/compatibility/checker.go b/compatibility/checker.go
new file mode 100644
index 0000000..9ababb8
--- /dev/null
+++ b/compatibility/checker.go
@@ -0,0 +1,439 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+import (
+	"github.com/compose-spec/compose-go/errdefs"
+	"github.com/compose-spec/compose-go/types"
+)
+
+type Checker interface {
+	Errors() []error
+	CheckBlkioConfig(build *types.ServiceConfig)
+	CheckBlkioWeight(build *types.BlkioConfig)
+	CheckBlkioWeightDevice(build *types.BlkioConfig)
+	CheckBlkioDeviceReadBps(build *types.BlkioConfig)
+	CheckBlkioDeviceReadIOps(build *types.BlkioConfig)
+	CheckBlkioDeviceWriteBps(build *types.BlkioConfig)
+	CheckBlkioDeviceWriteIOps(build *types.BlkioConfig)
+	CheckBuild(build *types.ServiceConfig) bool
+	CheckBuildArgs(build *types.BuildConfig)
+	CheckBuildLabels(build *types.BuildConfig)
+	CheckBuildCacheFrom(build *types.BuildConfig)
+	CheckBuildExtraHosts(build *types.BuildConfig)
+	CheckBuildIsolation(build *types.BuildConfig)
+	CheckBuildNetwork(build *types.BuildConfig)
+	CheckBuildTarget(build *types.BuildConfig)
+	CheckCapAdd(service *types.ServiceConfig)
+	CheckCapDrop(service *types.ServiceConfig)
+	CheckCgroupParent(service *types.ServiceConfig)
+	CheckCPUCount(service *types.ServiceConfig)
+	CheckCPUPercent(service *types.ServiceConfig)
+	CheckCPUPeriod(service *types.ServiceConfig)
+	CheckCPUQuota(service *types.ServiceConfig)
+	CheckCPURTRuntime(service *types.ServiceConfig)
+	CheckCPURTPeriod(service *types.ServiceConfig)
+	CheckCPUs(service *types.ServiceConfig)
+	CheckCPUSet(service *types.ServiceConfig)
+	CheckCPUShares(service *types.ServiceConfig)
+	CheckCommand(service *types.ServiceConfig)
+	CheckConfigs(service *types.ServiceConfig)
+	CheckContainerName(service *types.ServiceConfig)
+	CheckCredentialSpec(service *types.ServiceConfig)
+	CheckDependsOn(service *types.ServiceConfig)
+	CheckDeviceCgroupRules(service *types.ServiceConfig)
+	CheckDevices(service *types.ServiceConfig)
+	CheckDNS(service *types.ServiceConfig)
+	CheckDNSOpts(service *types.ServiceConfig)
+	CheckDNSSearch(service *types.ServiceConfig)
+	CheckDomainName(service *types.ServiceConfig)
+	CheckEntrypoint(service *types.ServiceConfig)
+	CheckEnvironment(service *types.ServiceConfig)
+	CheckEnvFile(service *types.ServiceConfig)
+	CheckExpose(service *types.ServiceConfig)
+	CheckExtends(service *types.ServiceConfig)
+	CheckExternalLinks(service *types.ServiceConfig)
+	CheckExtraHosts(service *types.ServiceConfig)
+	CheckGroupAdd(service *types.ServiceConfig)
+	CheckHostname(service *types.ServiceConfig)
+	CheckHealthCheckTest(h *types.HealthCheckConfig)
+	CheckHealthCheckTimeout(h *types.HealthCheckConfig)
+	CheckHealthCheckInterval(h *types.HealthCheckConfig)
+	CheckHealthCheckRetries(h *types.HealthCheckConfig)
+	CheckHealthCheckStartPeriod(h *types.HealthCheckConfig)
+	CheckImage(service *types.ServiceConfig)
+	CheckInit(service *types.ServiceConfig)
+	CheckIpc(service *types.ServiceConfig)
+	CheckIsolation(service *types.ServiceConfig)
+	CheckLabels(service *types.ServiceConfig)
+	CheckLinks(service *types.ServiceConfig)
+	CheckLoggingDriver(logging *types.LoggingConfig)
+	CheckLoggingOptions(logging *types.LoggingConfig)
+	CheckMemLimit(service *types.ServiceConfig)
+	CheckMemReservation(service *types.ServiceConfig)
+	CheckMemSwapLimit(service *types.ServiceConfig)
+	CheckMemSwappiness(service *types.ServiceConfig)
+	CheckMacAddress(service *types.ServiceConfig)
+	CheckNet(service *types.ServiceConfig)
+	CheckNetworkMode(service *types.ServiceConfig)
+	CheckNetworkAliases(n *types.ServiceNetworkConfig)
+	CheckNetworkIpv4Address(n *types.ServiceNetworkConfig)
+	CheckNetworkIpv6Address(n *types.ServiceNetworkConfig)
+	CheckOomKillDisable(service *types.ServiceConfig)
+	CheckOomScoreAdj(service *types.ServiceConfig)
+	CheckPid(service *types.ServiceConfig)
+	CheckPidsLimit(service *types.ServiceConfig)
+	CheckPlatform(service *types.ServiceConfig)
+	CheckPortsMode(p *types.ServicePortConfig)
+	CheckPortsTarget(p *types.ServicePortConfig)
+	CheckPortsPublished(p *types.ServicePortConfig)
+	CheckPortsProtocol(p *types.ServicePortConfig)
+	CheckPrivileged(service *types.ServiceConfig)
+	CheckPullPolicy(service *types.ServiceConfig)
+	CheckReadOnly(service *types.ServiceConfig)
+	CheckRestart(service *types.ServiceConfig)
+	CheckRuntime(service *types.ServiceConfig)
+	CheckScale(service *types.ServiceConfig)
+	CheckSecrets(service *types.ServiceConfig)
+	CheckFileReferenceSource(s string, config *types.FileReferenceConfig)
+	CheckFileReferenceTarget(s string, config *types.FileReferenceConfig)
+	CheckFileReferenceUID(s string, config *types.FileReferenceConfig)
+	CheckFileReferenceGID(s string, config *types.FileReferenceConfig)
+	CheckFileReferenceMode(s string, config *types.FileReferenceConfig)
+	CheckSecurityOpt(service *types.ServiceConfig)
+	CheckShmSize(service *types.ServiceConfig)
+	CheckStdinOpen(service *types.ServiceConfig)
+	CheckStopGracePeriod(service *types.ServiceConfig)
+	CheckStopSignal(service *types.ServiceConfig)
+	CheckSysctls(service *types.ServiceConfig)
+	CheckTmpfs(service *types.ServiceConfig)
+	CheckTty(service *types.ServiceConfig)
+	CheckUlimits(service *types.ServiceConfig)
+	CheckUser(service *types.ServiceConfig)
+	CheckUserNSMode(service *types.ServiceConfig)
+	CheckUts(service *types.ServiceConfig)
+	CheckVolumeDriver(service *types.ServiceConfig)
+	CheckVolumesSource(config *types.ServiceVolumeConfig)
+	CheckVolumesTarget(config *types.ServiceVolumeConfig)
+	CheckVolumesReadOnly(config *types.ServiceVolumeConfig)
+	CheckVolumesConsistency(config *types.ServiceVolumeConfig)
+	CheckVolumesBind(config *types.ServiceVolumeBind)
+	CheckVolumesVolume(config *types.ServiceVolumeVolume)
+	CheckVolumesTmpfs(config *types.ServiceVolumeTmpfs)
+	CheckVolumesFrom(service *types.ServiceConfig)
+	CheckWorkingDir(service *types.ServiceConfig)
+	CheckVolumeConfigDriver(config *types.VolumeConfig)
+	CheckVolumeConfigDriverOpts(config *types.VolumeConfig)
+	CheckVolumeConfigExternal(config *types.VolumeConfig)
+	CheckVolumeConfigLabels(config *types.VolumeConfig)
+	CheckFileObjectConfigFile(s string, config *types.FileObjectConfig)
+	CheckFileObjectConfigExternal(s string, config *types.FileObjectConfig)
+	CheckFileObjectConfigLabels(s string, config *types.FileObjectConfig)
+	CheckFileObjectConfigDriver(s string, config *types.FileObjectConfig)
+	CheckFileObjectConfigDriverOpts(s string, config *types.FileObjectConfig)
+	CheckFileObjectConfigTemplateDriver(s string, config *types.FileObjectConfig)
+	CheckDeploy(deploy *types.ServiceConfig) bool
+	CheckDeployEndpointMode(deploy *types.DeployConfig)
+	CheckDeployLabels(deploy *types.DeployConfig)
+	CheckDeployMode(deploy *types.DeployConfig)
+	CheckDeployReplicas(deploy *types.DeployConfig)
+	CheckDeployRestartPolicy(deploy *types.DeployConfig) bool
+	CheckDeployRollbackConfig(deploy *types.DeployConfig) bool
+	CheckDeployUpdateConfig(deploy *types.DeployConfig) bool
+	CheckPlacementConstraints(p *types.Placement)
+	CheckPlacementMaxReplicas(p *types.Placement)
+	CheckPlacementPreferences(p *types.Placement)
+	CheckRestartPolicyDelay(policy *types.RestartPolicy)
+	CheckRestartPolicyCondition(policy *types.RestartPolicy)
+	CheckRestartPolicyMaxAttempts(policy *types.RestartPolicy)
+	CheckRestartPolicyWindow(policy *types.RestartPolicy)
+	CheckUpdateConfigDelay(rollback string, config *types.UpdateConfig)
+	CheckUpdateConfigFailureAction(rollback string, config *types.UpdateConfig)
+	CheckUpdateConfigMaxFailureRatio(rollback string, config *types.UpdateConfig)
+	CheckUpdateConfigMonitor(rollback string, config *types.UpdateConfig)
+	CheckUpdateConfigOrder(rollback string, config *types.UpdateConfig)
+	CheckUpdateConfigParallelism(rollback string, config *types.UpdateConfig)
+	CheckDeployResourcesNanoCPUs(s string, resource *types.Resource)
+	CheckDeployResourcesMemoryBytes(s string, resource *types.Resource)
+	CheckDeployResourcesDevices(s string, resource *types.Resource)
+	CheckDeployResourcesDevicesCapabilities(s string, r types.DeviceRequest)
+	CheckDeployResourcesDevicesCount(s string, r types.DeviceRequest)
+	CheckDeployResourcesDevicesIDs(s string, r types.DeviceRequest)
+	CheckDeployResourcesDevicesDriver(s string, r types.DeviceRequest)
+	CheckDeployResourcesGenericResources(s string, resource *types.Resource)
+	CheckDeployResourcesLimits(deploy *types.DeployConfig) bool
+	CheckDeployResourcesReservations(deploy *types.DeployConfig) bool
+	CheckHealthCheck(service *types.ServiceConfig) bool
+	CheckLogging(service *types.ServiceConfig) bool
+	CheckNetworks(service *types.ServiceConfig) bool
+	CheckPorts(service *types.ServiceConfig) bool
+	CheckServiceVolumes(service *types.ServiceConfig) bool
+	CheckNetworkConfigIpam(network *types.NetworkConfig)
+	CheckNetworkConfigIpamSubnet(config *types.IPAMPool)
+	CheckNetworkConfigIpamGateway(config *types.IPAMPool)
+	CheckNetworkConfigIpamIPRange(config *types.IPAMPool)
+	CheckNetworkConfigIpamAuxiliaryAddresses(config *types.IPAMPool)
+	CheckNetworkConfigDriver(network *types.NetworkConfig)
+	CheckNetworkConfigDriverOpts(network *types.NetworkConfig)
+	CheckNetworkConfigExternal(network *types.NetworkConfig)
+	CheckNetworkConfigInternal(network *types.NetworkConfig)
+	CheckNetworkConfigAttachable(network *types.NetworkConfig)
+	CheckNetworkConfigLabels(network *types.NetworkConfig)
+}
+
+func Check(project *types.Project, c Checker) {
+	for i, service := range project.Services {
+		CheckServiceConfig(&service, c)
+		project.Services[i] = service
+	}
+
+	for i, network := range project.Networks {
+		CheckNetworkConfig(&network, c)
+		project.Networks[i] = network
+	}
+
+	for i, volume := range project.Volumes {
+		CheckVolumeConfig(&volume, c)
+		project.Volumes[i] = volume
+	}
+
+	for i, config := range project.Configs {
+		CheckConfigsConfig(&config, c)
+		project.Configs[i] = config
+	}
+
+	for i, secret := range project.Secrets {
+		CheckSecretsConfig(&secret, c)
+		project.Secrets[i] = secret
+	}
+}
+
+// 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) {
+		c.CheckBuildArgs(service.Build)
+		c.CheckBuildLabels(service.Build)
+		c.CheckBuildCacheFrom(service.Build)
+		c.CheckBuildNetwork(service.Build)
+		c.CheckBuildTarget(service.Build)
+	}
+	c.CheckCapAdd(service)
+	c.CheckCapDrop(service)
+	c.CheckCgroupParent(service)
+	c.CheckCPUCount(service)
+	c.CheckCPUPercent(service)
+	c.CheckCPUPeriod(service)
+	c.CheckCPUQuota(service)
+	c.CheckCPURTPeriod(service)
+	c.CheckCPURTRuntime(service)
+	c.CheckCPUs(service)
+	c.CheckCPUSet(service)
+	c.CheckCPUShares(service)
+	c.CheckCommand(service)
+	c.CheckConfigs(service)
+	c.CheckContainerName(service)
+	c.CheckCredentialSpec(service)
+	c.CheckDependsOn(service)
+	if service.Deploy != nil && c.CheckDeploy(service) {
+		c.CheckDeployEndpointMode(service.Deploy)
+		c.CheckDeployLabels(service.Deploy)
+		c.CheckDeployMode(service.Deploy)
+		c.CheckPlacementConstraints(&service.Deploy.Placement)
+		c.CheckPlacementMaxReplicas(&service.Deploy.Placement)
+		c.CheckPlacementPreferences(&service.Deploy.Placement)
+		c.CheckDeployReplicas(service.Deploy)
+		if service.Deploy.Resources.Limits != nil && c.CheckDeployResourcesLimits(service.Deploy) {
+			c.CheckDeployResourcesNanoCPUs(ResourceLimits, service.Deploy.Resources.Limits)
+			c.CheckDeployResourcesMemoryBytes(ResourceLimits, service.Deploy.Resources.Limits)
+			c.CheckDeployResourcesGenericResources(ResourceLimits, service.Deploy.Resources.Limits)
+		}
+		if service.Deploy.Resources.Reservations != nil && c.CheckDeployResourcesReservations(service.Deploy) {
+			c.CheckDeployResourcesNanoCPUs(ResourceReservations, service.Deploy.Resources.Reservations)
+			c.CheckDeployResourcesMemoryBytes(ResourceReservations, service.Deploy.Resources.Reservations)
+			c.CheckDeployResourcesGenericResources(ResourceReservations, service.Deploy.Resources.Reservations)
+			c.CheckDeployResourcesDevices(ResourceReservations, service.Deploy.Resources.Reservations)
+		}
+		if service.Deploy.RestartPolicy != nil && c.CheckDeployRestartPolicy(service.Deploy) {
+			c.CheckRestartPolicyCondition(service.Deploy.RestartPolicy)
+			c.CheckRestartPolicyDelay(service.Deploy.RestartPolicy)
+			c.CheckRestartPolicyMaxAttempts(service.Deploy.RestartPolicy)
+			c.CheckRestartPolicyWindow(service.Deploy.RestartPolicy)
+		}
+		if service.Deploy.UpdateConfig != nil && c.CheckDeployUpdateConfig(service.Deploy) {
+			c.CheckUpdateConfigDelay(UpdateConfigUpdate, service.Deploy.UpdateConfig)
+			c.CheckUpdateConfigFailureAction(UpdateConfigUpdate, service.Deploy.UpdateConfig)
+			c.CheckUpdateConfigMaxFailureRatio(UpdateConfigUpdate, service.Deploy.UpdateConfig)
+			c.CheckUpdateConfigMonitor(UpdateConfigUpdate, service.Deploy.UpdateConfig)
+			c.CheckUpdateConfigOrder(UpdateConfigUpdate, service.Deploy.UpdateConfig)
+			c.CheckUpdateConfigParallelism(UpdateConfigUpdate, service.Deploy.UpdateConfig)
+		}
+		if service.Deploy.RollbackConfig != nil && c.CheckDeployRollbackConfig(service.Deploy) {
+			c.CheckUpdateConfigDelay(UpdateConfigRollback, service.Deploy.RollbackConfig)
+			c.CheckUpdateConfigFailureAction(UpdateConfigRollback, service.Deploy.RollbackConfig)
+			c.CheckUpdateConfigMaxFailureRatio(UpdateConfigRollback, service.Deploy.RollbackConfig)
+			c.CheckUpdateConfigMonitor(UpdateConfigRollback, service.Deploy.RollbackConfig)
+			c.CheckUpdateConfigOrder(UpdateConfigRollback, service.Deploy.RollbackConfig)
+			c.CheckUpdateConfigParallelism(UpdateConfigRollback, service.Deploy.RollbackConfig)
+		}
+	}
+	c.CheckDeviceCgroupRules(service)
+	c.CheckDevices(service)
+	c.CheckDNS(service)
+	c.CheckDNSOpts(service)
+	c.CheckDNSSearch(service)
+	c.CheckDomainName(service)
+	c.CheckEntrypoint(service)
+	c.CheckEnvironment(service)
+	c.CheckEnvFile(service)
+	c.CheckExpose(service)
+	c.CheckExtends(service)
+	c.CheckExternalLinks(service)
+	c.CheckExtraHosts(service)
+	c.CheckGroupAdd(service)
+	c.CheckHostname(service)
+	if service.HealthCheck != nil && c.CheckHealthCheck(service) {
+		c.CheckHealthCheckInterval(service.HealthCheck)
+		c.CheckHealthCheckRetries(service.HealthCheck)
+		c.CheckHealthCheckStartPeriod(service.HealthCheck)
+		c.CheckHealthCheckTest(service.HealthCheck)
+		c.CheckHealthCheckTimeout(service.HealthCheck)
+	}
+	c.CheckImage(service)
+	c.CheckInit(service)
+	c.CheckIpc(service)
+	c.CheckIsolation(service)
+	c.CheckLabels(service)
+	c.CheckLinks(service)
+	if service.Logging != nil && c.CheckLogging(service) {
+		c.CheckLoggingDriver(service.Logging)
+		c.CheckLoggingOptions(service.Logging)
+	}
+	c.CheckMemLimit(service)
+	c.CheckMemReservation(service)
+	c.CheckMemSwapLimit(service)
+	c.CheckMemSwappiness(service)
+	c.CheckMacAddress(service)
+	c.CheckNet(service)
+	c.CheckNetworkMode(service)
+	if len(service.Networks) > 0 && c.CheckNetworks(service) {
+		for _, n := range service.Networks {
+			if n != nil {
+				c.CheckNetworkAliases(n)
+				c.CheckNetworkIpv4Address(n)
+				c.CheckNetworkIpv6Address(n)
+			}
+		}
+	}
+	c.CheckOomKillDisable(service)
+	c.CheckOomScoreAdj(service)
+	c.CheckPid(service)
+	c.CheckPidsLimit(service)
+	c.CheckPlatform(service)
+	if len(service.Ports) > 0 && c.CheckPorts(service) {
+		for i, p := range service.Ports {
+			c.CheckPortsMode(&p)
+			c.CheckPortsTarget(&p)
+			c.CheckPortsProtocol(&p)
+			c.CheckPortsPublished(&p)
+			service.Ports[i] = p
+		}
+	}
+	c.CheckPrivileged(service)
+	c.CheckPullPolicy(service)
+	c.CheckReadOnly(service)
+	c.CheckRestart(service)
+	c.CheckRuntime(service)
+	c.CheckScale(service)
+	c.CheckSecrets(service)
+	c.CheckSecurityOpt(service)
+	c.CheckShmSize(service)
+	c.CheckStdinOpen(service)
+	c.CheckStopGracePeriod(service)
+	c.CheckStopSignal(service)
+	c.CheckSysctls(service)
+	c.CheckTmpfs(service)
+	c.CheckTty(service)
+	c.CheckUlimits(service)
+	c.CheckUser(service)
+	c.CheckUserNSMode(service)
+	c.CheckUts(service)
+	c.CheckVolumeDriver(service)
+	if len(service.Volumes) > 0 && c.CheckServiceVolumes(service) {
+		for i, v := range service.Volumes {
+			c.CheckVolumesSource(&v)
+			c.CheckVolumesTarget(&v)
+			c.CheckVolumesReadOnly(&v)
+			switch v.Type {
+			case types.VolumeTypeBind:
+				c.CheckVolumesBind(v.Bind)
+			case types.VolumeTypeVolume:
+				c.CheckVolumesVolume(v.Volume)
+			case types.VolumeTypeTmpfs:
+				c.CheckVolumesTmpfs(v.Tmpfs)
+			}
+			service.Volumes[i] = v
+		}
+	}
+	c.CheckVolumesFrom(service)
+	c.CheckWorkingDir(service)
+}
+
+func CheckNetworkConfig(network *types.NetworkConfig, c Checker) {
+	c.CheckNetworkConfigDriver(network)
+	c.CheckNetworkConfigDriverOpts(network)
+	c.CheckNetworkConfigIpam(network)
+	c.CheckNetworkConfigExternal(network)
+	c.CheckNetworkConfigInternal(network)
+	c.CheckNetworkConfigAttachable(network)
+	c.CheckNetworkConfigLabels(network)
+}
+
+func CheckVolumeConfig(config *types.VolumeConfig, c Checker) {
+	c.CheckVolumeConfigDriver(config)
+	c.CheckVolumeConfigDriverOpts(config)
+	c.CheckVolumeConfigExternal(config)
+	c.CheckVolumeConfigLabels(config)
+}
+
+func CheckConfigsConfig(config *types.ConfigObjConfig, c Checker) {
+	ref := types.FileObjectConfig(*config)
+	CheckFileObjectConfig("configs", &ref, c)
+}
+
+func CheckSecretsConfig(config *types.SecretConfig, c Checker) {
+	ref := types.FileObjectConfig(*config)
+	CheckFileObjectConfig("secrets", &ref, c)
+}
+
+func CheckFileObjectConfig(s string, config *types.FileObjectConfig, c Checker) {
+	c.CheckFileObjectConfigDriver(s, config)
+	c.CheckFileObjectConfigDriverOpts(s, config)
+	c.CheckFileObjectConfigExternal(s, config)
+	c.CheckFileObjectConfigFile(s, config)
+	c.CheckFileObjectConfigLabels(s, config)
+	c.CheckFileObjectConfigTemplateDriver(s, config)
+}
diff --git a/compatibility/configs.go b/compatibility/configs.go
new file mode 100644
index 0000000..25a114a
--- /dev/null
+++ b/compatibility/configs.go
@@ -0,0 +1,71 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+import (
+	"fmt"
+
+	"github.com/compose-spec/compose-go/types"
+)
+
+func (c *AllowList) CheckFileObjectConfigFile(s string, config *types.FileObjectConfig) {
+	k := fmt.Sprintf("%s.file", s)
+	if !c.supported(k) && config.File != "" {
+		config.File = ""
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileObjectConfigExternal(s string, config *types.FileObjectConfig) {
+	k := fmt.Sprintf("%s.external", s)
+	if !c.supported(k) && config.External.External {
+		config.External.External = false
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileObjectConfigLabels(s string, config *types.FileObjectConfig) {
+	k := fmt.Sprintf("%s.labels", s)
+	if !c.supported(k) && len(config.Labels) != 0 {
+		config.Labels = nil
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileObjectConfigDriver(s string, config *types.FileObjectConfig) {
+	k := fmt.Sprintf("%s.driver", s)
+	if !c.supported(k) && config.Driver != "" {
+		config.Driver = ""
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileObjectConfigDriverOpts(s string, config *types.FileObjectConfig) {
+	k := fmt.Sprintf("%s.driver_opts", s)
+	if !c.supported(k) && len(config.DriverOpts) != 0 {
+		config.DriverOpts = nil
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileObjectConfigTemplateDriver(s string, config *types.FileObjectConfig) {
+	k := fmt.Sprintf("%s.template_driver", s)
+	if !c.supported(k) && config.TemplateDriver != "" {
+		config.TemplateDriver = ""
+		c.Unsupported(k)
+	}
+}
diff --git a/compatibility/deploy.go b/compatibility/deploy.go
new file mode 100644
index 0000000..dc7abd4
--- /dev/null
+++ b/compatibility/deploy.go
@@ -0,0 +1,275 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+import (
+	"fmt"
+
+	"github.com/compose-spec/compose-go/types"
+)
+
+func (c *AllowList) CheckDeploy(service *types.ServiceConfig) bool {
+	if !c.supported("services.deploy") && service.Deploy != nil {
+		service.Deploy = nil
+		c.Unsupported("services.deploy")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckDeployMode(config *types.DeployConfig) {
+	if !c.supported("services.deploy.mode") && config.Mode != "" {
+		config.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.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.Unsupported("services.deploy.labels")
+	}
+}
+
+const (
+	UpdateConfigUpdate   = "update_config"
+	UpdateConfigRollback = "rolback_config"
+)
+
+func (c *AllowList) CheckDeployUpdateConfig(config *types.DeployConfig) bool {
+	if !c.supported("services.deploy.update_config") {
+		config.UpdateConfig = nil
+		c.Unsupported("services.deploy.update_config")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckDeployRollbackConfig(config *types.DeployConfig) bool {
+	if !c.supported("services.deploy.rollback_config") {
+		config.RollbackConfig = nil
+		c.Unsupported("services.deploy.rollback_config")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckUpdateConfigParallelism(s string, config *types.UpdateConfig) {
+	k := fmt.Sprintf("services.deploy.%s.parallelism", s)
+	if !c.supported(k) && config.Parallelism != nil {
+		config.Parallelism = nil
+		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.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.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.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.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.Unsupported(k)
+	}
+}
+
+const (
+	ResourceLimits       = "limits"
+	ResourceReservations = "reservations"
+)
+
+func (c *AllowList) CheckDeployResourcesLimits(config *types.DeployConfig) bool {
+	if !c.supported("services.deploy.resources.limits") {
+		config.Resources.Limits = nil
+		c.Unsupported("services.deploy.resources.limits")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckDeployResourcesReservations(config *types.DeployConfig) bool {
+	if !c.supported("services.deploy.resources.reservations") {
+		config.Resources.Reservations = nil
+		c.Unsupported("services.deploy.resources.reservations")
+		return false
+	}
+	return true
+}
+
+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.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.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckDeployResourcesDevices(s string, r *types.Resource) {
+	if len(r.Devices) == 0 {
+		return
+	}
+	k := fmt.Sprintf("services.deploy.resources.%s.devices", s)
+	if !c.supported(k) {
+		r.Devices = nil
+		c.Unsupported(k)
+		return
+	}
+	for _, d := range r.Devices {
+		c.CheckDeployResourcesDevicesCapabilities(s, d)
+		c.CheckDeployResourcesDevicesCount(s, d)
+		c.CheckDeployResourcesDevicesIDs(s, d)
+		c.CheckDeployResourcesDevicesDriver(s, d)
+	}
+}
+
+func (c *AllowList) CheckDeployResourcesDevicesCapabilities(s string, r types.DeviceRequest) {
+	k := fmt.Sprintf("services.deploy.resources.%s.devices.capabilities", s)
+	if !c.supported(k) && len(r.Capabilities) != 0 {
+		r.Capabilities = nil
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckDeployResourcesDevicesCount(s string, r types.DeviceRequest) {
+	k := fmt.Sprintf("services.deploy.resources.%s.devices.count", s)
+	if !c.supported(k) && r.Count != 0 {
+		r.Count = 0
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckDeployResourcesDevicesIDs(s string, r types.DeviceRequest) {
+	k := fmt.Sprintf("services.deploy.resources.%s.devices.device_ids", s)
+	if !c.supported(k) && len(r.IDs) != 0 {
+		r.IDs = nil
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckDeployResourcesDevicesDriver(s string, r types.DeviceRequest) {
+	k := fmt.Sprintf("services.deploy.resources.%s.devices.driver", s)
+	if !c.supported(k) && r.Driver != "" {
+		r.Driver = ""
+		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.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckDeployRestartPolicy(config *types.DeployConfig) bool {
+	if !c.supported("services.deploy.restart_policy") {
+		config.RestartPolicy = nil
+		c.Unsupported("services.deploy.restart_policy")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckRestartPolicyCondition(p *types.RestartPolicy) {
+	if !c.supported("services.deploy.restart_policy.condition") && p.Condition != "" {
+		p.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.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.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.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.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.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.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.Unsupported("services.deploy.endpoint_mode")
+	}
+}
diff --git a/compatibility/networks.go b/compatibility/networks.go
new file mode 100644
index 0000000..4701693
--- /dev/null
+++ b/compatibility/networks.go
@@ -0,0 +1,122 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+import "github.com/compose-spec/compose-go/types"
+
+func (c *AllowList) CheckNetworkConfig(network *types.NetworkConfig) {
+	c.CheckNetworkConfigDriver(network)
+	c.CheckNetworkConfigDriverOpts(network)
+	c.CheckNetworkConfigIpam(network)
+	c.CheckNetworkConfigExternal(network)
+	c.CheckNetworkConfigInternal(network)
+	c.CheckNetworkConfigAttachable(network)
+	c.CheckNetworkConfigLabels(network)
+}
+
+func (c *AllowList) CheckNetworkConfigDriver(config *types.NetworkConfig) {
+	if !c.supported("networks.driver") && config.Driver != "" {
+		config.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.Unsupported("networks.driver_opts")
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigIpam(config *types.NetworkConfig) {
+	c.CheckNetworkConfigIpamDriver(&config.Ipam)
+	if len(config.Ipam.Config) != 0 {
+		if !c.supported("networks.ipam.config") {
+			c.Unsupported("networks.ipam.config")
+			return
+		}
+		for _, p := range config.Ipam.Config {
+			c.CheckNetworkConfigIpamSubnet(p)
+			c.CheckNetworkConfigIpamGateway(p)
+			c.CheckNetworkConfigIpamIPRange(p)
+			c.CheckNetworkConfigIpamAuxiliaryAddresses(p)
+		}
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigIpamDriver(config *types.IPAMConfig) {
+	if !c.supported("networks.ipam.driver") && config.Driver != "" {
+		config.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.Unsupported("networks.ipam.config.subnet")
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigIpamGateway(config *types.IPAMPool) {
+	if !c.supported("networks.ipam.config.gateway") && config.Gateway != "" {
+		config.Gateway = ""
+		c.Unsupported("networks.ipam.config.gateway")
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigIpamIPRange(config *types.IPAMPool) {
+	if !c.supported("networks.ipam.config.ip_range") && config.IPRange != "" {
+		config.IPRange = ""
+		c.Unsupported("networks.ipam.config.ip_range")
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigIpamAuxiliaryAddresses(config *types.IPAMPool) {
+	if !c.supported("networks.ipam.config.aux_addresses") && len(config.AuxiliaryAddresses) > 0 {
+		config.AuxiliaryAddresses = nil
+		c.Unsupported("networks.ipam.config.aux_addresses")
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigExternal(config *types.NetworkConfig) {
+	if !c.supported("networks.external") && config.External.External {
+		config.External.External = false
+		c.Unsupported("networks.external")
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigInternal(config *types.NetworkConfig) {
+	if !c.supported("networks.internal") && config.Internal {
+		config.Internal = false
+		c.Unsupported("networks.internal")
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigAttachable(config *types.NetworkConfig) {
+	if !c.supported("networks.attachable") && config.Attachable {
+		config.Attachable = false
+		c.Unsupported("networks.attachable")
+	}
+}
+
+func (c *AllowList) CheckNetworkConfigLabels(config *types.NetworkConfig) {
+	if !c.supported("networks.labels") && len(config.Labels) != 0 {
+		config.Labels = nil
+		c.Unsupported("networks.labels")
+	}
+}
diff --git a/compatibility/services.go b/compatibility/services.go
new file mode 100644
index 0000000..1e652f2
--- /dev/null
+++ b/compatibility/services.go
@@ -0,0 +1,840 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+import (
+	"fmt"
+
+	"github.com/compose-spec/compose-go/types"
+)
+
+func (c *AllowList) CheckBlkioConfig(service *types.ServiceConfig) {
+	if !c.supported("services.blkio_config") && service.BlkioConfig != nil {
+		service.BlkioConfig = nil
+		c.Unsupported("services.blkio_config")
+	}
+}
+
+func (c *AllowList) CheckBlkioWeight(config *types.BlkioConfig) {
+	if !c.supported("services.blkio_config.weight") && config.Weight != 0 {
+		config.Weight = 0
+		c.Unsupported("services.blkio_config.weight")
+	}
+}
+
+func (c *AllowList) CheckBlkioWeightDevice(config *types.BlkioConfig) {
+	if !c.supported("services.blkio_config.weight_device") && len(config.WeightDevice) != 0 {
+		config.WeightDevice = nil
+		c.Unsupported("services.blkio_config.weight_device")
+	}
+}
+
+func (c *AllowList) CheckBlkioDeviceReadBps(config *types.BlkioConfig) {
+	if !c.supported("services.blkio_config.device_read_bps") && len(config.DeviceWriteBps) != 0 {
+		config.DeviceWriteBps = nil
+		c.Unsupported("services.blkio_config.device_read_bps")
+	}
+}
+
+func (c *AllowList) CheckBlkioDeviceReadIOps(config *types.BlkioConfig) {
+	if !c.supported("services.blkio_config.device_read_iops") && len(config.DeviceReadIOps) != 0 {
+		config.DeviceReadIOps = nil
+		c.Unsupported("services.blkio_config.device_read_iops")
+	}
+}
+
+func (c *AllowList) CheckBlkioDeviceWriteBps(config *types.BlkioConfig) {
+	if !c.supported("services.blkio_config.device_write_bps") && len(config.DeviceWriteBps) != 0 {
+		config.DeviceWriteBps = nil
+		c.Unsupported("services.blkio_config.device_write_bps")
+	}
+}
+
+func (c *AllowList) CheckBlkioDeviceWriteIOps(config *types.BlkioConfig) {
+	if !c.supported("services.blkio_config.device_write_iops") && len(config.DeviceWriteIOps) != 0 {
+		config.DeviceWriteIOps = nil
+		c.Unsupported("services.blkio_config.device_write_iops")
+	}
+}
+
+func (c *AllowList) CheckCapAdd(service *types.ServiceConfig) {
+	if !c.supported("services.cap_add") && len(service.CapAdd) != 0 {
+		service.CapAdd = nil
+		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.Unsupported("services.cap_drop")
+	}
+}
+
+func (c *AllowList) CheckCgroupParent(service *types.ServiceConfig) {
+	if !c.supported("services.cgroup_parent") && service.CgroupParent != "" {
+		service.CgroupParent = ""
+		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.Unsupported("services.cpu_quota")
+	}
+}
+
+func (c *AllowList) CheckCPUCount(service *types.ServiceConfig) {
+	if !c.supported("services.cpu_count") && service.CPUCount != 0 {
+		service.CPUCount = 0
+		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.Unsupported("services.cpu_percent")
+	}
+}
+
+func (c *AllowList) CheckCPUPeriod(service *types.ServiceConfig) {
+	if !c.supported("services.cpu_period") && service.CPUPeriod != 0 {
+		service.CPUPeriod = 0
+		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.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.Unsupported("services.cpu_rt_period")
+	}
+}
+
+func (c *AllowList) CheckCPUs(service *types.ServiceConfig) {
+	if !c.supported("services.cpus") && service.CPUS != 0 {
+		service.CPUS = 0
+		c.Unsupported("services.cpus")
+	}
+}
+
+func (c *AllowList) CheckCPUSet(service *types.ServiceConfig) {
+	if !c.supported("services.cpuset") && service.CPUSet != "" {
+		service.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.Unsupported("services.cpu_shares")
+	}
+}
+
+func (c *AllowList) CheckCommand(service *types.ServiceConfig) {
+	if !c.supported("services.command") && len(service.Command) != 0 {
+		service.Command = nil
+		c.Unsupported("services.command")
+	}
+}
+
+func (c *AllowList) CheckConfigs(service *types.ServiceConfig) {
+	if len(service.Configs) != 0 {
+		if !c.supported("services.configs") {
+			service.Configs = nil
+			c.Unsupported("services.configs")
+			return
+		}
+		for i, conf := range service.Configs {
+			ref := types.FileReferenceConfig(conf)
+			c.CheckFileReference("configs", &ref)
+			service.Configs[i] = conf
+		}
+	}
+}
+
+func (c *AllowList) CheckContainerName(service *types.ServiceConfig) {
+	if !c.supported("services.container_name") && service.ContainerName != "" {
+		service.ContainerName = ""
+		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.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.Unsupported("services.depends_on")
+	}
+}
+
+func (c *AllowList) CheckDeviceCgroupRules(service *types.ServiceConfig) {
+	if !c.supported("services.device_cgroup_rules") && len(service.DeviceCgroupRules) != 0 {
+		service.DeviceCgroupRules = nil
+		c.Unsupported("services.device_cgroup_rules")
+	}
+}
+
+func (c *AllowList) CheckDevices(service *types.ServiceConfig) {
+	if !c.supported("services.devices") && len(service.Devices) != 0 {
+		service.Devices = nil
+		c.Unsupported("services.devices")
+	}
+}
+
+func (c *AllowList) CheckDNS(service *types.ServiceConfig) {
+	if !c.supported("services.dns") && service.DNS != nil {
+		service.DNS = nil
+		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.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.Unsupported("services.dns_search")
+	}
+}
+
+func (c *AllowList) CheckDomainName(service *types.ServiceConfig) {
+	if !c.supported("services.domainname") && service.DomainName != "" {
+		service.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.Unsupported("services.entrypoint")
+	}
+}
+
+func (c *AllowList) CheckEnvironment(service *types.ServiceConfig) {
+	if !c.supported("services.environment") && len(service.Environment) != 0 {
+		service.Environment = nil
+		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.Unsupported("services.env_file")
+	}
+}
+
+func (c *AllowList) CheckExpose(service *types.ServiceConfig) {
+	if !c.supported("services.expose") && len(service.Expose) != 0 {
+		service.Expose = nil
+		c.Unsupported("services.expose")
+	}
+}
+
+func (c *AllowList) CheckExtends(service *types.ServiceConfig) {
+	if !c.supported("services.extends") && len(service.Extends) != 0 {
+		service.Extends = nil
+		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.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.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.Unsupported("services.group_app")
+	}
+}
+
+func (c *AllowList) CheckHostname(service *types.ServiceConfig) {
+	if !c.supported("services.hostname") && service.Hostname != "" {
+		service.Hostname = ""
+		c.Unsupported("services.hostname")
+	}
+}
+
+func (c *AllowList) CheckHealthCheck(service *types.ServiceConfig) bool {
+	if !c.supported("services.healthcheck") {
+		service.HealthCheck = nil
+		c.Unsupported("services.healthcheck")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckHealthCheckTest(h *types.HealthCheckConfig) {
+	if !c.supported("services.healthcheck.test") && len(h.Test) != 0 {
+		h.Test = nil
+		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.Unsupported("services.healthcheck.timeout")
+	}
+}
+
+func (c *AllowList) CheckHealthCheckInterval(h *types.HealthCheckConfig) {
+	if !c.supported("services.healthcheck.interval") && h.Interval != nil {
+		h.Interval = nil
+		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.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.Unsupported("services.healthcheck.start_period")
+	}
+}
+
+func (c *AllowList) CheckImage(service *types.ServiceConfig) {
+	if !c.supported("services.image") && service.Image != "" {
+		service.Image = ""
+		c.Unsupported("services.image")
+	}
+}
+
+func (c *AllowList) CheckInit(service *types.ServiceConfig) {
+	if !c.supported("services.init") && service.Init != nil {
+		service.Init = nil
+		c.Unsupported("services.init")
+	}
+}
+
+func (c *AllowList) CheckIpc(service *types.ServiceConfig) {
+	if !c.supported("services.ipc") && service.Ipc != "" {
+		service.Ipc = ""
+		c.Unsupported("services.ipc")
+	}
+}
+
+func (c *AllowList) CheckIsolation(service *types.ServiceConfig) {
+	if !c.supported("services.isolation") && service.Isolation != "" {
+		service.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.Unsupported("services.labels")
+	}
+}
+
+func (c *AllowList) CheckLinks(service *types.ServiceConfig) {
+	if !c.supported("services.links") && len(service.Links) != 0 {
+		service.Links = nil
+		c.Unsupported("services.links")
+	}
+}
+
+func (c *AllowList) CheckLogging(service *types.ServiceConfig) bool {
+	if !c.supported("services.logging") {
+		service.Logging = nil
+		c.Unsupported("services.logging")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckLoggingDriver(logging *types.LoggingConfig) {
+	if !c.supported("services.logging.driver") && logging.Driver != "" {
+		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.Unsupported("services.logging.options")
+	}
+}
+
+func (c *AllowList) CheckMemLimit(service *types.ServiceConfig) {
+	if !c.supported("services.mem_limit") && service.MemLimit != 0 {
+		service.MemLimit = 0
+		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.Unsupported("services.mem_reservation")
+	}
+}
+
+func (c *AllowList) CheckMemSwapLimit(service *types.ServiceConfig) {
+	if !c.supported("services.memswap_limit") && service.MemSwapLimit != 0 {
+		service.MemSwapLimit = 0
+		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.Unsupported("services.mem_swappiness")
+	}
+}
+
+func (c *AllowList) CheckMacAddress(service *types.ServiceConfig) {
+	if !c.supported("services.mac_address") && service.MacAddress != "" {
+		service.MacAddress = ""
+		c.Unsupported("services.mac_address")
+	}
+}
+
+func (c *AllowList) CheckNet(service *types.ServiceConfig) {
+	if !c.supported("services.net") && service.Net != "" {
+		service.Net = ""
+		c.Unsupported("services.net")
+	}
+}
+
+func (c *AllowList) CheckNetworkMode(service *types.ServiceConfig) {
+	if !c.supported("services.network_mode") && service.NetworkMode != "" {
+		service.NetworkMode = ""
+		c.Unsupported("services.network_mode")
+	}
+}
+
+func (c *AllowList) CheckNetworks(service *types.ServiceConfig) bool {
+	if !c.supported("services.networks") {
+		service.Networks = nil
+		c.Unsupported("services.networks")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckNetworkAliases(n *types.ServiceNetworkConfig) {
+	if !c.supported("services.networks.aliases") && len(n.Aliases) != 0 {
+		n.Aliases = nil
+		c.Unsupported("services.networks.aliases")
+	}
+}
+
+func (c *AllowList) CheckNetworkIpv4Address(n *types.ServiceNetworkConfig) {
+	if !c.supported("services.networks.ipv4_address") && n.Ipv4Address != "" {
+		n.Ipv4Address = ""
+		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.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.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.Unsupported("services.oom_score_adj")
+	}
+}
+
+func (c *AllowList) CheckPid(service *types.ServiceConfig) {
+	if !c.supported("services.pid") && service.Pid != "" {
+		service.Pid = ""
+		c.Unsupported("services.pid")
+	}
+}
+
+func (c *AllowList) CheckPidsLimit(service *types.ServiceConfig) {
+	if !c.supported("services.pids_limit") && service.PidsLimit != 0 {
+		service.PidsLimit = 0
+		c.Unsupported("services.pids_limit")
+	}
+}
+
+func (c *AllowList) CheckPlatform(service *types.ServiceConfig) {
+	if !c.supported("services.platform") && service.Platform != "" {
+		service.Platform = ""
+		c.Unsupported("services.platform")
+	}
+}
+
+func (c *AllowList) CheckPorts(service *types.ServiceConfig) bool {
+	if !c.supported("services.ports") {
+		service.Ports = nil
+		c.Unsupported("services.ports")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckPortsMode(p *types.ServicePortConfig) {
+	if !c.supported("services.ports.mode") && p.Mode != "" {
+		p.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.Unsupported("services.ports.target")
+	}
+}
+
+func (c *AllowList) CheckPortsPublished(p *types.ServicePortConfig) {
+	if !c.supported("services.ports.published") && p.Published == "" {
+		p.Published = ""
+		c.Unsupported("services.ports.published")
+	}
+}
+
+func (c *AllowList) CheckPortsProtocol(p *types.ServicePortConfig) {
+	if !c.supported("services.ports.protocol") && p.Protocol != "" {
+		p.Protocol = ""
+		c.Unsupported("services.ports.protocol")
+	}
+}
+
+func (c *AllowList) CheckPrivileged(service *types.ServiceConfig) {
+	if !c.supported("services.privileged") && service.Privileged {
+		service.Privileged = false
+		c.Unsupported("services.privileged")
+	}
+}
+
+func (c *AllowList) CheckPullPolicy(service *types.ServiceConfig) {
+	if !c.supported("services.pull_policy") && service.PullPolicy != "" {
+		service.PullPolicy = "false"
+		c.Unsupported("services.pull_policy")
+	}
+}
+
+func (c *AllowList) CheckReadOnly(service *types.ServiceConfig) {
+	if !c.supported("services.read_only") && service.ReadOnly {
+		service.ReadOnly = false
+		c.Unsupported("services.read_only")
+	}
+}
+
+func (c *AllowList) CheckRestart(service *types.ServiceConfig) {
+	if !c.supported("services.restart") && service.Restart != "" {
+		service.Restart = ""
+		c.Unsupported("services.restart")
+	}
+}
+
+func (c *AllowList) CheckRuntime(service *types.ServiceConfig) {
+	if !c.supported("services.runtime") && service.Runtime != "" {
+		service.Runtime = ""
+		c.Unsupported("services.runtime")
+	}
+}
+
+func (c *AllowList) CheckScale(service *types.ServiceConfig) {
+	if !c.supported("services.scale") && service.Scale != 0 {
+		service.Scale = 0
+		c.Unsupported("services.scale")
+	}
+}
+
+func (c *AllowList) CheckSecrets(service *types.ServiceConfig) {
+	if len(service.Secrets) != 0 {
+		if !c.supported("services.secrets") {
+			service.Secrets = nil
+			c.Unsupported("services.secrets")
+		}
+		for i, s := range service.Secrets {
+			ref := types.FileReferenceConfig(s)
+			c.CheckFileReference("services.secrets", &ref)
+			service.Secrets[i] = s
+		}
+	}
+}
+
+func (c *AllowList) CheckFileReference(s string, config *types.FileReferenceConfig) {
+	c.CheckFileReferenceSource(s, config)
+	c.CheckFileReferenceTarget(s, config)
+	c.CheckFileReferenceGID(s, config)
+	c.CheckFileReferenceUID(s, config)
+	c.CheckFileReferenceMode(s, config)
+}
+
+func (c *AllowList) CheckFileReferenceSource(s string, config *types.FileReferenceConfig) {
+	k := fmt.Sprintf("%s.source", s)
+	if !c.supported(k) && config.Source != "" {
+		config.Source = ""
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileReferenceTarget(s string, config *types.FileReferenceConfig) {
+	k := fmt.Sprintf("%s.target", s)
+	if !c.supported(k) && config.Target == "" {
+		config.Target = ""
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileReferenceUID(s string, config *types.FileReferenceConfig) {
+	k := fmt.Sprintf("%s.uid", s)
+	if !c.supported(k) && config.UID != "" {
+		config.UID = ""
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileReferenceGID(s string, config *types.FileReferenceConfig) {
+	k := fmt.Sprintf("%s.gid", s)
+	if !c.supported(k) && config.GID != "" {
+		config.GID = ""
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckFileReferenceMode(s string, config *types.FileReferenceConfig) {
+	k := fmt.Sprintf("%s.mode", s)
+	if !c.supported(k) && config.Mode != nil {
+		config.Mode = nil
+		c.Unsupported(k)
+	}
+}
+
+func (c *AllowList) CheckSecurityOpt(service *types.ServiceConfig) {
+	if !c.supported("services.security_opt") && len(service.SecurityOpt) != 0 {
+		service.SecurityOpt = nil
+		c.Unsupported("services.security_opt")
+	}
+}
+
+func (c *AllowList) CheckShmSize(service *types.ServiceConfig) {
+	if !c.supported("services.shm_size") && service.ShmSize != 0 {
+		service.ShmSize = 0
+		c.Unsupported("services.shm_size")
+	}
+}
+
+func (c *AllowList) CheckStdinOpen(service *types.ServiceConfig) {
+	if !c.supported("services.stdin_open") && service.StdinOpen {
+		service.StdinOpen = true
+		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.Unsupported("services.stop_grace_period")
+	}
+}
+
+func (c *AllowList) CheckStopSignal(service *types.ServiceConfig) {
+	if !c.supported("services.stop_signal") && service.StopSignal != "" {
+		service.StopSignal = ""
+		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.Unsupported("services.sysctls")
+	}
+}
+
+func (c *AllowList) CheckTmpfs(service *types.ServiceConfig) {
+	if !c.supported("services.tmpfs") && len(service.Tmpfs) != 0 {
+		service.Tmpfs = nil
+		c.Unsupported("services.tmpfs")
+	}
+}
+
+func (c *AllowList) CheckTty(service *types.ServiceConfig) {
+	if !c.supported("services.tty") && service.Tty {
+		service.Tty = false
+		c.Unsupported("services.tty")
+	}
+}
+
+func (c *AllowList) CheckUlimits(service *types.ServiceConfig) {
+	if !c.supported("services.ulimits") && len(service.Ulimits) != 0 {
+		service.Ulimits = nil
+		c.Unsupported("services.ulimits")
+	}
+}
+
+func (c *AllowList) CheckUser(service *types.ServiceConfig) {
+	if !c.supported("services.user") && service.User != "" {
+		service.User = ""
+		c.Unsupported("services.user")
+	}
+}
+
+func (c *AllowList) CheckUserNSMode(service *types.ServiceConfig) {
+	if !c.supported("services.userns_mode") && service.UserNSMode != "" {
+		service.UserNSMode = ""
+		c.Unsupported("services.userns_mode")
+	}
+}
+
+func (c *AllowList) CheckUts(service *types.ServiceConfig) {
+	if !c.supported("services.build") && service.Uts != "" {
+		service.Uts = ""
+		c.Unsupported("services.uts")
+	}
+}
+
+func (c *AllowList) CheckVolumeDriver(service *types.ServiceConfig) {
+	if !c.supported("services.volume_driver") && service.VolumeDriver != "" {
+		service.VolumeDriver = ""
+		c.Unsupported("services.volume_driver")
+	}
+}
+
+func (c *AllowList) CheckServiceVolumes(service *types.ServiceConfig) bool {
+	if !c.supported("services.volumes") {
+		service.Volumes = nil
+		c.Unsupported("services.volumes")
+		return false
+	}
+	return true
+}
+
+func (c *AllowList) CheckVolumesSource(config *types.ServiceVolumeConfig) {
+	if !c.supported("services.volumes.source") && config.Source != "" {
+		config.Source = ""
+		c.Unsupported("services.volumes.source")
+	}
+}
+
+func (c *AllowList) CheckVolumesTarget(config *types.ServiceVolumeConfig) {
+	if !c.supported("services.volumes.target") && config.Target != "" {
+		config.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.Unsupported("services.volumes.read_only")
+	}
+}
+
+func (c *AllowList) CheckVolumesConsistency(config *types.ServiceVolumeConfig) {
+	if !c.supported("services.volumes.consistency") && config.Consistency != "" {
+		config.Consistency = ""
+		c.Unsupported("services.volumes.consistency")
+	}
+}
+
+func (c *AllowList) CheckVolumesBind(config *types.ServiceVolumeBind) {
+	if config == nil {
+		return
+	}
+	if !c.supported("services.volumes.bind.propagation") && config.Propagation != "" {
+		config.Propagation = ""
+		c.Unsupported("services.volumes.bind.propagation")
+	}
+}
+
+func (c *AllowList) CheckVolumesVolume(config *types.ServiceVolumeVolume) {
+	if config == nil {
+		return
+	}
+	if !c.supported("services.volumes.nocopy") && config.NoCopy {
+		config.NoCopy = false
+		c.Unsupported("services.volumes.nocopy")
+	}
+}
+
+func (c *AllowList) CheckVolumesTmpfs(config *types.ServiceVolumeTmpfs) {
+	if config == nil {
+		return
+	}
+	if !c.supported("services.volumes.tmpfs.size") && config.Size != 0 {
+		config.Size = 0
+		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.Unsupported("services.volumes_from")
+	}
+}
+
+func (c *AllowList) CheckWorkingDir(service *types.ServiceConfig) {
+	if !c.supported("services.working_dir") && service.WorkingDir != "" {
+		service.WorkingDir = ""
+		c.Unsupported("services.working_dir")
+	}
+}
diff --git a/compatibility/volumes.go b/compatibility/volumes.go
new file mode 100644
index 0000000..e2b0d0a
--- /dev/null
+++ b/compatibility/volumes.go
@@ -0,0 +1,47 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+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.Unsupported("volumes.driver")
+	}
+}
+
+func (c *AllowList) CheckVolumeConfigDriverOpts(config *types.VolumeConfig) {
+	if !c.supported("volumes.driver_opts") && len(config.DriverOpts) != 0 {
+		config.DriverOpts = nil
+		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.Unsupported("volumes.external")
+	}
+}
+
+func (c *AllowList) CheckVolumeConfigLabels(config *types.VolumeConfig) {
+	if !c.supported("volumes.labels") && len(config.Labels) != 0 {
+		config.Labels = nil
+		c.Unsupported("volumes.labels")
+	}
+}
diff --git a/loader/full-example.yml b/loader/full-example.yml
index 585c31b..cb300c9 100644
--- a/loader/full-example.yml
+++ b/loader/full-example.yml
@@ -160,8 +160,8 @@ services:
     #   somehost: "162.242.195.82"
     #   otherhost: "50.31.209.229"
     extra_hosts:
-      - "otherhost:50.31.209.229"
       - "somehost:162.242.195.82"
+      - "otherhost:50.31.209.229"
 
     hostname: foo
 
@@ -428,8 +428,6 @@ secrets:
     environment: BAR
     x-bar: baz
     x-foo: bar
-  secret5:
-    file: /abs/secret_data
 x-bar: baz
 x-foo: bar
 x-nested:
diff --git a/loader/loader.go b/loader/loader.go
index 784cdb2..2b5ab25 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -563,7 +563,7 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
 				if vol.Type != types.VolumeTypeBind {
 					continue
 				}
-				baseService.Volumes[i].Source = resolveMaybeUnixPath(vol.Source, baseFileParent, lookupEnv)
+				baseService.Volumes[i].Source = absPath(baseFileParent, vol.Source)
 			}
 		}
 
@@ -667,8 +667,8 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string, l
 	return nil
 }
 
-func resolveMaybeUnixPath(path string, workingDir string, lookupEnv template.Mapping) string {
-	filePath := expandUser(path, lookupEnv)
+func resolveVolumePath(volume types.ServiceVolumeConfig, workingDir string, lookupEnv template.Mapping) types.ServiceVolumeConfig {
+	filePath := expandUser(volume.Source, lookupEnv)
 	// Check if source is an absolute path (either Unix or Windows), to
 	// handle a Windows client with a Unix daemon or vice-versa.
 	//
@@ -678,21 +678,10 @@ func resolveMaybeUnixPath(path string, workingDir string, lookupEnv template.Map
 	if !paths.IsAbs(filePath) && !isAbs(filePath) {
 		filePath = absPath(workingDir, filePath)
 	}
-	return filePath
-}
-
-func resolveVolumePath(volume types.ServiceVolumeConfig, workingDir string, lookupEnv template.Mapping) types.ServiceVolumeConfig {
-	volume.Source = resolveMaybeUnixPath(volume.Source, workingDir, lookupEnv)
+	volume.Source = filePath
 	return volume
 }
 
-func resolveSecretsPath(secret types.SecretConfig, workingDir string, lookupEnv template.Mapping) types.SecretConfig {
-	if ! secret.External.External && secret.File != "" {
-		secret.File = resolveMaybeUnixPath(secret.File, workingDir, lookupEnv)
-	}
-	return secret
-}
-
 // TODO: make this more robust
 func expandUser(path string, lookupEnv template.Mapping) string {
 	if strings.HasPrefix(path, "~") {
@@ -800,14 +789,11 @@ func LoadSecrets(source map[string]interface{}, details types.ConfigDetails, res
 		return secrets, err
 	}
 	for name, secret := range secrets {
-		obj, err := loadFileObjectConfig(name, "secret", types.FileObjectConfig(secret), details, false)
+		obj, err := loadFileObjectConfig(name, "secret", types.FileObjectConfig(secret), details, resolvePaths)
 		if err != nil {
 			return nil, err
 		}
 		secretConfig := types.SecretConfig(obj)
-		if resolvePaths {
-			secretConfig = resolveSecretsPath(secretConfig, details.WorkingDir, details.LookupEnv)
-		}
 		secrets[name] = secretConfig
 	}
 	return secrets, nil
diff --git a/loader/merge.go b/loader/merge.go
index 019ebac..2b1e330 100644
--- a/loader/merge.go
+++ b/loader/merge.go
@@ -38,20 +38,10 @@ var serviceSpecials = &specials{
 		reflect.TypeOf([]types.ServiceSecretConfig{}):    mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
 		reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice),
 		reflect.TypeOf(&types.UlimitsConfig{}):           mergeUlimitsConfig,
-		reflect.TypeOf(&types.ServiceNetworkConfig{}):    mergeServiceNetworkConfig,
 	},
 }
 
 func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error {
-	// TODO this is a workaround waiting for imdario/mergo#131
-	if t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Bool {
-		return func(dst, src reflect.Value) error {
-			if dst.CanSet() && !src.IsNil() {
-				dst.Set(src)
-			}
-			return nil
-		}
-	}
 	if fn, ok := s.m[t]; ok {
 		return fn
 	}
@@ -122,10 +112,7 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
 }
 
 func _merge(baseService *types.ServiceConfig, overrideService *types.ServiceConfig) (*types.ServiceConfig, error) {
-	if err := mergo.Merge(baseService, overrideService,
-		mergo.WithAppendSlice,
-		mergo.WithOverride,
-		mergo.WithTransformers(serviceSpecials)); err != nil {
+	if err := mergo.Merge(baseService, overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(serviceSpecials)); err != nil {
 		return nil, err
 	}
 	if overrideService.Command != nil {
@@ -339,20 +326,6 @@ func mergeUlimitsConfig(dst, src reflect.Value) error {
 	return nil
 }
 
-// nolint: unparam
-func mergeServiceNetworkConfig(dst, src reflect.Value) error {
-	if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
-		dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))
-		if ipv4 := src.Elem().FieldByName("Ipv4Address").Interface().(string); ipv4 != "" {
-			dst.Elem().FieldByName("Ipv4Address").SetString(ipv4)
-		}
-		if ipv6 := src.Elem().FieldByName("Ipv6Address").Interface().(string); ipv6 != "" {
-			dst.Elem().FieldByName("Ipv6Address").SetString(ipv6)
-		}
-	}
-	return nil
-}
-
 func getLoggingDriver(v reflect.Value) string {
 	return v.FieldByName("Driver").String()
 }
diff --git a/schema/compose-spec.json b/schema/compose-spec.json
index 5710e47..d444e71 100644
--- a/schema/compose-spec.json
+++ b/schema/compose-spec.json
@@ -406,9 +406,6 @@
                           {"type": "integer", "minimum": 0},
                           {"type": "string"}
                         ]
-                      },
-                      "mode": {
-                        "type": "number"
                       }
                     },
                     "additionalProperties": false,
diff --git a/types/types.go b/types/types.go
index 2c4f02d..cb42c6e 100644
--- a/types/types.go
+++ b/types/types.go
@@ -539,18 +539,6 @@ func (h HostsList) AsList() []string {
 	return l
 }
 
-func (h HostsList) MarshalYAML() (interface{}, error) {
-	list := h.AsList()
-	sort.Strings(list)
-	return list, nil
-}
-
-func (h HostsList) MarshalJSON() ([]byte, error) {
-	list := h.AsList()
-	sort.Strings(list)
-	return json.Marshal(list)
-}
-
 // LoggingConfig the logging configuration for a service
 type LoggingConfig struct {
 	Driver  string            `yaml:",omitempty" json:"driver,omitempty"`
@@ -840,8 +828,6 @@ type ServiceVolumeVolume struct {
 type ServiceVolumeTmpfs struct {
 	Size UnitBytes `yaml:",omitempty" json:"size,omitempty"`
 
-	Mode uint32 `yaml:",omitempty" json:"mode,omitempty"`
-
 	Extensions map[string]interface{} `yaml:",inline" json:"-"`
 }
 


Test patch:
diff --git a/compatibility/allowlist_test.go b/compatibility/allowlist_test.go
new file mode 100644
index 0000000..f5951da
--- /dev/null
+++ b/compatibility/allowlist_test.go
@@ -0,0 +1,78 @@
+/*
+   Copyright 2020 The Compose Specification Authors.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compatibility
+
+import (
+	"testing"
+
+	"github.com/compose-spec/compose-go/errdefs"
+	"github.com/compose-spec/compose-go/loader"
+	"github.com/compose-spec/compose-go/types"
+	"gotest.tools/v3/assert"
+)
+
+func TestAllowList(t *testing.T) {
+	var checker Checker = customChecker{
+		&AllowList{
+			Supported: []string{
+				"services.image",
+				"services.network_mode",
+				"services.privileged",
+				"services.networks",
+				"services.scale",
+			},
+		},
+	}
+	dict := []byte(`
+services:
+  foo:
+    image: busybox
+    network_mode: host
+    privileged: true
+    mac_address: "a:b:c:d"
+`)
+
+	project, err := loader.Load(types.ConfigDetails{
+		ConfigFiles: []types.ConfigFile{
+			{Filename: "filename.yml", Content: dict},
+		},
+	})
+	assert.NilError(t, err)
+
+	Check(project, checker)
+	errors := checker.Errors()
+	assert.Check(t, len(errors) == 2)
+	assert.Check(t, errdefs.IsUnsupportedError(errors[0]))
+	assert.Equal(t, errors[0].Error(), "services.mac_address: unsupported attribute")
+
+	assert.Check(t, errdefs.IsUnsupportedError(errors[1]))
+	assert.Equal(t, errors[1].Error(), "services.network_mode=host: unsupported attribute")
+
+	service, err := project.GetService("foo")
+	assert.NilError(t, err)
+	assert.Check(t, service.MacAddress == "")
+}
+
+type customChecker struct {
+	*AllowList
+}
+
+func (c customChecker) CheckNetworkMode(service *types.ServiceConfig) {
+	if service.NetworkMode == "host" {
+		c.Unsupported("services.network_mode=host")
+	}
+}
diff --git a/loader/full-struct_test.go b/loader/full-struct_test.go
index 263ef22..c339c4c 100644
--- a/loader/full-struct_test.go
+++ b/loader/full-struct_test.go
@@ -574,9 +574,6 @@ func secrets(workingDir string) map[string]types.SecretConfig {
 				"x-foo": "bar",
 			},
 		},
-		"secret5": {
-			File: "/abs/secret_data",
-		},
 	}
 }
 
@@ -716,8 +713,8 @@ services:
     - project_db_1:mysql
     - project_db_1:postgresql
     extra_hosts:
-    - otherhost:50.31.209.229
-    - somehost:162.242.195.82
+      otherhost: 50.31.209.229
+      somehost: 162.242.195.82
     hostname: foo
     healthcheck:
       test:
@@ -988,8 +985,6 @@ secrets:
     environment: BAR
     x-bar: baz
     x-foo: bar
-  secret5:
-    file: /abs/secret_data
 configs:
   config1:
     file: %s
@@ -1111,10 +1106,6 @@ func fullExampleJSON(workingDir, homeDir string) string {
       "name": "bar",
       "environment": "BAR",
       "external": false
-    },
-    "secret5": {
-      "file": "/abs/secret_data",
-      "external": false
     }
   },
   "services": {
@@ -1302,10 +1293,10 @@ func fullExampleJSON(workingDir, homeDir string) string {
         "project_db_1:mysql",
         "project_db_1:postgresql"
       ],
-      "extra_hosts": [
-        "otherhost:50.31.209.229",
-        "somehost:162.242.195.82"
-      ],
+      "extra_hosts": {
+        "otherhost": "50.31.209.229",
+        "somehost": "162.242.195.82"
+      },
       "hostname": "foo",
       "healthcheck": {
         "test": [
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 8c1ccec..14217dc 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -1861,12 +1861,6 @@ func TestLoadWithExtends(t *testing.T) {
 			},
 			Environment: types.MappingWithEquals{},
 			Networks:    map[string]*types.ServiceNetworkConfig{"default": nil},
-			Volumes:     []types.ServiceVolumeConfig{{
-				Type:   "bind",
-				Source: "/opt/data",
-				Target: "/var/lib/mysql",
-				Bind:   &types.ServiceVolumeBind{CreateHostPath: true},
-			}},
 			Scale:       1,
 		},
 	}
diff --git a/loader/merge_test.go b/loader/merge_test.go
index bbe32cd..698c510 100644
--- a/loader/merge_test.go
+++ b/loader/merge_test.go
@@ -816,7 +816,7 @@ func TestLoadMultipleServiceNetworks(t *testing.T) {
 			},
 			expected: map[string]*types.ServiceNetworkConfig{
 				"net1": {
-					Aliases: []string{"alias2", "alias3"},
+					Aliases: []string{"alias1", "alias2", "alias3"},
 				},
 				"net2": nil,
 				"net3": {},
@@ -1110,53 +1110,9 @@ func TestMergeUlimitsConfig(t *testing.T) {
 	)
 }
 
-func TestInitOverride(t *testing.T) {
-	var (
-		bt = true
-		bf = false
-	)
-	cases := []struct {
-		base     *bool
-		override *bool
-		expect   bool
-	}{
-		{
-			base:     &bt,
-			override: &bf,
-			expect:   false,
-		},
-		{
-			base:     nil,
-			override: &bt,
-			expect:   true,
-		},
-		{
-			base:     &bt,
-			override: nil,
-			expect:   true,
-		},
-	}
-	for _, test := range cases {
-		base := types.ServiceConfig{
-			Init: test.base,
-		}
-		override := types.ServiceConfig{
-			Init: test.override,
-		}
-		config, err := _merge(&base, &override)
-		assert.NilError(t, err)
-		assert.Check(t, *config.Init == test.expect)
-	}
-}
-
 func TestMergeServiceNetworkConfig(t *testing.T) {
-	specials := &specials{
-		m: map[reflect.Type]func(dst, src reflect.Value) error{
-			reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig,
-		},
-	}
 	base := map[string]*types.ServiceNetworkConfig{
-		"override-aliases": {
+		"merge": {
 			Aliases:     []string{"100", "101"},
 			Ipv4Address: "127.0.0.1",
 			Ipv6Address: "0:0:0:0:0:0:0:1",
@@ -1168,7 +1124,7 @@ func TestMergeServiceNetworkConfig(t *testing.T) {
 		},
 	}
 	override := map[string]*types.ServiceNetworkConfig{
-		"override-aliases": {
+		"merge": {
 			Aliases:     []string{"110", "111"},
 			Ipv4Address: "127.0.1.1",
 			Ipv6Address: "0:0:0:0:0:0:1:1",
@@ -1179,14 +1135,14 @@ func TestMergeServiceNetworkConfig(t *testing.T) {
 			Ipv6Address: "0:0:0:0:0:0:3:1",
 		},
 	}
-	err := mergo.Merge(&base, &override, mergo.WithOverride, mergo.WithTransformers(specials))
+	err := mergo.Merge(&base, &override, mergo.WithAppendSlice, mergo.WithOverride)
 	assert.NilError(t, err)
 	assert.DeepEqual(
 		t,
 		base,
 		map[string]*types.ServiceNetworkConfig{
-			"override-aliases": {
-				Aliases:     []string{"110", "111"},
+			"merge": {
+				Aliases:     []string{"100", "101", "110", "111"},
 				Ipv4Address: "127.0.1.1",
 				Ipv6Address: "0:0:0:0:0:0:1:1",
 			},
diff --git a/loader/testdata/compose-test-extends-imported.yaml b/loader/testdata/compose-test-extends-imported.yaml
index c38f39f..03f2ea2 100644
--- a/loader/testdata/compose-test-extends-imported.yaml
+++ b/loader/testdata/compose-test-extends-imported.yaml
@@ -1,5 +1,3 @@
 services:
   imported:
     image: nginx
-    volumes:
-      - /opt/data:/var/lib/mysql