Initial Commit
This commit is contained in:
commit
6d50c6b060
0
cmd/root.go
Normal file
0
cmd/root.go
Normal file
3
go.mod
Normal file
3
go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module git.cryptic.systems/volker.raschek/dcmerge
|
||||
|
||||
go 1.20
|
256
pki/domain/dockerCompose/config.go
Normal file
256
pki/domain/dockerCompose/config.go
Normal file
@ -0,0 +1,256 @@
|
||||
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"`
|
||||
}
|
||||
|
||||
// ExistsNetwork returns true if a network with the passed named exists.
|
||||
func (c *Config) ExistsNetwork(name string) bool {
|
||||
return existsKeyInMap(c.Networks, name)
|
||||
}
|
||||
|
||||
// ExistsSecret returns true if a secret with the passed named exists.
|
||||
func (c *Config) ExistsSecret(name string) bool {
|
||||
return existsKeyInMap(c.Secrets, name)
|
||||
}
|
||||
|
||||
// ExistsService returns true if a service with the passed named exists.
|
||||
func (c *Config) ExistsService(name string) bool {
|
||||
return existsKeyInMap(c.Services, name)
|
||||
}
|
||||
|
||||
// ExistsVolumes returns true if a volume with the passed named exists.
|
||||
func (c *Config) ExistsVolume(name string) bool {
|
||||
return existsKeyInMap(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"`
|
||||
}
|
||||
|
||||
// MergeLastWin merges a network and overwrite already existing properties
|
||||
func (n *Network) MergeLastWin(network *Network) {
|
||||
if n.External != network.External {
|
||||
n.External = network.External
|
||||
}
|
||||
|
||||
if n.Driver != network.Driver {
|
||||
n.Driver = network.Driver
|
||||
}
|
||||
|
||||
// n.IPAM.MergeLastWin(network.IPAM.Config)
|
||||
}
|
||||
|
||||
type NetworkIPAM struct {
|
||||
Config []*NetworkIPAMConfig `json:"config,omitempty" yaml:"config,omitempty"`
|
||||
}
|
||||
|
||||
func (nipam *NetworkIPAM) Merge(networkIPAM *NetworkIPAM) {
|
||||
if networkIPAM == nil {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type NetworkIPAMConfig struct {
|
||||
Subnet string `json:"subnet,omitempty" yaml:"subnet,omitempty"`
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
File string `json:"file,omitempty" yaml:"file,omitempty"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
type ServiceDeploy struct {
|
||||
Resources *ServiceDeployResources `json:"resources" yaml:"resources"`
|
||||
}
|
||||
|
||||
type ServiceDeployResources struct {
|
||||
Limits *ServiceDeployResourcesLimits `json:"limits,omitempty" yaml:"limits,omitempty"`
|
||||
Reservations *ServiceDeployResourcesLimits `json:"reservations,omitempty" yaml:"reservations,omitempty"`
|
||||
}
|
||||
|
||||
type ServiceDeployResourcesLimits struct {
|
||||
CPUs string `json:"cpus,omitempty" yaml:"cpus,omitempty"`
|
||||
Memory string `json:"memory,omitempty" yaml:"memory,omitempty"`
|
||||
}
|
||||
|
||||
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:
|
||||
//TODO: return false
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
54
pki/domain/dockerCompose/equalable.go
Normal file
54
pki/domain/dockerCompose/equalable.go
Normal file
@ -0,0 +1,54 @@
|
||||
package dockerCompose
|
||||
|
||||
type Equalable interface {
|
||||
Equal(equalable Equalable) bool
|
||||
}
|
||||
|
||||
// Contains returns true when sliceA is in sliceB.
|
||||
func Contains[R Equalable](sliceA, sliceB []R) bool {
|
||||
switch {
|
||||
case sliceA == nil && sliceB == nil:
|
||||
return true
|
||||
case sliceA != nil && sliceB == nil:
|
||||
return false
|
||||
case sliceA == nil && sliceB != nil:
|
||||
return false
|
||||
default:
|
||||
LOOP:
|
||||
for i := range sliceA {
|
||||
for j := range sliceB {
|
||||
if sliceA[i].Equal(sliceB[j]) {
|
||||
continue LOOP
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Equal returns true when sliceA and sliceB are equal.
|
||||
func Equal[R Equalable](sliceA, sliceB []R) bool {
|
||||
return Contains(sliceA, sliceB) &&
|
||||
Contains(sliceB, sliceA) &&
|
||||
len(sliceA) == len(sliceB)
|
||||
}
|
||||
|
||||
// Equal returns true when booth string maps of Equalable are equal.
|
||||
func EqualStringMap[R Equalable](mapA, mapB map[string]R) bool {
|
||||
equalFunc := func(mapA, mapB map[string]R) bool {
|
||||
LOOP:
|
||||
for keyA, valueA := range mapA {
|
||||
for keyB, valueB := range mapB {
|
||||
if keyA == keyB &&
|
||||
valueA.Equal(valueB) {
|
||||
continue LOOP
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return equalFunc(mapA, mapB) && equalFunc(mapB, mapA)
|
||||
}
|
Loading…
Reference in New Issue
Block a user