package dockerCompose type Config struct { Networks map[string]*Network `json:"networks,omitempty" yaml:"networks,omitempty"` Secrets map[string]*Secret `json:"secrets,omitempty" yaml:"secrets,omitempty"` Services map[string]*Service `json:"services,omitempty" yaml:"services,omitempty"` Version string `json:"version,omitempty" yaml:"version,omitempty"` Volumes map[string]*Volume `json:"volumes,omitempty" yaml:"volumes,omitempty"` } // Equal returns true if the passed equalable is equal func (c *Config) Equal(equalable Equalable) bool { config, ok := equalable.(*Config) if !ok { return false } switch { case c == nil && config == nil: return true case c != nil && config == nil: fallthrough case c == nil && config != nil: return false default: return EqualStringMap(c.Networks, config.Networks) && EqualStringMap(c.Secrets, config.Secrets) && EqualStringMap(c.Services, config.Services) && c.Version == config.Version && EqualStringMap(c.Volumes, config.Volumes) } } // ExistsNetwork returns true if a network with the passed named exists. func (c *Config) ExistsNetwork(name string) bool { return ExistsInMap(c.Networks, name) } // ExistsSecret returns true if a secret with the passed named exists. func (c *Config) ExistsSecret(name string) bool { return ExistsInMap(c.Secrets, name) } // ExistsService returns true if a service with the passed named exists. func (c *Config) ExistsService(name string) bool { return ExistsInMap(c.Services, name) } // ExistsVolumes returns true if a volume with the passed named exists. func (c *Config) ExistsVolume(name string) bool { return ExistsInMap(c.Volumes, name) } // MergeLastWin merges a config and overwrite already existing properties func (c *Config) MergeLastWin(config *Config) { if c.Version != config.Version { c.Version = config.Version } for networkName, network := range c.Networks { if network == nil { continue } if c.ExistsNetwork(networkName) { c.Networks[networkName].MergeLastWin(network) } else { c.Networks[networkName] = network } } for secretName, secret := range c.Secrets { if secret == nil { continue } if c.ExistsSecret(secretName) { c.Secrets[secretName].MergeLastWin(secret) } else { c.Secrets[secretName] = secret } } for serviceName, service := range c.Services { if service == nil { continue } if c.ExistsService(serviceName) { c.Services[serviceName].MergeLastWin(service) } else { c.Services[serviceName] = service } } // for volumeName, volume := range c.Volumes { // if volume == nil { // continue // } // if c.ExistsVolume(volumeName) { // c.Volumes[volumeName].MergeLastWin(volume) // } else { // c.Volumes[volumeName] = volume // } // } } func NewConfig() *Config { return &Config{ Services: make(map[string]*Service), Networks: make(map[string]*Network), Secrets: make(map[string]*Secret), Volumes: make(map[string]*Volume), } } type Network struct { External bool `json:"external,omitempty" yaml:"external,omitempty"` Driver string `json:"driver,omitempty" yaml:"driver,omitempty"` IPAM *NetworkIPAM `json:"ipam,omitempty" yaml:"ipam,omitempty"` } // Equal returns true if the passed equalable is equal func (n *Network) Equal(equalable Equalable) bool { network, ok := equalable.(*Network) if !ok { return false } switch { case n == nil && network == nil: return true case n != nil && network == nil: fallthrough case n == nil && network != nil: return false default: return n.External == network.External && n.Driver == network.Driver && n.IPAM.Equal(network.IPAM) } } func (n *Network) MergeLastWin(network *Network) { if !n.IPAM.Equal(network.IPAM) { n.IPAM = network.IPAM } } type NetworkIPAM struct { Config []*NetworkIPAMConfig `json:"config,omitempty" yaml:"config,omitempty"` } // Equal returns true if the passed equalable is equal func (nIPAM *NetworkIPAM) Equal(equalable Equalable) bool { networkIPAM, ok := equalable.(*NetworkIPAM) if !ok { return false } switch { case nIPAM == nil && networkIPAM == nil: return true case nIPAM != nil && networkIPAM == nil: fallthrough case nIPAM == nil && networkIPAM != nil: return false default: return Equal(nIPAM.Config, networkIPAM.Config) } } type NetworkIPAMConfig struct { Subnet string `json:"subnet,omitempty" yaml:"subnet,omitempty"` } // Equal returns true if the passed equalable is equal func (nIPAMConfig *NetworkIPAMConfig) Equal(equalable Equalable) bool { networkIPAMConfig, ok := equalable.(*NetworkIPAMConfig) if !ok { return false } switch { case nIPAMConfig == nil && networkIPAMConfig == nil: return true case nIPAMConfig != nil && networkIPAMConfig == nil: fallthrough case nIPAMConfig == nil && networkIPAMConfig != nil: return false default: return nIPAMConfig.Subnet == networkIPAMConfig.Subnet } } type Secret struct { File string `json:"file,omitempty" yaml:"file,omitempty"` } // Equal returns true if the passed equalable is equal func (s *Secret) Equal(equalable Equalable) bool { secret, ok := equalable.(*Secret) if !ok { return false } switch { case s == nil && secret == nil: return true case s != nil && secret == nil: fallthrough case s == nil && secret != nil: return false default: return s.File == secret.File } } func (s *Secret) MergeLastWin(secret *Secret) { if !s.Equal(secret) { s.File = secret.File } } type Service struct { CapabilitiesAdd []string `json:"cap_add,omitempty" yaml:"cap_add,omitempty"` CapabilitiesDrop []string `json:"cap_drop,omitempty" yaml:"cap_drop,omitempty"` Deploy *ServiceDeploy `json:"deploy,omitempty" yaml:"deploy,omitempty"` Environments []string `json:"environment,omitempty" yaml:"environment,omitempty"` ExtraHosts []string `json:"extra_hosts,omitempty" yaml:"extra_hosts,omitempty"` Image string `json:"image,omitempty" yaml:"image,omitempty"` Labels []string `json:"labels,omitempty" yaml:"labels,omitempty"` Networks map[string]*ServiceNetwork `json:"networks,omitempty" yaml:"networks,omitempty"` Ports []string `json:"ports,omitempty" yaml:"ports,omitempty"` Secrets []string `json:"secrets,omitempty" yaml:"secrets,omitempty"` ULimits *ServiceULimits `json:"ulimits,omitempty" yaml:"ulimits,omitempty"` Volumes []string `json:"volumes,omitempty" yaml:"volumes,omitempty"` } // Equal returns true if the passed equalable is equal func (s *Service) Equal(equalable Equalable) bool { service, ok := equalable.(*Service) if !ok { return false } switch { case s == nil && service == nil: return true case s != nil && service == nil: fallthrough case s == nil && service != nil: return false default: return equalSlice(s.CapabilitiesAdd, service.CapabilitiesAdd) && equalSlice(s.CapabilitiesDrop, service.CapabilitiesDrop) && s.Deploy.Equal(service.Deploy) && equalSlice(s.Environments, service.Environments) && equalSlice(s.ExtraHosts, service.ExtraHosts) && s.Image == service.Image && equalSlice(s.Labels, service.Labels) && EqualStringMap(s.Networks, service.Networks) && equalSlice(s.Ports, service.Ports) && equalSlice(s.Secrets, service.Secrets) && s.ULimits.Equal(service.ULimits) && equalSlice(s.Volumes, service.Volumes) } } type ServiceDeploy struct { Resources *ServiceDeployResources `json:"resources" yaml:"resources"` } // Equal returns true if the passed equalable is equal func (sd *ServiceDeploy) Equal(equalable Equalable) bool { serviceDeployment, ok := equalable.(*ServiceDeploy) if !ok { return false } switch { case sd == nil && serviceDeployment == nil: return true case sd != nil && serviceDeployment == nil: fallthrough case sd == nil && serviceDeployment != nil: return false default: return sd.Resources.Equal(serviceDeployment.Resources) } } type ServiceDeployResources struct { Limits *ServiceDeployResourcesLimits `json:"limits,omitempty" yaml:"limits,omitempty"` Reservations *ServiceDeployResourcesLimits `json:"reservations,omitempty" yaml:"reservations,omitempty"` } // Equal returns true if the passed equalable is equal func (sdr *ServiceDeployResources) Equal(equalable Equalable) bool { serviceDeploymentResources, ok := equalable.(*ServiceDeployResources) if !ok { return false } switch { case sdr == nil && serviceDeploymentResources == nil: return true case sdr != nil && serviceDeploymentResources == nil: fallthrough case sdr == nil && serviceDeploymentResources != nil: return false default: return sdr.Limits.Equal(serviceDeploymentResources.Limits) && sdr.Reservations.Equal(serviceDeploymentResources) } } type ServiceDeployResourcesLimits struct { CPUs string `json:"cpus,omitempty" yaml:"cpus,omitempty"` Memory string `json:"memory,omitempty" yaml:"memory,omitempty"` } // Equal returns true if the passed equalable is equal func (sdrl *ServiceDeployResourcesLimits) Equal(equalable Equalable) bool { serviceDeploymentResourcesLimits, ok := equalable.(*ServiceDeployResourcesLimits) if !ok { return false } switch { case sdrl == nil && serviceDeploymentResourcesLimits == nil: return true case sdrl != nil && serviceDeploymentResourcesLimits == nil: fallthrough case sdrl == nil && serviceDeploymentResourcesLimits != nil: return false default: return sdrl.CPUs == serviceDeploymentResourcesLimits.CPUs && sdrl.Memory == serviceDeploymentResourcesLimits.Memory } } type ServiceNetwork struct { Aliases []string `json:"aliases,omitempty" yaml:"aliases,omitempty"` } // Equal returns true if the passed equalable is equal func (sn *ServiceNetwork) Equal(equalable Equalable) bool { serviceNetwork, ok := equalable.(*ServiceNetwork) if !ok { return false } switch { case sn == nil && serviceNetwork == nil: return true case sn != nil && serviceNetwork == nil: fallthrough case sn == nil && serviceNetwork != nil: return false default: return equalSlice(sn.Aliases, serviceNetwork.Aliases) } } type ServiceULimits struct { NProc uint `json:"nproc,omitempty" yaml:"nproc,omitempty"` NoFile *ServiceULimitsNoFile `json:"nofile,omitempty" yaml:"nofile,omitempty"` } // Equal returns true if the passed equalable is equal func (l *ServiceULimits) Equal(equalable Equalable) bool { serviceULimits, ok := equalable.(*ServiceULimits) if !ok { return false } switch { case l == nil && serviceULimits == nil: return true case l != nil && serviceULimits == nil: fallthrough case l == nil && serviceULimits != nil: return false default: return l.NProc == serviceULimits.NProc && l.NoFile.Equal(serviceULimits.NoFile) } } type ServiceULimitsNoFile struct { Hard uint `json:"hard" yaml:"hard"` Soft uint `json:"soft" yaml:"soft"` } // Equal returns true if the passed equalable is equal func (nf *ServiceULimitsNoFile) Equal(equalable Equalable) bool { serviceULimitsNoFile, ok := equalable.(*ServiceULimitsNoFile) if !ok { return false } switch { case nf == nil && serviceULimitsNoFile == nil: return true case nf != nil && serviceULimitsNoFile == nil: fallthrough case nf == nil && serviceULimitsNoFile != nil: return false default: return nf.Hard == serviceULimitsNoFile.Hard && nf.Soft == serviceULimitsNoFile.Soft } } type Volume struct { External bool `json:"external,omitempty" yaml:"external,omitempty"` } // Equal returns true if the passed equalable is equal func (v *Volume) Equal(equalable Equalable) bool { volume, ok := equalable.(*Volume) if !ok { return false } switch { case v == nil && volume == nil: return true case v != nil && volume == nil: fallthrough case v == nil && volume != nil: return false default: return v.External == volume.External } } func equalSlice[K comparable](sliceA []K, sliceB []K) bool { equalFunc := func(sliceA []K, sliceB []K) bool { LOOP: for i := range sliceA { for j := range sliceB { if sliceA[i] == sliceB[j] { continue LOOP } } return false } return true } return equalFunc(sliceA, sliceB) && equalFunc(sliceB, sliceA) }