Skip to content

Commit 4577fce

Browse files
DavidHurtaingvagabund
authored andcommitted
lib/resourcebuilder/core.go: Error out on any error during TLS injection
And some minor modifications here and there. Preferably to be squashed before merging.
1 parent b0ab4f6 commit 4577fce

1 file changed

Lines changed: 64 additions & 94 deletions

File tree

lib/resourcebuilder/core.go

Lines changed: 64 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package resourcebuilder
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
6-
"slices"
77
"sort"
88

99
"sigs.k8s.io/kustomize/kyaml/yaml"
@@ -17,6 +17,7 @@ import (
1717
"k8s.io/utils/clock"
1818

1919
configv1 "github.com/openshift/api/config/v1"
20+
operatorv1alpha1 "github.com/openshift/api/operator/v1alpha1"
2021
configclientv1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
2122
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1"
2223
"github.com/openshift/library-go/pkg/operator/configobserver/apiserver"
@@ -29,6 +30,16 @@ const (
2930
ConfigMapInjectTLSAnnotation = "config.openshift.io/inject-tls"
3031
)
3132

33+
type optional[T any] struct {
34+
value T
35+
found bool
36+
}
37+
38+
type tlsConfig struct {
39+
minTLSVersion optional[string]
40+
cipherSuites optional[[]string]
41+
}
42+
3243
func (b *builder) modifyConfigMap(ctx context.Context, cm *corev1.ConfigMap) error {
3344
// Check for TLS injection annotation
3445
if value, ok := cm.Annotations[ConfigMapInjectTLSAnnotation]; !ok || value != "true" {
@@ -39,21 +50,26 @@ func (b *builder) modifyConfigMap(ctx context.Context, cm *corev1.ConfigMap) err
3950

4051
// Empty data, nothing to inject into
4152
if cm.Data == nil {
42-
klog.V(2).Infof("ConfigMap %s/%s has empty data, skipping", cm.Namespace, cm.Name)
53+
klog.V(2).Infof("ConfigMap %s/%s has empty data, skipping TLS profile injection", cm.Namespace, cm.Name)
4354
return nil
4455
}
4556

4657
// Observe TLS configuration from APIServer
47-
minTLSVersion, minTLSFound, cipherSuites, ciphersFound, err := b.observeTLSConfiguration(ctx, cm)
58+
tlsConf, err := b.observeTLSConfiguration(ctx, cm)
4859
if err != nil {
4960
return fmt.Errorf("unable to observe TLS configuration: %v", err)
5061
}
51-
if !minTLSFound && !ciphersFound {
52-
klog.V(2).Infof("ConfigMap %s/%s: no TLS configuration found, skipping", cm.Namespace, cm.Name)
53-
return nil
54-
}
5562

56-
klog.V(4).Infof("Observing minTLSVersion=%v, cipherSuites=%v", minTLSVersion, cipherSuites)
63+
minTLSLog := "<not found>"
64+
if tlsConf.minTLSVersion.found {
65+
minTLSLog = tlsConf.minTLSVersion.value
66+
}
67+
cipherSuitesLog := "<not found>"
68+
if tlsConf.cipherSuites.found {
69+
cipherSuitesLog = fmt.Sprintf("%v", tlsConf.cipherSuites.value)
70+
}
71+
klog.V(4).Infof("ConfigMap %s/%s: observed minTLSVersion=%v, cipherSuites=%v",
72+
cm.Namespace, cm.Name, minTLSLog, cipherSuitesLog)
5773

5874
// Process each data entry that contains GenericOperatorConfig
5975
for key, value := range cm.Data {
@@ -66,43 +82,35 @@ func (b *builder) modifyConfigMap(ctx context.Context, cm *corev1.ConfigMap) err
6682
continue
6783
}
6884

69-
// Check if this is a GenericOperatorConfig by checking the kind field
70-
kind := rnode.GetKind()
71-
if kind != "GenericOperatorConfig" {
72-
klog.V(4).Infof("ConfigMap's %q entry is not a GenericOperatorConfig, skipping this entry", key)
85+
// Check if this is a supported GenericOperatorConfig kind
86+
if rnode.GetKind() != "GenericOperatorConfig" || rnode.GetApiVersion() != operatorv1alpha1.GroupVersion.String() {
87+
klog.V(4).Infof("ConfigMap's %q entry is not a supported GenericOperatorConfig, skipping this entry", key)
7388
continue
7489
}
7590

7691
klog.V(2).Infof("ConfigMap %s/%s processing GenericOperatorConfig in key %s", cm.Namespace, cm.Name, key)
7792

7893
// Inject TLS settings into the GenericOperatorConfig while preserving structure
79-
if err := updateRNodeWithTLSSettings(rnode, minTLSVersion, minTLSFound, cipherSuites, ciphersFound); err != nil {
80-
klog.V(4).Infof("Error injecting the TLS configuration: %v", err)
81-
return err
94+
if err := updateRNodeWithTLSSettings(rnode, tlsConf); err != nil {
95+
return fmt.Errorf("failed to inject the TLS configuration: %v", err)
8296
}
8397

8498
// Marshal the modified RNode back to YAML
8599
modifiedYAML, err := rnode.String()
86100
if err != nil {
87-
klog.V(4).Infof("Error marshalling the modified ConfigMap back to YAML: %v", err)
88-
return err
101+
return fmt.Errorf("failed to marshall the modified ConfigMap back to YAML: %v", err)
89102
}
90103

91104
// Update the ConfigMap data entry with the modified YAML
92105
cm.Data[key] = modifiedYAML
93-
klog.V(2).Infof("ConfigMap %s/%s updated GenericOperatorConfig in key %s with %d ciphers and minTLSVersion=%s",
94-
cm.Namespace, cm.Name, key, len(cipherSuites), minTLSVersion)
106+
klog.V(2).Infof("ConfigMap %s/%s updated GenericOperatorConfig with TLS profile in key %s", cm.Namespace, cm.Name, key)
95107
}
96-
97-
klog.V(2).Infof("APIServer config available for ConfigMap %s/%s TLS injection", cm.Namespace, cm.Name)
98-
99108
return nil
100109
}
101110

102111
// observeTLSConfiguration retrieves TLS configuration from the APIServer cluster CR
103112
// using ObserveTLSSecurityProfile and extracts minTLSVersion and cipherSuites.
104-
// minTLSVersion string, minTLSFound bool, cipherSuites []string, ciphersFound bool, err error
105-
func (b *builder) observeTLSConfiguration(ctx context.Context, cm *corev1.ConfigMap) (string, bool, []string, bool, error) {
113+
func (b *builder) observeTLSConfiguration(ctx context.Context, cm *corev1.ConfigMap) (*tlsConfig, error) {
106114
// Create a lister adapter for ObserveTLSSecurityProfile
107115
lister := &apiServerListerAdapter{
108116
client: b.configClientv1.APIServers(),
@@ -118,100 +126,62 @@ func (b *builder) observeTLSConfiguration(ctx context.Context, cm *corev1.Config
118126
// Call ObserveTLSSecurityProfile to get TLS configuration
119127
observedConfig, errs := apiserver.ObserveTLSSecurityProfile(listers, recorder, map[string]any{})
120128
if len(errs) > 0 {
121-
// Log errors but continue - ObserveTLSSecurityProfile is tolerant of missing config
122-
for _, err := range errs {
123-
klog.Errorf("ConfigMap %s/%s: error observing TLS profile: %v", cm.Namespace, cm.Name, err)
124-
}
129+
return nil, fmt.Errorf("error observing TLS profile for ConfigMap %s/%s: %w", cm.Namespace, cm.Name, errors.Join(errs...))
125130
}
126131

127-
// Extract the TLS settings from the observed config
128-
minTLSVersion, minTLSFound, err := unstructured.NestedString(observedConfig, "servingInfo", "minTLSVersion")
129-
if err != nil {
130-
// This error is unlikely to happen unless unstructured.NestedString is buggy.
131-
// From unstructured.NestedString's description:
132-
// "Returns false if value is not found and an error if not a string."
133-
// The observedConfig's servingInfo.minTLSVersion is of a string type
134-
return "", false, nil, false, err
135-
}
136-
cipherSuites, ciphersFound, _ := unstructured.NestedStringSlice(observedConfig, "servingInfo", "cipherSuites")
137-
if err != nil {
138-
// This error is unlikely to happen unless unstructured.NestedStringSlice is buggy
139-
// From unstructured.NestedString's description:
140-
// "Returns false if value is not found and an error if not a []interface{} or contains non-string items in the slice."
141-
// The observedConfig's servingInfo.minTLSVersion is of a string type
142-
return "", false, nil, false, err
132+
config := &tlsConfig{}
133+
134+
// Extract minTLSVersion from the observed config
135+
if minTLSVersion, minTLSFound, err := unstructured.NestedString(observedConfig, "servingInfo", "minTLSVersion"); err != nil {
136+
return nil, err
137+
} else if minTLSFound {
138+
config.minTLSVersion = optional[string]{value: minTLSVersion, found: true}
143139
}
144140

145-
// Sort cipher suites for consistent comparison
146-
if ciphersFound && len(cipherSuites) > 0 {
141+
// Extract cipherSuites from the observed config
142+
if cipherSuites, ciphersFound, err := unstructured.NestedStringSlice(observedConfig, "servingInfo", "cipherSuites"); err != nil {
143+
return nil, err
144+
} else if ciphersFound {
145+
// Sort cipher suites for consistent ordering
147146
sort.Strings(cipherSuites)
147+
config.cipherSuites = optional[[]string]{value: cipherSuites, found: true}
148148
}
149149

150-
return minTLSVersion, minTLSFound, cipherSuites, ciphersFound, nil
150+
return config, nil
151151
}
152152

153-
// updateRNodeWithTLSSettings injects TLS settings into a GenericOperatorConfig RNode while preserving structure
154-
// cipherSuites is expected to be sorted
155-
func updateRNodeWithTLSSettings(rnode *yaml.RNode, minTLSVersion string, minTLSFound bool, cipherSuites []string, ciphersFound bool) error {
153+
// updateRNodeWithTLSSettings injects TLS settings into a GenericOperatorConfig RNode while preserving structure.
154+
// If a field in tlsConf is not found, the corresponding field will be deleted from the RNode.
155+
func updateRNodeWithTLSSettings(rnode *yaml.RNode, tlsConf *tlsConfig) error {
156156
servingInfo, err := rnode.Pipe(yaml.LookupCreate(yaml.MappingNode, "servingInfo"))
157157
if err != nil {
158158
return err
159159
}
160160

161-
if ciphersFound {
162-
currentCiphers, err := getSortedCipherSuites(servingInfo)
163-
if err != nil {
161+
// Handle cipherSuites field
162+
if tlsConf.cipherSuites.found {
163+
seqNode := yaml.NewListRNode(tlsConf.cipherSuites.value...)
164+
if err := servingInfo.PipeE(yaml.SetField("cipherSuites", seqNode)); err != nil {
164165
return err
165166
}
166-
if !slices.Equal(currentCiphers, cipherSuites) {
167-
// Create a sequence node with the cipher suites
168-
seqNode := yaml.NewListRNode(cipherSuites...)
169-
if err := servingInfo.PipeE(yaml.SetField("cipherSuites", seqNode)); err != nil {
170-
return err
171-
}
167+
} else {
168+
if err := servingInfo.PipeE(yaml.Clear("cipherSuites")); err != nil {
169+
return err
172170
}
173171
}
174172

175-
// Update minTLSVersion if found
176-
if minTLSFound {
177-
if err := servingInfo.PipeE(yaml.SetField("minTLSVersion", yaml.NewStringRNode(minTLSVersion))); err != nil {
173+
// Handle minTLSVersion field
174+
if tlsConf.minTLSVersion.found {
175+
if err := servingInfo.PipeE(yaml.SetField("minTLSVersion", yaml.NewStringRNode(tlsConf.minTLSVersion.value))); err != nil {
178176
return err
179177
}
180-
}
181-
182-
return nil
183-
}
184-
185-
// getSortedCipherSuites extracts and sorts the cipherSuites string slice from a servingInfo RNode
186-
func getSortedCipherSuites(servingInfo *yaml.RNode) ([]string, error) {
187-
ciphersNode, err := servingInfo.Pipe(yaml.Lookup("cipherSuites"))
188-
if err != nil || ciphersNode == nil {
189-
return nil, err
190-
}
191-
192-
elements, err := ciphersNode.Elements()
193-
if err != nil {
194-
return nil, err
195-
}
196-
197-
var ciphers []string
198-
for _, elem := range elements {
199-
// For scalar nodes, access the value directly without YAML serialization
200-
// This avoids the trailing newline that String() (which uses yaml.Encode) adds
201-
if elem.YNode().Kind == yaml.ScalarNode {
202-
value := elem.YNode().Value
203-
// Skip empty values
204-
if value == "" {
205-
continue
206-
}
207-
ciphers = append(ciphers, value)
178+
} else {
179+
if err := servingInfo.PipeE(yaml.Clear("minTLSVersion")); err != nil {
180+
return err
208181
}
209182
}
210183

211-
// Sort cipher suites for consistent comparison
212-
sort.Strings(ciphers)
213-
214-
return ciphers, nil
184+
return nil
215185
}
216186

217187
// apiServerListerAdapter adapts a client interface to the lister interface

0 commit comments

Comments
 (0)