Skip to content

Commit f547d5e

Browse files
committed
Order branches and commits where loaded, remove marshal overrides
1 parent 472c375 commit f547d5e

File tree

5 files changed

+145
-4
lines changed

5 files changed

+145
-4
lines changed

pkg/api/secretbootstrap/secretboostrap.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"reflect"
8+
"sort"
89
"strings"
910

1011
"github.com/getlantern/deepcopy"
@@ -61,6 +62,41 @@ type SecretConfig struct {
6162
To []SecretContext `json:"to"`
6263
}
6364

65+
// orderSecretConfig sorts the SecretConfig data structures for deterministic ordering
66+
func (s *SecretConfig) orderSecretConfig() {
67+
// Sort From map keys and DockerConfigJSONData slices
68+
sortedFrom := make(map[string]ItemContext)
69+
keys := make([]string, 0, len(s.From))
70+
for k := range s.From {
71+
keys = append(keys, k)
72+
}
73+
sort.Strings(keys)
74+
75+
for _, k := range keys {
76+
itemCtx := s.From[k]
77+
// Sort DockerConfigJSONData slice
78+
dockerData := make([]DockerConfigJSONData, len(itemCtx.DockerConfigJSONData))
79+
copy(dockerData, itemCtx.DockerConfigJSONData)
80+
sort.Slice(dockerData, func(i, j int) bool {
81+
if dockerData[i].RegistryURL != dockerData[j].RegistryURL {
82+
return dockerData[i].RegistryURL < dockerData[j].RegistryURL
83+
}
84+
if dockerData[i].Item != dockerData[j].Item {
85+
return dockerData[i].Item < dockerData[j].Item
86+
}
87+
return dockerData[i].AuthField < dockerData[j].AuthField
88+
})
89+
90+
sortedFrom[k] = ItemContext{
91+
Item: itemCtx.Item,
92+
Field: itemCtx.Field,
93+
DockerConfigJSONData: dockerData,
94+
Base64Decode: itemCtx.Base64Decode,
95+
}
96+
}
97+
s.From = sortedFrom
98+
}
99+
64100
// LoadConfigFromFile renders a Config object loaded from the given file
65101
func LoadConfigFromFile(file string, config *Config) error {
66102
bytes, err := gzip.ReadFileMaybeGZIP(file)
@@ -79,6 +115,56 @@ func SaveConfigToFile(file string, config *Config) error {
79115
return os.WriteFile(file, bytes, 0644)
80116
}
81117

118+
// orderConfig sorts the Config data structures for deterministic ordering
119+
func (c *Config) orderConfig() {
120+
// Sort ClusterGroups map keys
121+
sortedClusterGroups := make(map[string][]string)
122+
clusterGroupKeys := make([]string, 0, len(c.ClusterGroups))
123+
for k := range c.ClusterGroups {
124+
clusterGroupKeys = append(clusterGroupKeys, k)
125+
}
126+
sort.Strings(clusterGroupKeys)
127+
for _, k := range clusterGroupKeys {
128+
clusters := make([]string, len(c.ClusterGroups[k]))
129+
copy(clusters, c.ClusterGroups[k])
130+
sort.Strings(clusters)
131+
sortedClusterGroups[k] = clusters
132+
}
133+
c.ClusterGroups = sortedClusterGroups
134+
135+
// Sort Secrets slice
136+
sort.Slice(c.Secrets, func(i, j int) bool {
137+
// Sort by first To entry's Cluster, then Namespace, then Name
138+
// This groups secrets by cluster, making the output more organized
139+
if len(c.Secrets[i].To) == 0 && len(c.Secrets[j].To) == 0 {
140+
return false
141+
}
142+
if len(c.Secrets[i].To) == 0 {
143+
return true
144+
}
145+
if len(c.Secrets[j].To) == 0 {
146+
return false
147+
}
148+
toI := c.Secrets[i].To[0]
149+
toJ := c.Secrets[j].To[0]
150+
if toI.Cluster != toJ.Cluster {
151+
return toI.Cluster < toJ.Cluster
152+
}
153+
if toI.Namespace != toJ.Namespace {
154+
return toI.Namespace < toJ.Namespace
155+
}
156+
return toI.Name < toJ.Name
157+
})
158+
159+
// Sort UserSecretsTargetClusters
160+
sort.Strings(c.UserSecretsTargetClusters)
161+
162+
// Order each SecretConfig
163+
for i := range c.Secrets {
164+
c.Secrets[i].orderSecretConfig()
165+
}
166+
}
167+
82168
// Config is what we version in our repository
83169
type Config struct {
84170
VaultDPTPPrefix string `json:"vault_dptp_prefix,omitempty"`
@@ -237,6 +323,9 @@ func (c *Config) resolve() error {
237323

238324
}
239325

326+
// Order the config data structures for deterministic output
327+
c.orderConfig()
328+
240329
return utilerrors.NewAggregate(errs)
241330
}
242331

pkg/clusterinit/onboard/manifestgenerator.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,17 @@ func (w *manifestGeneratorStep) marshalManifests(manifests []interface{}, patche
127127
return nil, err
128128
}
129129

130+
// Fix YAML scalar style for monitoring config: change trailing newline handling
131+
// yaml.v2 doesn't support controlling scalar style, so we post-process it.
132+
// We change "config.yaml: |-" (literal block scalar that strips trailing newline) to
133+
// "config.yaml: |" (literal block scalar that preserves trailing newline).
134+
// This is safe and targeted: only runs for the cluster-monitoring-config ConfigMap.
135+
if kind, ok := manifestMap["kind"].(string); ok && kind == "ConfigMap" {
136+
if name, ok := manifestMap["metadata"].(map[string]interface{})["name"].(string); ok && name == "cluster-monitoring-config" {
137+
manifestBytesPatched = bytes.ReplaceAll(manifestBytesPatched, []byte("config.yaml: |-"), []byte("config.yaml: |"))
138+
}
139+
}
140+
130141
manifestsBytes = append(manifestsBytes, manifestBytesPatched)
131142
}
132143

