@@ -23,6 +23,7 @@ import (
23
23
"io"
24
24
"net"
25
25
"net/http"
26
+ "strings"
26
27
"time"
27
28
28
29
"github.com/pkg/errors"
@@ -34,14 +35,27 @@ import (
34
35
"k8s.io/apimachinery/pkg/util/wait"
35
36
clientset "k8s.io/client-go/kubernetes"
36
37
37
- kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
38
38
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
39
39
)
40
40
41
+ const (
42
+ // TODO: switch to /livez once all components support it
43
+ // and delete the endpointHealthz constant.
44
+ // https://github.com/kubernetes/kubernetes/issues/118158
45
+ endpointHealthz = "healthz"
46
+ endpointLivez = "livez"
47
+
48
+ argPort = "secure-port"
49
+ argBindAddress = "bind-address"
50
+ // By default, for kube-api-server, kubeadm does not apply a --bind-address flag.
51
+ // Check --advertise-address instead.
52
+ argAdvertiseAddress = "advertise-address"
53
+ )
54
+
41
55
// Waiter is an interface for waiting for criteria in Kubernetes to happen
42
56
type Waiter interface {
43
57
// WaitForControlPlaneComponents waits for all control plane components to be ready.
44
- WaitForControlPlaneComponents (cfg * kubeadmapi. ClusterConfiguration , apiServerAddress string ) error
58
+ WaitForControlPlaneComponents (podMap map [ string ] * v1. Pod , apiServerAddress string ) error
45
59
// WaitForAPI waits for the API Server's /healthz endpoint to become "ok"
46
60
// TODO: remove WaitForAPI once WaitForAllControlPlaneComponents goes GA:
47
61
// https://github.com/kubernetes/kubeadm/issues/2907
@@ -77,80 +91,147 @@ func NewKubeWaiter(client clientset.Interface, timeout time.Duration, writer io.
77
91
}
78
92
}
79
93
94
+ // controlPlaneComponent holds a component name and an URL
95
+ // on which to perform health checks.
80
96
type controlPlaneComponent struct {
81
97
name string
82
98
url string
83
99
}
84
100
85
- const (
86
- // TODO: switch to /livez once all components support it
87
- // and delete the endpointHealthz constant.
88
- // https://github.com/kubernetes/kubernetes/issues/118158
89
- endpointHealthz = "healthz"
90
- endpointLivez = "livez"
91
- )
92
-
93
- // getControlPlaneComponents takes a ClusterConfiguration and returns a slice of
94
- // control plane components and their health check URLs.
95
- func getControlPlaneComponents (cfg * kubeadmapi.ClusterConfiguration , defaultAddressAPIServer string ) []controlPlaneComponent {
96
- const (
97
- portArg = "secure-port"
98
- bindAddressArg = "bind-address"
99
- // By default, for kube-api-server, kubeadm does not apply a --bind-address flag.
100
- // Check --advertise-address instead, which can override the defaultAddressAPIServer value.
101
- advertiseAddressArg = "advertise-address"
102
- // By default kubeadm deploys the kube-controller-manager and kube-scheduler
103
- // with --bind-address=127.0.0.1. This should match get{Scheduler|ControllerManager}Command().
104
- defaultAddressKCM = "127.0.0.1"
105
- defaultAddressScheduler = "127.0.0.1"
101
+ // getControlPlaneComponentAddressAndPort parses the command in a static Pod
102
+ // container and extracts the values of the given args.
103
+ func getControlPlaneComponentAddressAndPort (pod * v1.Pod , name string , args []string ) ([]string , error ) {
104
+ var (
105
+ values = make ([]string , len (args ))
106
+ container * v1.Container
106
107
)
107
108
108
- portAPIServer , idx := kubeadmapi .GetArgValue (cfg .APIServer .ExtraArgs , portArg , - 1 )
109
- if idx == - 1 {
110
- portAPIServer = fmt .Sprintf ("%d" , constants .KubeAPIServerPort )
109
+ if pod == nil {
110
+ return values , errors .Errorf ("got nil Pod for component %q" , name )
111
111
}
112
- portKCM , idx := kubeadmapi .GetArgValue (cfg .ControllerManager .ExtraArgs , portArg , - 1 )
113
- if idx == - 1 {
114
- portKCM = fmt .Sprintf ("%d" , constants .KubeControllerManagerPort )
112
+
113
+ for i , c := range pod .Spec .Containers {
114
+ if len (c .Command ) == 0 {
115
+ continue
116
+ }
117
+ if c .Command [0 ] == name {
118
+ container = & pod .Spec .Containers [i ]
119
+ break
120
+ }
115
121
}
116
- portScheduler , idx := kubeadmapi .GetArgValue (cfg .Scheduler .ExtraArgs , portArg , - 1 )
117
- if idx == - 1 {
118
- portScheduler = fmt .Sprintf ("%d" , constants .KubeSchedulerPort )
122
+ if container == nil {
123
+ return values , errors .Errorf ("the Pod has no container command starting with %q" , name )
119
124
}
120
125
121
- addressAPIServer , idx := kubeadmapi .GetArgValue (cfg .APIServer .ExtraArgs , advertiseAddressArg , - 1 )
122
- if idx == - 1 {
123
- addressAPIServer = defaultAddressAPIServer
126
+ for _ , line := range container .Command {
127
+ for i , arg := range args {
128
+ line = strings .TrimSpace (line )
129
+ if ! strings .HasPrefix (line , "--" + arg ) && ! strings .HasPrefix (line , "-" + arg ) {
130
+ continue
131
+ }
132
+ _ , value , found := strings .Cut (line , "=" )
133
+ if ! found {
134
+ _ , value , _ = strings .Cut (line , " " )
135
+ }
136
+ values [i ] = value
137
+ }
124
138
}
125
- addressKCM , idx := kubeadmapi .GetArgValue (cfg .ControllerManager .ExtraArgs , bindAddressArg , - 1 )
126
- if idx == - 1 {
127
- addressKCM = defaultAddressKCM
139
+ return values , nil
140
+ }
141
+
142
+ // getControlPlaneComponents reads the static Pods of control plane components
143
+ // and returns a slice of 'controlPlaneComponent'.
144
+ func getControlPlaneComponents (podMap map [string ]* v1.Pod , addressAPIServer string ) ([]controlPlaneComponent , error ) {
145
+ var (
146
+ // By default kubeadm deploys the kube-controller-manager and kube-scheduler
147
+ // with --bind-address=127.0.0.1. This should match get{Scheduler|ControllerManager}Command().
148
+ addressKCM = "127.0.0.1"
149
+ addressScheduler = "127.0.0.1"
150
+
151
+ portAPIServer = fmt .Sprintf ("%d" , constants .KubeAPIServerPort )
152
+ portKCM = fmt .Sprintf ("%d" , constants .KubeControllerManagerPort )
153
+ portScheduler = fmt .Sprintf ("%d" , constants .KubeSchedulerPort )
154
+
155
+ errs []error
156
+ result []controlPlaneComponent
157
+ )
158
+
159
+ type componentConfig struct {
160
+ name string
161
+ podKey string
162
+ args []string
163
+ defaultAddr string
164
+ defaultPort string
165
+ endpoint string
128
166
}
129
- addressScheduler , idx := kubeadmapi .GetArgValue (cfg .Scheduler .ExtraArgs , bindAddressArg , - 1 )
130
- if idx == - 1 {
131
- addressScheduler = defaultAddressScheduler
167
+
168
+ components := []componentConfig {
169
+ {
170
+ name : "kube-apiserver" ,
171
+ podKey : constants .KubeAPIServer ,
172
+ args : []string {argAdvertiseAddress , argPort },
173
+ defaultAddr : addressAPIServer ,
174
+ defaultPort : portAPIServer ,
175
+ endpoint : endpointLivez ,
176
+ },
177
+ {
178
+ name : "kube-controller-manager" ,
179
+ podKey : constants .KubeControllerManager ,
180
+ args : []string {argBindAddress , argPort },
181
+ defaultAddr : addressKCM ,
182
+ defaultPort : portKCM ,
183
+ endpoint : endpointHealthz ,
184
+ },
185
+ {
186
+ name : "kube-scheduler" ,
187
+ podKey : constants .KubeScheduler ,
188
+ args : []string {argBindAddress , argPort },
189
+ defaultAddr : addressScheduler ,
190
+ defaultPort : portScheduler ,
191
+ endpoint : endpointLivez ,
192
+ },
132
193
}
133
194
134
- getURL := func (address , port , endpoint string ) string {
135
- return fmt .Sprintf (
136
- "https://%s/%s" ,
137
- net .JoinHostPort (address , port ),
138
- endpoint ,
195
+ for _ , component := range components {
196
+ address , port := component .defaultAddr , component .defaultPort
197
+
198
+ values , err := getControlPlaneComponentAddressAndPort (
199
+ podMap [component .podKey ],
200
+ component .podKey ,
201
+ component .args ,
139
202
)
203
+ if err != nil {
204
+ errs = append (errs , err )
205
+ }
206
+
207
+ if len (values [0 ]) != 0 {
208
+ address = values [0 ]
209
+ }
210
+ if len (values [1 ]) != 0 {
211
+ port = values [1 ]
212
+ }
213
+
214
+ result = append (result , controlPlaneComponent {
215
+ name : component .name ,
216
+ url : fmt .Sprintf ("https://%s/%s" , net .JoinHostPort (address , port ), component .endpoint ),
217
+ })
140
218
}
141
- return []controlPlaneComponent {
142
- {name : "kube-apiserver" , url : getURL (addressAPIServer , portAPIServer , endpointLivez )},
143
- {name : "kube-controller-manager" , url : getURL (addressKCM , portKCM , endpointHealthz )},
144
- {name : "kube-scheduler" , url : getURL (addressScheduler , portScheduler , endpointLivez )},
219
+
220
+ if len (errs ) > 0 {
221
+ return nil , utilerrors .NewAggregate (errs )
145
222
}
223
+ return result , nil
146
224
}
147
225
148
226
// WaitForControlPlaneComponents waits for all control plane components to report "ok".
149
- func (w * KubeWaiter ) WaitForControlPlaneComponents (cfg * kubeadmapi. ClusterConfiguration , apiSeverAddress string ) error {
227
+ func (w * KubeWaiter ) WaitForControlPlaneComponents (podMap map [ string ] * v1. Pod , apiSeverAddress string ) error {
150
228
fmt .Printf ("[control-plane-check] Waiting for healthy control plane components." +
151
229
" This can take up to %v\n " , w .timeout )
152
230
153
- components := getControlPlaneComponents (cfg , apiSeverAddress )
231
+ components , err := getControlPlaneComponents (podMap , apiSeverAddress )
232
+ if err != nil {
233
+ return errors .Wrap (err , "could not parse the address and port of all control plane components" )
234
+ }
154
235
155
236
var errs []error
156
237
errChan := make (chan error , len (components ))
0 commit comments