forked from openshift/cluster-kube-apiserver-operator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
degraded_webhook.go
144 lines (135 loc) · 4.25 KB
/
degraded_webhook.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package webhooksupportabilitycontroller
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"sort"
"strings"
"time"
operatorv1 "github.com/openshift/api/operator/v1"
"github.com/openshift/library-go/pkg/operator/v1helpers"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2"
)
// webhookInfo generically represents a webhook
type webhookInfo struct {
Name string
Service *serviceReference
CABundle []byte
FailurePolicyIsIgnore bool
// TimeoutSeconds specifies the timeout for a webhook.
// After the timeout passes, the webhook call will be ignored or the API call will fail
TimeoutSeconds *int32
}
// serviceReference generically represents a service reference
type serviceReference struct {
Namespace string
Name string
Port *int32
}
// updateWebhookConfigurationDegraded updates the condition specified after
// checking that the services associated with the specified webhooks exist
// and have at least one ready endpoint.
func (c *webhookSupportabilityController) updateWebhookConfigurationDegraded(ctx context.Context, condition operatorv1.OperatorCondition, webhookInfos []webhookInfo) v1helpers.UpdateStatusFunc {
var serviceMsgs []string
var tlsMsgs []string
for _, webhook := range webhookInfos {
if webhook.Service != nil {
err := c.assertService(webhook.Service)
if err != nil {
msg := fmt.Sprintf("%s: %s", webhook.Name, err)
if webhook.FailurePolicyIsIgnore {
klog.Error(msg)
continue
}
serviceMsgs = append(serviceMsgs, msg)
continue
}
err = c.assertConnect(ctx, webhook.Name, webhook.Service, webhook.CABundle, webhook.TimeoutSeconds)
if err != nil {
msg := fmt.Sprintf("%s: %s", webhook.Name, err)
if webhook.FailurePolicyIsIgnore {
klog.Error(msg)
continue
}
tlsMsgs = append(tlsMsgs, msg)
continue
}
}
}
svc, tls := len(serviceMsgs) > 0, len(tlsMsgs) > 0
switch {
case svc && tls:
condition.Reason = WebhookServiceNotReadyReason
condition.Status = operatorv1.ConditionTrue
case svc:
condition.Reason = WebhookServiceNotFoundReason
condition.Status = operatorv1.ConditionTrue
case tls:
condition.Reason = WebhookServiceConnectionErrorReason
condition.Status = operatorv1.ConditionTrue
default:
condition.Reason = ""
condition.Status = operatorv1.ConditionFalse
}
msgs := append(serviceMsgs, tlsMsgs...)
sort.Strings(msgs)
condition.Message = strings.Join(msgs, "\n")
return v1helpers.UpdateConditionFn(condition)
}
// assertService checks that the referenced service resource exists.
func (c *webhookSupportabilityController) assertService(reference *serviceReference) error {
_, err := c.serviceLister.Services(reference.Namespace).Get(reference.Name)
if err != nil {
return fmt.Errorf("unable to find service %s.%s: %v", reference.Name, reference.Namespace, err)
}
return nil
}
// assertConnect performs a dns lookup of service, opens a tcp connection, and performs a tls handshake.
func (c *webhookSupportabilityController) assertConnect(ctx context.Context, webhookName string, reference *serviceReference, caBundle []byte, webhookTimeoutSeconds *int32) error {
host := reference.Name + "." + reference.Namespace + ".svc"
port := "443"
if reference.Port != nil {
port = fmt.Sprintf("%d", *reference.Port)
}
rootCAs := x509.NewCertPool()
if len(caBundle) > 0 {
rootCAs.AppendCertsFromPEM(caBundle)
}
timeout := 10 * time.Second
if webhookTimeoutSeconds != nil {
timeout = time.Duration(*webhookTimeoutSeconds) * time.Second
}
// the last error that occurred in the loop below
var err error
// retry up to 3 times on error
for i := 0; i < 3; i++ {
select {
case <-ctx.Done():
return nil
case <-time.After(time.Duration(i) * time.Second):
}
dialer := &tls.Dialer{
NetDialer: &net.Dialer{Timeout: timeout},
Config: &tls.Config{
ServerName: host,
RootCAs: rootCAs,
},
}
var conn net.Conn
conn, err = dialer.DialContext(ctx, "tcp", net.JoinHostPort(host, port))
if err != nil {
if i != 2 {
// log err since only last one is reported
runtime.HandleError(fmt.Errorf("%s: %v", webhookName, err))
}
continue
}
// error from closing connection should not affect Degraded condition
runtime.HandleError(conn.Close())
break
}
return err
}