pkg/image-graph-generator/images.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"regexp"
7+
"sort"
78

89
"github.com/sirupsen/logrus"
910

@@ -84,6 +85,16 @@ func (o *Operator) UpdateImage(image api.ProjectDirectoryImageBuildStepConfigura
8485
}
8586
}
8687

88+
// Sort Parents by Name for deterministic ordering
89+
sort.Slice(imageRef.Parents, func(i, j int) bool {
90+
return imageRef.Parents[i].Name < imageRef.Parents[j].Name
91+
})
92+
93+
// Sort Branches by ID for deterministic ordering
94+
sort.Slice(imageRef.Branches, func(i, j int) bool {
95+
return imageRef.Branches[i].ID < imageRef.Branches[j].ID
96+
})
97+
8798
if id, ok := o.images[imageName]; ok {
8899
if err := o.updateImageRef(imageRef, id); err != nil {
89100
return err
@@ -113,13 +124,26 @@ func (o *Operator) addImageRef(image *ImageRef) error {
113124
}
114125

115126
if len(image.Branches) > 0 {
127+
// Sort Branches by ID for deterministic ordering
128+
sortedBranches := make([]BranchRef, len(image.Branches))
129+
copy(sortedBranches, image.Branches)
130+
sort.Slice(sortedBranches, func(i, j int) bool {
131+
return sortedBranches[i].ID < sortedBranches[j].ID
132+
})
116133
input["branches"] = map[string]interface{}{
117-
"id": image.Branches[0].ID,
134+
"id": sortedBranches[0].ID,
118135
}
119136
}
120137

138+
// Sort Parents by Name for deterministic ordering
139+
sortedParents := make([]ImageRef, len(image.Parents))
140+
copy(sortedParents, image.Parents)
141+
sort.Slice(sortedParents, func(i, j int) bool {
142+
return sortedParents[i].Name < sortedParents[j].Name
143+
})
144+
121145
var parents []interface{}
122-
for _, parent := range image.Parents {
146+
for _, parent := range sortedParents {
123147
p := map[string]interface{}{
124148
"name": parent.Name,
125149
"imageStreamRef": parent.ImageStreamRef,
@@ -177,13 +201,24 @@ func (o *Operator) updateImageRef(newImage *ImageRef, id string) error {
177201
patch["source"] = newImage.Source
178202
}
179203
if len(newImage.Branches) > 0 {
204+
// Sort Branches by ID for deterministic ordering
205+
sort.Slice(newImage.Branches, func(i, j int) bool {
206+
return newImage.Branches[i].ID < newImage.Branches[j].ID
207+
})
180208
patch["branches"] = map[string]interface{}{
181209
"id": newImage.Branches[0].ID,
182210
}
183211
}
184212

213+
// Sort Parents by Name for deterministic ordering
214+
sortedParents := make([]ImageRef, len(newImage.Parents))
215+
copy(sortedParents, newImage.Parents)
216+
sort.Slice(sortedParents, func(i, j int) bool {
217+
return sortedParents[i].Name < sortedParents[j].Name
218+
})
219+
185220
var parents []interface{}
186-
for _, parent := range newImage.Parents {
221+
for _, parent := range sortedParents {
187222
p := map[string]interface{}{
188223
"name": parent.Name,
189224
"imageStreamRef": parent.ImageStreamRef,

pkg/image-graph-generator/manifests.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"sort"
78
"strings"
89

910
corev1 "k8s.io/api/core/v1"
@@ -157,6 +158,11 @@ func (o *Operator) AddManifestImages() error {
157158
}
158159
}
159160

161+
// Sort Parents by Name for deterministic ordering
162+
sort.Slice(imageRef.Parents, func(i, j int) bool {
163+
return imageRef.Parents[i].Name < imageRef.Parents[j].Name
164+
})
165+
160166
if id, ok := o.images[name]; !ok {
161167
if err := o.addImageRef(imageRef); err != nil {
162168
return err

test/integration/cluster-init/update-build99/expected/clusters/build-clusters/build99/openshift-monitoring/cluster-monitoring-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
apiVersion: v1
88
data:
9-
config.yaml: |-
9+
config.yaml: |
1010
alertmanagerMain:
1111
nodeSelector:
1212
node-role.kubernetes.io/infra: ""

0 commit comments

Comments
 (0)