Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
Signed-off-by: Suleiman Dibirov <[email protected]>
  • Loading branch information
idsulik committed Feb 24, 2025
1 parent fe61eaf commit f97276d
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 46 deletions.
4 changes: 2 additions & 2 deletions pkg/api/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ const (
// ConfigHashLabel stores configuration hash for a compose service
ConfigHashLabel = "com.docker.compose.config-hash"
// ServiceConfigsHash stores configuration hash for a compose service configs
ServiceConfigsHash = "com.docker.compose.service.configs-hash"
ServiceConfigsHash = "com.docker.compose.service.%s.configs.hash"
// ServiceSecretsHash stores configuration hash for a compose service secrets
ServiceSecretsHash = "com.docker.compose.service.secrets-hash"
ServiceSecretsHash = "com.docker.compose.service.%s.secrets.hash"
// ContainerNumberLabel stores the container index of a replicated service
ContainerNumberLabel = "com.docker.compose.container-number"
// VolumeLabel allow to track resource related to a compose volume
Expand Down
19 changes: 13 additions & 6 deletions pkg/compose/convergence.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ func (c *convergence) resolveSharedNamespaces(service *types.ServiceConfig) erro
return nil
}

func (c *convergence) mustRecreate(project *types.Project,, expected types.ServiceConfig, actual containerType.Summary, policy string) (bool, error) {
//nolint:gocyclo
func (c *convergence) mustRecreate(project *types.Project, expected types.ServiceConfig, actual containerType.Summary, policy string) (bool, error) {
if policy == api.RecreateNever {
return false, nil
}
Expand All @@ -343,20 +344,26 @@ func (c *convergence) mustRecreate(project *types.Project,, expected types.Servi
return true, nil
}

serviceConfigsHash, err := ServiceConfigsHash(project, expected)
serviceNameToConfigHash, err := ServiceConfigsHash(project, expected)
if err != nil {
return false, err
}

for serviceName, hash := range serviceNameToConfigHash {
if actual.Labels[fmt.Sprintf(api.ServiceConfigsHash, serviceName)] != hash {
return true, nil
}
}

serviceSecretsHash, err := ServiceSecretsHash(project, expected)
if err != nil {
return false, err
}
serviceConfigsChanged := actual.Labels[api.ServiceConfigsHash] != serviceConfigsHash
serviceSecretsChanged := actual.Labels[api.ServiceSecretsHash] != serviceSecretsHash

if serviceConfigsChanged || serviceSecretsChanged {
return true, nil
for serviceName, hash := range serviceSecretsHash {
if actual.Labels[fmt.Sprintf(api.ServiceSecretsHash, serviceName)] != hash {
return true, nil
}
}

if c.networks != nil && actual.State == "running" {
Expand Down
14 changes: 10 additions & 4 deletions pkg/compose/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,23 +513,29 @@ func (s *composeService) prepareLabels(labels types.Labels, project *types.Proje
return nil, err
}

serviceConfigsHash, err := ServiceConfigsHash(project, service)
serviceNameToConfigHash, err := ServiceConfigsHash(project, service)
if err != nil {
return nil, err
}

serviceSecretsHash, err := ServiceSecretsHash(project, service)
for serviceName, hash := range serviceNameToConfigHash {
labels[fmt.Sprintf(api.ServiceConfigsHash, serviceName)] = hash
}

serviceNameToSecretHash, err := ServiceSecretsHash(project, service)
if err != nil {
return nil, err
}

for serviceName, hash := range serviceNameToSecretHash {
labels[fmt.Sprintf(api.ServiceSecretsHash, serviceName)] = hash
}

if number > 0 {
// One-off containers are not indexed
labels[api.ContainerNumberLabel] = strconv.Itoa(number)
}
labels[api.ConfigHashLabel] = serviceHash
labels[api.ServiceConfigsHash] = serviceConfigsHash
labels[api.ServiceSecretsHash] = serviceSecretsHash
labels[api.ContainerNumberLabel] = strconv.Itoa(number)

var dependencies []string
Expand Down
20 changes: 10 additions & 10 deletions pkg/compose/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,37 +47,37 @@ func ServiceHash(o types.ServiceConfig) (string, error) {
}

// ServiceConfigsHash computes the configuration hash for service configs.
func ServiceConfigsHash(project *types.Project, serviceConfig types.ServiceConfig) (string, error) {
data := make([]byte, 0)
func ServiceConfigsHash(project *types.Project, serviceConfig types.ServiceConfig) (map[string]string, error) {
serviceNameToHash := make(map[string]string)
for _, config := range serviceConfig.Configs {
file := project.Configs[config.Source]
b, err := createTarForConfig(project, types.FileReferenceConfig(config), types.FileObjectConfig(file))

if err != nil {
return "", err
return nil, err
}

data = append(data, b.Bytes()...)
serviceNameToHash[config.Target] = digest.SHA256.FromBytes(b.Bytes()).Encoded()
}

return digest.SHA256.FromBytes(data).Encoded(), nil
return serviceNameToHash, nil
}

// ServiceSecretsHash computes the configuration hash for service secrets.
func ServiceSecretsHash(project *types.Project, serviceConfig types.ServiceConfig) (string, error) {
data := make([]byte, 0)
func ServiceSecretsHash(project *types.Project, serviceConfig types.ServiceConfig) (map[string]string, error) {
serviceNameToHash := make(map[string]string)
for _, secret := range serviceConfig.Secrets {
file := project.Secrets[secret.Source]
b, err := createTarForConfig(project, types.FileReferenceConfig(secret), types.FileObjectConfig(file))

if err != nil {
return "", err
return nil, err
}

data = append(data, b.Bytes()...)
serviceNameToHash[secret.Target] = digest.SHA256.FromBytes(b.Bytes()).Encoded()
}

return digest.SHA256.FromBytes(data).Encoded(), nil
return serviceNameToHash, nil
}

func createTarForConfig(
Expand Down
80 changes: 56 additions & 24 deletions pkg/compose/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,79 +40,111 @@ func TestServiceHashWithIgnorableValues(t *testing.T) {
}

func TestServiceConfigsHashWithoutChangesContent(t *testing.T) {
hash1, err := ServiceConfigsHash(projectWithConfigs("a", "", ""), serviceConfig("myContext1", "always", 1))
serviceNameToConfigHash1, err := ServiceConfigsHash(projectWithConfigs("a", "", ""), serviceConfig("myContext1", "always", 1))
assert.NilError(t, err)
hash2, err := ServiceConfigsHash(projectWithConfigs("a", "", ""), serviceConfig("myContext2", "never", 2))
serviceNameToConfigHas2, err := ServiceConfigsHash(projectWithConfigs("a", "", ""), serviceConfig("myContext2", "never", 2))
assert.NilError(t, err)
assert.Assert(t, hash1 == hash2)
assert.Equal(t, len(serviceNameToConfigHash1), len(serviceNameToConfigHas2))

for serviceName, hash := range serviceNameToConfigHash1 {
assert.Equal(t, hash, serviceNameToConfigHas2[serviceName])
}
}

func TestServiceConfigsHashWithChangedConfigContent(t *testing.T) {
hash1, err := ServiceConfigsHash(projectWithConfigs("a", "", ""), serviceConfig("myContext1", "always", 1))
serviceNameToConfigHash1, err := ServiceConfigsHash(projectWithConfigs("a", "", ""), serviceConfig("myContext1", "always", 1))
assert.NilError(t, err)
hash2, err := ServiceConfigsHash(projectWithConfigs("b", "", ""), serviceConfig("myContext2", "never", 2))
serviceNameToConfigHash2, err := ServiceConfigsHash(projectWithConfigs("b", "", ""), serviceConfig("myContext2", "never", 2))
assert.NilError(t, err)
assert.Assert(t, hash1 != hash2)
assert.Equal(t, len(serviceNameToConfigHash1), len(serviceNameToConfigHash2))

for serviceName, hash := range serviceNameToConfigHash1 {
assert.Assert(t, hash != serviceNameToConfigHash2[serviceName])
}
}

func TestServiceConfigsHashWithChangedConfigEnvironment(t *testing.T) {
hash1, err := ServiceConfigsHash(projectWithConfigs("", "a", ""), serviceConfig("myContext1", "always", 1))
serviceNameToConfigHash1, err := ServiceConfigsHash(projectWithConfigs("", "a", ""), serviceConfig("myContext1", "always", 1))
assert.NilError(t, err)
hash2, err := ServiceConfigsHash(projectWithConfigs("", "b", ""), serviceConfig("myContext2", "never", 2))
serviceNameToConfigHash2, err := ServiceConfigsHash(projectWithConfigs("", "b", ""), serviceConfig("myContext2", "never", 2))
assert.NilError(t, err)
assert.Assert(t, hash1 != hash2)
assert.Equal(t, len(serviceNameToConfigHash1), len(serviceNameToConfigHash2))

for serviceName, hash := range serviceNameToConfigHash1 {
assert.Assert(t, hash != serviceNameToConfigHash2[serviceName])
}
}

func TestServiceConfigsHashWithChangedConfigFile(t *testing.T) {
hash1, err := ServiceConfigsHash(
serviceNameToConfigHash1, err := ServiceConfigsHash(
projectWithConfigs("", "", "./testdata/config1.txt"),
serviceConfig("myContext1", "always", 1),
)
assert.NilError(t, err)
hash2, err := ServiceConfigsHash(
serviceNameToConfigHash2, err := ServiceConfigsHash(
projectWithConfigs("", "", "./testdata/config2.txt"),
serviceConfig("myContext2", "never", 2),
)
assert.NilError(t, err)
assert.Assert(t, hash1 != hash2)
assert.Equal(t, len(serviceNameToConfigHash1), len(serviceNameToConfigHash2))

for serviceName, hash := range serviceNameToConfigHash1 {
assert.Assert(t, hash != serviceNameToConfigHash2[serviceName])
}
}

func TestServiceSecretsHashWithoutChangesContent(t *testing.T) {
hash1, err := ServiceSecretsHash(projectWithSecrets("a", "", ""), serviceConfig("myContext1", "always", 1))
serviceNameToSecretHash1, err := ServiceSecretsHash(projectWithSecrets("a", "", ""), serviceConfig("myContext1", "always", 1))
assert.NilError(t, err)
hash2, err := ServiceSecretsHash(projectWithSecrets("a", "", ""), serviceConfig("myContext2", "never", 2))
serviceNameToSecretHash2, err := ServiceSecretsHash(projectWithSecrets("a", "", ""), serviceConfig("myContext2", "never", 2))
assert.NilError(t, err)
assert.Assert(t, hash1 == hash2)
assert.Equal(t, len(serviceNameToSecretHash1), len(serviceNameToSecretHash2))

for serviceName, hash := range serviceNameToSecretHash1 {
assert.Equal(t, hash, serviceNameToSecretHash2[serviceName])
}
}

func TestServiceSecretsHashWithChangedSecretContent(t *testing.T) {
hash1, err := ServiceSecretsHash(projectWithSecrets("a", "", ""), serviceConfig("myContext1", "always", 1))
serviceNameToSecretHash1, err := ServiceSecretsHash(projectWithSecrets("a", "", ""), serviceConfig("myContext1", "always", 1))
assert.NilError(t, err)
hash2, err := ServiceSecretsHash(projectWithSecrets("b", "", ""), serviceConfig("myContext2", "never", 2))
serviceNameToSecretHash2, err := ServiceSecretsHash(projectWithSecrets("b", "", ""), serviceConfig("myContext2", "never", 2))
assert.NilError(t, err)
assert.Assert(t, hash1 != hash2)
assert.Equal(t, len(serviceNameToSecretHash1), len(serviceNameToSecretHash2))

for serviceName, hash := range serviceNameToSecretHash1 {
assert.Assert(t, hash != serviceNameToSecretHash2[serviceName])
}
}

func TestServiceSecretsHashWithChangedSecretEnvironment(t *testing.T) {
hash1, err := ServiceSecretsHash(projectWithSecrets("", "a", ""), serviceConfig("myContext1", "always", 1))
serviceNameToSecretHash1, err := ServiceSecretsHash(projectWithSecrets("", "a", ""), serviceConfig("myContext1", "always", 1))
assert.NilError(t, err)
hash2, err := ServiceSecretsHash(projectWithSecrets("", "b", ""), serviceConfig("myContext2", "never", 2))
serviceNameToSecretHash2, err := ServiceSecretsHash(projectWithSecrets("", "b", ""), serviceConfig("myContext2", "never", 2))
assert.NilError(t, err)
assert.Assert(t, hash1 != hash2)
assert.Equal(t, len(serviceNameToSecretHash1), len(serviceNameToSecretHash2))

for serviceName, hash := range serviceNameToSecretHash1 {
assert.Assert(t, hash != serviceNameToSecretHash2[serviceName])
}
}

func TestServiceSecretsHashWithChangedSecretFile(t *testing.T) {
hash1, err := ServiceSecretsHash(
serviceNameToSecretHash1, err := ServiceSecretsHash(
projectWithSecrets("", "", "./testdata/config1.txt"),
serviceConfig("myContext1", "always", 1),
)
assert.NilError(t, err)
hash2, err := ServiceSecretsHash(
serviceNameToSecretHash2, err := ServiceSecretsHash(
projectWithSecrets("", "", "./testdata/config2.txt"),
serviceConfig("myContext2", "never", 2),
)
assert.NilError(t, err)
assert.Assert(t, hash1 != hash2)
assert.Equal(t, len(serviceNameToSecretHash1), len(serviceNameToSecretHash2))

for serviceName, hash := range serviceNameToSecretHash1 {
assert.Assert(t, hash != serviceNameToSecretHash2[serviceName])
}
}

func projectWithConfigs(configContent, configEnvironmentValue, configFile string) *types.Project {
Expand Down

0 comments on commit f97276d

Please sign in to comment.