Skip to content

Commit cde631b

Browse files
committed
OCPBUGS-62285: Add APIServer CR configobserver and pull TLS configuration from it. Assisted by Claude (Sonnet 4.5).
1 parent 032e38a commit cde631b

6 files changed

Lines changed: 495 additions & 32 deletions

File tree

pkg/cmd/checkendpoints/cmd.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@ import (
55
"os"
66
"time"
77

8+
operatorv1 "github.com/openshift/api/operator/v1"
9+
configclient "github.com/openshift/client-go/config/clientset/versioned"
10+
configinformers "github.com/openshift/client-go/config/informers/externalversions"
811
operatorcontrolplaneclient "github.com/openshift/client-go/operatorcontrolplane/clientset/versioned"
912
operatorcontrolplaneinformers "github.com/openshift/client-go/operatorcontrolplane/informers/externalversions"
1013
"github.com/openshift/cluster-network-operator/pkg/cmd/checkendpoints/controller"
1114
"github.com/openshift/cluster-network-operator/pkg/version"
1215
"github.com/openshift/library-go/pkg/controller/controllercmd"
1316
"github.com/openshift/library-go/pkg/operator/events"
1417
"github.com/openshift/library-go/pkg/operator/resource/retry"
18+
"github.com/openshift/library-go/pkg/operator/resourcesynccontroller"
19+
"github.com/openshift/library-go/pkg/operator/v1helpers"
1520
"github.com/spf13/cobra"
1621
corev1 "k8s.io/api/core/v1"
1722
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
@@ -29,9 +34,11 @@ func NewCheckEndpointsCommand() *cobra.Command {
2934
kubeClient := kubernetes.NewForConfigOrDie(cctx.ProtoKubeConfig)
3035
apiextensionsClient := apiextensionsclient.NewForConfigOrDie(cctx.KubeConfig)
3136
operatorcontrolplaneClient := operatorcontrolplaneclient.NewForConfigOrDie(cctx.KubeConfig)
37+
configClient := configclient.NewForConfigOrDie(cctx.KubeConfig)
3238
kubeInformers := informers.NewSharedInformerFactoryWithOptions(kubeClient, 10*time.Minute, informers.WithNamespace(namespace))
3339
operatorcontrolplaneInformers := operatorcontrolplaneinformers.NewSharedInformerFactoryWithOptions(operatorcontrolplaneClient, 10*time.Minute, operatorcontrolplaneinformers.WithNamespace(namespace))
3440
apiextensionsInformers := apiextensionsinformers.NewSharedInformerFactory(apiextensionsClient, 10*time.Minute)
41+
configInformers := configinformers.NewSharedInformerFactory(configClient, 10*time.Minute)
3542

3643
// create a recorder that sets the pod node as the involved object in events
3744
var involvedObjectRef *corev1.ObjectReference
@@ -58,12 +65,48 @@ func NewCheckEndpointsCommand() *cobra.Command {
5865
}
5966
recorder := events.NewRecorder(kubeClient.CoreV1().Events(namespace), "check-endpoint", involvedObjectRef, clock.RealClock{})
6067

68+
// Create a no-op operator client for the config observer
69+
operatorClient := v1helpers.NewFakeOperatorClient(
70+
&operatorv1.OperatorSpec{},
71+
&operatorv1.OperatorStatus{},
72+
nil,
73+
)
74+
75+
// Create KubeInformersForNamespaces for the resource syncer
76+
kubeInformersForNamespaces := v1helpers.NewKubeInformersForNamespaces(kubeClient, namespace)
77+
78+
// Create a no-op resource syncer
79+
resourceSyncer := resourcesynccontroller.NewResourceSyncController(
80+
"network-check-source",
81+
operatorClient,
82+
kubeInformersForNamespaces,
83+
kubeClient.CoreV1(),
84+
kubeClient.CoreV1(),
85+
recorder,
86+
)
87+
88+
// Create the APIServer config observer controller
89+
apiServerConfigObserver, err := controller.NewAPIServerConfigObserverController(
90+
operatorClient,
91+
configInformers.Config().V1().APIServers(),
92+
resourceSyncer,
93+
recorder,
94+
)
95+
if err != nil {
96+
return err
97+
}
98+
99+
// Create TLS config provider from the config observer
100+
tlsConfigProvider := controller.NewTLSConfigProvider(apiServerConfigObserver)
101+
102+
// Create the network connectivity check controller with TLS config provider
61103
check := controller.NewPodNetworkConnectivityCheckController(
62104
podName,
63105
namespace,
64106
operatorcontrolplaneClient.ControlplaneV1alpha1(),
65107
operatorcontrolplaneInformers.Controlplane().V1alpha1().PodNetworkConnectivityChecks(),
66108
kubeInformers.Core().V1().Secrets(),
109+
tlsConfigProvider,
67110
recorder,
68111
)
69112

@@ -95,9 +138,11 @@ func NewCheckEndpointsCommand() *cobra.Command {
95138
}
96139

97140
// continue startup
141+
configInformers.Start(ctx.Done())
98142
operatorcontrolplaneInformers.Start(ctx.Done())
99143
kubeInformers.Start(ctx.Done())
100144
go check.Run(ctx, 1)
145+
go apiServerConfigObserver.Run(ctx, 1)
101146
go stopController.Run(ctx, 1)
102147
<-ctx.Done()
103148
return nil
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package controller
2+
3+
import (
4+
"fmt"
5+
6+
configv1 "github.com/openshift/api/config/v1"
7+
configinformers "github.com/openshift/client-go/config/informers/externalversions/config/v1"
8+
configlisters "github.com/openshift/client-go/config/listers/config/v1"
9+
"github.com/openshift/library-go/pkg/controller/factory"
10+
"github.com/openshift/library-go/pkg/operator/configobserver"
11+
apiserver "github.com/openshift/library-go/pkg/operator/configobserver/apiserver"
12+
"github.com/openshift/library-go/pkg/operator/events"
13+
"github.com/openshift/library-go/pkg/operator/resourcesynccontroller"
14+
"github.com/openshift/library-go/pkg/operator/v1helpers"
15+
"k8s.io/client-go/tools/cache"
16+
)
17+
18+
// APIServerConfigObserverController observes the APIServer CR and extracts
19+
// TLS security profile configuration for use by the network check source.
20+
type APIServerConfigObserverController struct {
21+
factory.Controller
22+
apiServerLister configlisters.APIServerLister
23+
}
24+
25+
// Listers implements configobserver.Listers and apiserver.APIServerLister
26+
// to provide access to the resource syncer and APIServer lister
27+
type Listers struct {
28+
apiServerLister configlisters.APIServerLister
29+
resourceSyncer resourcesynccontroller.ResourceSyncer
30+
}
31+
32+
func (l Listers) APIServerLister() configlisters.APIServerLister {
33+
return l.apiServerLister
34+
}
35+
36+
func (l Listers) ResourceSyncer() resourcesynccontroller.ResourceSyncer {
37+
return l.resourceSyncer
38+
}
39+
40+
func (l Listers) PreRunHasSynced() []cache.InformerSynced {
41+
return []cache.InformerSynced{}
42+
}
43+
44+
// NewAPIServerConfigObserverController creates a new controller that observes
45+
// the APIServer CR and updates the observed configuration based on the TLS
46+
// security profile settings.
47+
func NewAPIServerConfigObserverController(
48+
operatorClient v1helpers.OperatorClient,
49+
apiServerInformer configinformers.APIServerInformer,
50+
resourceSyncer resourcesynccontroller.ResourceSyncer,
51+
eventRecorder events.Recorder,
52+
) (*APIServerConfigObserverController, error) {
53+
// Define the paths where TLS configuration should be stored in the operator config
54+
tlsSecurityProfilePaths := [][]string{
55+
{"tlsSecurityProfile"},
56+
}
57+
58+
// Create listers for the config observer
59+
listers := Listers{
60+
apiServerLister: apiServerInformer.Lister(),
61+
resourceSyncer: resourceSyncer,
62+
}
63+
64+
// Create the config observer with the TLS security profile observer function
65+
// This returns a factory.Controller that handles syncing automatically
66+
controller := configobserver.NewConfigObserver(
67+
"APIServerConfigObserver",
68+
operatorClient,
69+
eventRecorder,
70+
listers,
71+
[]factory.Informer{apiServerInformer.Informer()},
72+
apiserver.ObserveTLSSecurityProfileWithPaths(
73+
tlsSecurityProfilePaths,
74+
apiServerInformer,
75+
),
76+
)
77+
78+
return &APIServerConfigObserverController{
79+
Controller: controller,
80+
apiServerLister: apiServerInformer.Lister(),
81+
}, nil
82+
}
83+
84+
// GetObservedTLSSecurityProfile retrieves the currently observed TLS security profile
85+
// from the APIServer CR. This can be used by other components that need access to
86+
// the TLS configuration.
87+
func (c *APIServerConfigObserverController) GetObservedTLSSecurityProfile() (*configv1.TLSSecurityProfile, error) {
88+
apiServer, err := c.apiServerLister.Get("cluster")
89+
if err != nil {
90+
return nil, fmt.Errorf("failed to get APIServer 'cluster': %w", err)
91+
}
92+
return apiServer.Spec.TLSSecurityProfile, nil
93+
}

pkg/cmd/checkendpoints/controller/connection_checker.go

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,18 @@ type ConnectionChecker interface {
3131
type GetCheckFunc func() *operatorcontrolplanev1alpha1.PodNetworkConnectivityCheck
3232

3333
// NewConnectionChecker returns a ConnectionChecker.
34-
func NewConnectionChecker(name, podName, podNamespace string, getCheck GetCheckFunc, client v1alpha1helpers.PodNetworkConnectivityCheckClient, clientCertGetter CertificatesGetter, recorder Recorder) ConnectionChecker {
34+
func NewConnectionChecker(name, podName, podNamespace string, getCheck GetCheckFunc, client v1alpha1helpers.PodNetworkConnectivityCheckClient, clientCertGetter CertificatesGetter, tlsConfigProvider TLSConfigProvider, recorder Recorder) ConnectionChecker {
3535
return &connectionChecker{
36-
name: name,
37-
podName: podName,
38-
getCheck: getCheck,
39-
client: client,
40-
clientCertGetter: clientCertGetter,
41-
recorder: recorder,
42-
updates: NewUpdatesManager(checkPeriod, checkTimeout, newUpdatesProcessor(client, name)),
43-
stop: make(chan interface{}),
44-
metrics: NewMetricsContext(podNamespace, name),
36+
name: name,
37+
podName: podName,
38+
getCheck: getCheck,
39+
client: client,
40+
clientCertGetter: clientCertGetter,
41+
tlsConfigProvider: tlsConfigProvider,
42+
recorder: recorder,
43+
updates: NewUpdatesManager(checkPeriod, checkTimeout, newUpdatesProcessor(client, name)),
44+
stop: make(chan interface{}),
45+
metrics: NewMetricsContext(podNamespace, name),
4546
}
4647
}
4748

@@ -59,12 +60,13 @@ type connectionChecker struct {
5960
podName string
6061
getCheck GetCheckFunc
6162

62-
client v1alpha1helpers.PodNetworkConnectivityCheckClient
63-
clientCertGetter CertificatesGetter
64-
recorder Recorder
65-
updates UpdatesManager
66-
stop chan interface{}
67-
metrics MetricsContext
63+
client v1alpha1helpers.PodNetworkConnectivityCheckClient
64+
clientCertGetter CertificatesGetter
65+
tlsConfigProvider TLSConfigProvider
66+
recorder Recorder
67+
updates UpdatesManager
68+
stop chan interface{}
69+
metrics MetricsContext
6870
}
6971

7072
// checkConnection checks the connection periodically, updating status as needed
@@ -156,7 +158,11 @@ func (c *connectionChecker) getTCPConnectLatency(ctx context.Context, address st
156158

157159
// perform tls handshake to avoid spamming the logs of tls endpoints
158160
host, _, _ := net.SplitHostPort(address)
159-
tlsConn := tls.Client(tcpConn, &tls.Config{Certificates: c.clientCertGetter(), ServerName: host, InsecureSkipVerify: true})
161+
162+
// Get TLS config from the provider (which uses the observed APIServer TLS security profile)
163+
tlsConfig := c.tlsConfigProvider.GetTLSConfig(host, c.clientCertGetter())
164+
165+
tlsConn := tls.Client(tcpConn, tlsConfig)
160166
if err = tlsConn.Handshake(); err != nil {
161167
// ignore any error. most likely non-tls connection, plus we're not really testing tls
162168
klog.V(4).Infof("%s: tls error ignored: %v", address, err)

pkg/cmd/checkendpoints/controller/pod_network_connectivity_check_controller.go

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ type PodNetworkConnectivityCheckController interface {
3131
// the connectivity checks.
3232
type controller struct {
3333
factory.Controller
34-
podName string
35-
podNamespace string
36-
checksGetter operatorcontrolplaneclientv1alpha1.PodNetworkConnectivityCheckInterface
37-
checkLister v1alpha1.PodNetworkConnectivityCheckNamespaceLister
38-
secretLister corelistersv1.SecretLister
39-
recorder Recorder
34+
podName string
35+
podNamespace string
36+
checksGetter operatorcontrolplaneclientv1alpha1.PodNetworkConnectivityCheckInterface
37+
checkLister v1alpha1.PodNetworkConnectivityCheckNamespaceLister
38+
secretLister corelistersv1.SecretLister
39+
tlsConfigProvider TLSConfigProvider
40+
recorder Recorder
4041
// each PodNetworkConnectivityCheck gets its own ConnectionChecker
4142
updaters map[string]ConnectionChecker
4243
}
@@ -46,15 +47,18 @@ type controller struct {
4647
func NewPodNetworkConnectivityCheckController(podName, podNamespace string,
4748
checksGetter operatorcontrolplaneclientv1alpha1.PodNetworkConnectivityChecksGetter,
4849
checkInformer alpha1.PodNetworkConnectivityCheckInformer,
49-
secretInformer coreinformersv1.SecretInformer, recorder events.Recorder) PodNetworkConnectivityCheckController {
50+
secretInformer coreinformersv1.SecretInformer,
51+
tlsConfigProvider TLSConfigProvider,
52+
recorder events.Recorder) PodNetworkConnectivityCheckController {
5053
c := &controller{
51-
podName: podName,
52-
podNamespace: podNamespace,
53-
checksGetter: checksGetter.PodNetworkConnectivityChecks(podNamespace),
54-
checkLister: checkInformer.Lister().PodNetworkConnectivityChecks(podNamespace),
55-
secretLister: secretInformer.Lister(),
56-
recorder: NewBackoffEventRecorder(recorder),
57-
updaters: map[string]ConnectionChecker{},
54+
podName: podName,
55+
podNamespace: podNamespace,
56+
checksGetter: checksGetter.PodNetworkConnectivityChecks(podNamespace),
57+
checkLister: checkInformer.Lister().PodNetworkConnectivityChecks(podNamespace),
58+
secretLister: secretInformer.Lister(),
59+
tlsConfigProvider: tlsConfigProvider,
60+
recorder: NewBackoffEventRecorder(recorder),
61+
updaters: map[string]ConnectionChecker{},
5862
}
5963
c.Controller = factory.New().
6064
WithSync(c.Sync).
@@ -83,7 +87,7 @@ func (c *controller) Sync(ctx context.Context, syncContext factory.SyncContext)
8387
// create & start status updaters if needed
8488
for _, check := range checks {
8589
if updater := c.updaters[check.Name]; updater == nil {
86-
c.updaters[check.Name] = NewConnectionChecker(check.Name, c.podName, c.podNamespace, c.newCheckFunc(check.Name), c, c.getClientCerts(check), c.recorder)
90+
c.updaters[check.Name] = NewConnectionChecker(check.Name, c.podName, c.podNamespace, c.newCheckFunc(check.Name), c, c.getClientCerts(check), c.tlsConfigProvider, c.recorder)
8791
go c.updaters[check.Name].Run(ctx)
8892
}
8993
}

0 commit comments

Comments
 (0)