Skip to content

Commit 1c98a79

Browse files
committed
Use generics for csr approver
Signed-off-by: Jian Qiu <[email protected]>
1 parent 549a399 commit 1c98a79

File tree

4 files changed

+153
-177
lines changed

4 files changed

+153
-177
lines changed

pkg/addonmanager/controllers/certificate/csrapprove.go

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

33
import (
44
"context"
5-
"fmt"
65
"strings"
76

87
certificatesv1 "k8s.io/api/certificates/v1"
@@ -12,11 +11,7 @@ import (
1211
"k8s.io/apimachinery/pkg/api/meta"
1312
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1413
"k8s.io/apimachinery/pkg/runtime"
15-
certificatesinformers "k8s.io/client-go/informers/certificates/v1"
16-
v1beta1certificatesinformers "k8s.io/client-go/informers/certificates/v1beta1"
1714
"k8s.io/client-go/kubernetes"
18-
certificateslisters "k8s.io/client-go/listers/certificates/v1"
19-
v1beta1certificateslisters "k8s.io/client-go/listers/certificates/v1beta1"
2015
"k8s.io/client-go/tools/cache"
2116
"k8s.io/klog/v2"
2217
addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
@@ -50,42 +45,45 @@ var (
5045
EnableV1Beta1CSRCompatibility = true
5146
)
5247

48+
type CSR interface {
49+
*certificatesv1.CertificateSigningRequest | *certificatesv1beta1.CertificateSigningRequest
50+
GetLabels() map[string]string
51+
GetName() string
52+
}
53+
54+
type CSRLister[T CSR] interface {
55+
Get(name string) (T, error)
56+
}
57+
58+
type CSRApprover[T CSR] interface {
59+
approve(ctx context.Context, csr T) error
60+
isInTerminalState(csr T) bool
61+
}
62+
5363
// csrApprovingController auto approve the renewal CertificateSigningRequests for an accepted spoke cluster on the hub.
54-
type csrApprovingController struct {
55-
kubeClient kubernetes.Interface
64+
type csrApprovingController[T CSR] struct {
5665
agentAddons map[string]agent.AgentAddon
5766
managedClusterLister clusterlister.ManagedClusterLister
5867
managedClusterAddonLister addonlisterv1alpha1.ManagedClusterAddOnLister
59-
csrLister certificateslisters.CertificateSigningRequestLister
60-
csrListerBeta v1beta1certificateslisters.CertificateSigningRequestLister
68+
csrLister CSRLister[T]
69+
approver CSRApprover[T]
6170
}
6271

6372
// NewCSRApprovingController creates a new csr approving controller
64-
func NewCSRApprovingController(
65-
kubeClient kubernetes.Interface,
73+
func NewCSRApprovingController[T CSR](
6674
clusterInformers clusterinformers.ManagedClusterInformer,
67-
csrV1Informer certificatesinformers.CertificateSigningRequestInformer,
68-
csrBetaInformer v1beta1certificatesinformers.CertificateSigningRequestInformer,
6975
addonInformers addoninformerv1alpha1.ManagedClusterAddOnInformer,
76+
csrInformer cache.SharedIndexInformer,
77+
csrLister CSRLister[T],
78+
approver CSRApprover[T],
7079
agentAddons map[string]agent.AgentAddon,
7180
) factory.Controller {
72-
if (csrV1Informer != nil) == (csrBetaInformer != nil) {
73-
klog.Fatalf("V1 and V1beta1 CSR informer cannot be present or absent at the same time")
74-
}
75-
c := &csrApprovingController{
76-
kubeClient: kubeClient,
81+
c := &csrApprovingController[T]{
7782
agentAddons: agentAddons,
7883
managedClusterLister: clusterInformers.Lister(),
7984
managedClusterAddonLister: addonInformers.Lister(),
80-
}
81-
var csrInformer cache.SharedIndexInformer
82-
if csrV1Informer != nil {
83-
c.csrLister = csrV1Informer.Lister()
84-
csrInformer = csrV1Informer.Informer()
85-
}
86-
if EnableV1Beta1CSRCompatibility && csrBetaInformer != nil {
87-
c.csrListerBeta = csrBetaInformer.Lister()
88-
csrInformer = csrBetaInformer.Informer()
85+
csrLister: csrLister,
86+
approver: approver,
8987
}
9088

9189
return factory.New().
@@ -113,18 +111,18 @@ func NewCSRApprovingController(
113111
ToController("CSRApprovingController")
114112
}
115113

116-
func (c *csrApprovingController) sync(ctx context.Context, syncCtx factory.SyncContext, csrName string) error {
114+
func (c *csrApprovingController[T]) sync(ctx context.Context, syncCtx factory.SyncContext, csrName string) error {
117115
klog.V(4).Infof("Reconciling CertificateSigningRequests %q", csrName)
118116

119-
csr, err := c.getCSR(csrName)
120-
if csr == nil {
117+
csr, err := c.csrLister.Get(csrName)
118+
if errors.IsNotFound(err) {
121119
return nil
122120
}
123121
if err != nil {
124122
return err
125123
}
126124

127-
if isCSRApproved(csr) || IsCSRInTerminalState(csr) {
125+
if c.approver.isInTerminalState(csr) {
128126
return nil
129127
}
130128

@@ -161,32 +159,14 @@ func (c *csrApprovingController) sync(ctx context.Context, syncCtx factory.SyncC
161159
return err
162160
}
163161

164-
if registrationOption.CSRApproveCheck == nil {
165-
klog.V(4).Infof("addon csr %q cannont be auto approved due to approve check not defined", csr.GetName())
166-
return nil
167-
}
168-
169162
if err := c.approve(ctx, registrationOption, managedCluster, managedClusterAddon, csr); err != nil {
170163
return err
171164
}
172165

173166
return nil
174167
}
175168

176-
func (c *csrApprovingController) getCSR(csrName string) (metav1.Object, error) {
177-
// TODO: remove the following block for deprecating V1beta1 CSR compatibility
178-
if EnableV1Beta1CSRCompatibility {
179-
if c.csrListerBeta != nil {
180-
csr, err := c.csrListerBeta.Get(csrName)
181-
if errors.IsNotFound(err) {
182-
return nil, nil
183-
}
184-
if err != nil {
185-
return nil, err
186-
}
187-
return csr, nil
188-
}
189-
}
169+
func (c *csrApprovingController[T]) getCSR(csrName string) (T, error) {
190170
csr, err := c.csrLister.Get(csrName)
191171
if errors.IsNotFound(err) {
192172
return nil, nil
@@ -197,143 +177,125 @@ func (c *csrApprovingController) getCSR(csrName string) (metav1.Object, error) {
197177
return csr, nil
198178
}
199179

200-
func (c *csrApprovingController) approve(
201-
ctx context.Context,
202-
registrationOption *agent.RegistrationOption,
203-
managedCluster *clusterv1.ManagedCluster,
204-
managedClusterAddon *addonv1alpha1.ManagedClusterAddOn,
205-
csr metav1.Object) error {
180+
// CSRV1Approver implement CSRApprover interface
181+
type CSRV1Approver struct {
182+
kubeClient kubernetes.Interface
183+
}
206184

207-
switch t := csr.(type) {
208-
case *certificatesv1.CertificateSigningRequest:
209-
approve := registrationOption.CSRApproveCheck(managedCluster, managedClusterAddon, t)
210-
if !approve {
211-
klog.V(4).Infof("addon csr %q cannont be auto approved due to approve check fails", csr.GetName())
212-
return nil
185+
func NewCSRV1Approver(client kubernetes.Interface) *CSRV1Approver {
186+
return &CSRV1Approver{kubeClient: client}
187+
}
188+
189+
func (c *CSRV1Approver) isInTerminalState(csr *certificatesv1.CertificateSigningRequest) bool {
190+
for _, c := range csr.Status.Conditions {
191+
if c.Type == certificatesv1.CertificateApproved {
192+
return true
213193
}
214-
return c.approveCSRV1(ctx, t)
215-
// TODO: remove the following block for deprecating V1beta1 CSR compatibility
216-
case *certificatesv1beta1.CertificateSigningRequest:
217-
v1CSR := unsafeConvertV1beta1CSRToV1CSR(t)
218-
approve := registrationOption.CSRApproveCheck(managedCluster, managedClusterAddon, v1CSR)
219-
if !approve {
220-
klog.V(4).Infof("addon csr %q cannont be auto approved due to approve check fails", csr.GetName())
221-
return nil
194+
if c.Type == certificatesv1.CertificateDenied {
195+
return true
222196
}
223-
return c.approveCSRV1Beta1(ctx, t)
224-
default:
225-
return fmt.Errorf("unknown csr object type: %t", csr)
226197
}
198+
return false
227199
}
228200

229-
func (c *csrApprovingController) approveCSRV1(ctx context.Context, v1CSR *certificatesv1.CertificateSigningRequest) error {
230-
v1CSR.Status.Conditions = append(v1CSR.Status.Conditions, certificatesv1.CertificateSigningRequestCondition{
201+
func (c *CSRV1Approver) approve(ctx context.Context, csr *certificatesv1.CertificateSigningRequest) error {
202+
csrCopy := csr.DeepCopy()
203+
// Auto approve the spoke cluster csr
204+
csrCopy.Status.Conditions = append(csr.Status.Conditions, certificatesv1.CertificateSigningRequestCondition{
231205
Type: certificatesv1.CertificateApproved,
232206
Status: corev1.ConditionTrue,
233207
Reason: "AutoApprovedByHubCSRApprovingController",
234-
Message: "Auto approving addon agent certificate.",
208+
Message: "Auto approving Managed cluster agent certificate after SubjectAccessReview.",
235209
})
236-
_, err := c.kubeClient.CertificatesV1().CertificateSigningRequests().UpdateApproval(ctx, v1CSR.GetName(), v1CSR, metav1.UpdateOptions{})
237-
if err != nil {
238-
return err
239-
}
240-
return nil
210+
_, err := c.kubeClient.CertificatesV1().CertificateSigningRequests().UpdateApproval(ctx, csrCopy.Name, csrCopy, metav1.UpdateOptions{})
211+
return err
241212
}
242213

243-
func (c *csrApprovingController) approveCSRV1Beta1(ctx context.Context, v1beta1CSR *certificatesv1beta1.CertificateSigningRequest) error {
244-
v1beta1CSR.Status.Conditions = append(v1beta1CSR.Status.Conditions, certificatesv1beta1.CertificateSigningRequestCondition{
245-
Type: certificatesv1beta1.CertificateApproved,
246-
Status: corev1.ConditionTrue,
247-
Reason: "AutoApprovedByHubCSRApprovingController",
248-
Message: "Auto approving addon agent certificate.",
249-
})
250-
_, err := c.kubeClient.CertificatesV1beta1().CertificateSigningRequests().UpdateApproval(ctx, v1beta1CSR, metav1.UpdateOptions{})
251-
if err != nil {
252-
return err
253-
}
254-
return nil
214+
type CSRV1beta1Approver struct {
215+
kubeClient kubernetes.Interface
255216
}
256217

257-
// Check whether a CSR is in terminal state
258-
func IsCSRInTerminalState(csr metav1.Object) bool {
259-
if v1CSR, ok := csr.(*certificatesv1.CertificateSigningRequest); ok {
260-
for _, c := range v1CSR.Status.Conditions {
261-
if c.Type == certificatesv1.CertificateApproved {
262-
return true
263-
}
264-
if c.Type == certificatesv1.CertificateDenied {
265-
return true
266-
}
218+
func NewCSRV1beta1Approver(client kubernetes.Interface) *CSRV1beta1Approver {
219+
return &CSRV1beta1Approver{kubeClient: client}
220+
}
221+
222+
func (c *CSRV1beta1Approver) isInTerminalState(csr *certificatesv1beta1.CertificateSigningRequest) bool {
223+
for _, c := range csr.Status.Conditions {
224+
if c.Type == certificatesv1beta1.CertificateApproved {
225+
return true
267226
}
268-
}
269-
// TODO: remove the following block for deprecating V1beta1 CSR compatibility
270-
if EnableV1Beta1CSRCompatibility {
271-
if v1beta1CSR, ok := csr.(*certificatesv1beta1.CertificateSigningRequest); ok {
272-
for _, c := range v1beta1CSR.Status.Conditions {
273-
if c.Type == certificatesv1beta1.CertificateApproved {
274-
return true
275-
}
276-
if c.Type == certificatesv1beta1.CertificateDenied {
277-
return true
278-
}
279-
}
227+
if c.Type == certificatesv1beta1.CertificateDenied {
228+
return true
280229
}
281230
}
282231
return false
283232
}
284233

285-
func isCSRApproved(csr metav1.Object) bool {
286-
approved := false
287-
if v1CSR, ok := csr.(*certificatesv1.CertificateSigningRequest); ok {
288-
for _, condition := range v1CSR.Status.Conditions {
289-
if condition.Type == certificatesv1.CertificateDenied {
290-
return false
291-
} else if condition.Type == certificatesv1.CertificateApproved {
292-
approved = true
293-
}
294-
}
234+
func (c *CSRV1beta1Approver) approve(ctx context.Context, csr *certificatesv1beta1.CertificateSigningRequest) error {
235+
csrCopy := csr.DeepCopy()
236+
// Auto approve the spoke cluster csr
237+
csrCopy.Status.Conditions = append(csr.Status.Conditions, certificatesv1beta1.CertificateSigningRequestCondition{
238+
Type: certificatesv1beta1.CertificateApproved,
239+
Status: corev1.ConditionTrue,
240+
Reason: "AutoApprovedByHubCSRApprovingController",
241+
Message: "Auto approving Managed cluster agent certificate after SubjectAccessReview.",
242+
})
243+
_, err := c.kubeClient.CertificatesV1beta1().CertificateSigningRequests().UpdateApproval(ctx, csrCopy, metav1.UpdateOptions{})
244+
return err
245+
}
246+
247+
func (c *csrApprovingController[T]) approve(
248+
ctx context.Context,
249+
registrationOption *agent.RegistrationOption,
250+
managedCluster *clusterv1.ManagedCluster,
251+
managedClusterAddon *addonv1alpha1.ManagedClusterAddOn,
252+
csr T) error {
253+
254+
v1CSR := unsafeConvertV1beta1CSRToV1CSR[T](csr)
255+
if registrationOption.CSRApproveCheck == nil {
256+
klog.V(4).Infof("addon csr %q cannont be auto approved due to approve check not defined", csr.GetName())
257+
return nil
295258
}
296-
// TODO: remove the following block for deprecating V1beta1 CSR compatibility
297-
if EnableV1Beta1CSRCompatibility {
298-
if v1beta1CSR, ok := csr.(*certificatesv1beta1.CertificateSigningRequest); ok {
299-
for _, condition := range v1beta1CSR.Status.Conditions {
300-
if condition.Type == certificatesv1beta1.CertificateDenied {
301-
return false
302-
} else if condition.Type == certificatesv1beta1.CertificateApproved {
303-
approved = true
304-
}
305-
}
306-
}
259+
approve := registrationOption.CSRApproveCheck(managedCluster, managedClusterAddon, v1CSR)
260+
if !approve {
261+
klog.V(4).Infof("addon csr %q cannont be auto approved due to approve check fails", csr.GetName())
262+
return nil
307263
}
308-
return approved
264+
265+
return c.approver.approve(ctx, csr)
309266
}
310267

311268
// TODO: remove the following block for deprecating V1beta1 CSR compatibility
312-
func unsafeConvertV1beta1CSRToV1CSR(v1beta1CSR *certificatesv1beta1.CertificateSigningRequest) *certificatesv1.CertificateSigningRequest {
313-
v1CSR := &certificatesv1.CertificateSigningRequest{
314-
TypeMeta: metav1.TypeMeta{
315-
APIVersion: certificatesv1.SchemeGroupVersion.String(),
316-
Kind: "CertificateSigningRequest",
317-
},
318-
ObjectMeta: *v1beta1CSR.ObjectMeta.DeepCopy(),
319-
Spec: certificatesv1.CertificateSigningRequestSpec{
320-
Request: v1beta1CSR.Spec.Request,
321-
ExpirationSeconds: v1beta1CSR.Spec.ExpirationSeconds,
322-
Usages: unsafeCovertV1beta1KeyUsageToV1KeyUsage(v1beta1CSR.Spec.Usages),
323-
Username: v1beta1CSR.Spec.Username,
324-
UID: v1beta1CSR.Spec.UID,
325-
Groups: v1beta1CSR.Spec.Groups,
326-
Extra: unsafeCovertV1beta1ExtraValueToV1ExtraValue(v1beta1CSR.Spec.Extra),
327-
},
328-
Status: certificatesv1.CertificateSigningRequestStatus{
329-
Certificate: v1beta1CSR.Status.Certificate,
330-
Conditions: unsafeCovertV1beta1ConditionsToV1Conditions(v1beta1CSR.Status.Conditions),
331-
},
332-
}
333-
if v1beta1CSR.Spec.SignerName != nil {
334-
v1CSR.Spec.SignerName = *v1beta1CSR.Spec.SignerName
269+
func unsafeConvertV1beta1CSRToV1CSR[T CSR](csr T) *certificatesv1.CertificateSigningRequest {
270+
switch obj := any(csr).(type) {
271+
case *certificatesv1.CertificateSigningRequest:
272+
return obj
273+
case *certificatesv1beta1.CertificateSigningRequest:
274+
v1CSR := &certificatesv1.CertificateSigningRequest{
275+
TypeMeta: metav1.TypeMeta{
276+
APIVersion: certificatesv1.SchemeGroupVersion.String(),
277+
Kind: "CertificateSigningRequest",
278+
},
279+
ObjectMeta: *obj.ObjectMeta.DeepCopy(),
280+
Spec: certificatesv1.CertificateSigningRequestSpec{
281+
Request: obj.Spec.Request,
282+
ExpirationSeconds: obj.Spec.ExpirationSeconds,
283+
Usages: unsafeCovertV1beta1KeyUsageToV1KeyUsage(obj.Spec.Usages),
284+
Username: obj.Spec.Username,
285+
UID: obj.Spec.UID,
286+
Groups: obj.Spec.Groups,
287+
Extra: unsafeCovertV1beta1ExtraValueToV1ExtraValue(obj.Spec.Extra),
288+
},
289+
Status: certificatesv1.CertificateSigningRequestStatus{
290+
Certificate: obj.Status.Certificate,
291+
Conditions: unsafeCovertV1beta1ConditionsToV1Conditions(obj.Status.Conditions),
292+
},
293+
}
294+
if obj.Spec.SignerName != nil {
295+
v1CSR.Spec.SignerName = *obj.Spec.SignerName
296+
}
335297
}
336-
return v1CSR
298+
return nil
337299
}
338300

339301
// TODO: remove the following block for deprecating V1beta1 CSR compatibility

0 commit comments

Comments
 (0)