Skip to content

Commit c676c75

Browse files
committed
formatter: Sort labels for stable output
Several Labels() methods iterated over maps without sorting, producing non-deterministic output. In early versions of Go, map iteration order happened to be stable in practice, so the original code appeared to work correctly. Since Go 1.12, the runtime intentionally randomizes map iteration order, making the output unpredictable between runs. The API response produces sorted labels and the container formatter already sorted its labels (changed in 5ee17ee). Apply the same fix to the volume, network, config, and secret formatters, and update tests to assert exact ordering instead of using order-independent comparison. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
1 parent 977ee83 commit c676c75

6 files changed

Lines changed: 13 additions & 6 deletions

File tree

cli/command/config/formatter.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package config
22

33
import (
44
"fmt"
5+
"sort"
56
"strings"
67
"time"
78

@@ -102,6 +103,7 @@ func (c *configContext) Labels() string {
102103
for k, v := range mapLabels {
103104
joinLabels = append(joinLabels, k+"="+v)
104105
}
106+
sort.Strings(joinLabels)
105107
return strings.Join(joinLabels, ",")
106108
}
107109

cli/command/formatter/volume.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
2+
//go:build go1.25
3+
14
package formatter
25

36
import (
47
"fmt"
8+
"sort"
59
"strconv"
610
"strings"
711

@@ -104,6 +108,7 @@ func (c *volumeContext) Labels() string {
104108
for k, v := range c.v.Labels {
105109
joinLabels = append(joinLabels, k+"="+v)
106110
}
111+
sort.Strings(joinLabels)
107112
return strings.Join(joinLabels, ",")
108113
}
109114

cli/command/formatter/volume_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ func TestVolumeContext(t *testing.T) {
4848
for _, c := range cases {
4949
ctx = c.volumeCtx
5050
v := c.call()
51-
if strings.Contains(v, ",") {
52-
test.CompareMultipleValues(t, v, c.expValue)
53-
} else if v != c.expValue {
51+
if v != c.expValue {
5452
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
5553
}
5654
}

cli/command/network/formatter.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package network
22

33
import (
4+
"sort"
45
"strconv"
56
"strings"
67

@@ -115,6 +116,7 @@ func (c *networkContext) Labels() string {
115116
for k, v := range c.n.Labels {
116117
joinLabels = append(joinLabels, k+"="+v)
117118
}
119+
sort.Strings(joinLabels)
118120
return strings.Join(joinLabels, ",")
119121
}
120122

cli/command/network/formatter_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ func TestNetworkContext(t *testing.T) {
6868
for _, c := range cases {
6969
ctx = c.networkCtx
7070
v := c.call()
71-
if strings.Contains(v, ",") {
72-
test.CompareMultipleValues(t, v, c.expValue)
73-
} else if v != c.expValue {
71+
if v != c.expValue {
7472
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
7573
}
7674
}

cli/command/secret/formatter.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package secret
22

33
import (
44
"fmt"
5+
"sort"
56
"strings"
67
"time"
78

@@ -109,6 +110,7 @@ func (c *secretContext) Labels() string {
109110
for k, v := range mapLabels {
110111
joinLabels = append(joinLabels, k+"="+v)
111112
}
113+
sort.Strings(joinLabels)
112114
return strings.Join(joinLabels, ",")
113115
}
114116

0 commit comments

Comments
 (0)