@@ -2,6 +2,9 @@ package controller
22
33import (
44 "context"
5+ "crypto/tls"
6+ "fmt"
7+ "io"
58 "net/http"
69 "time"
710
@@ -47,20 +50,10 @@ func New(logger log.Logger) *Controller {
4750}
4851
4952func (c * Controller ) Run (ctx context.Context ) {
53+ defer cleanup ()
5054 for {
5155 select {
5256 case <- ctx .Done ():
53- // cleanup
54- containers , err := container .ListByLabel (constants .NodeCCMLabelKey )
55- if err != nil {
56- klog .Errorf ("can't list containers: %v" , err )
57- return
58- }
59- for _ , id := range containers {
60- if err := container .Delete (id ); err != nil {
61- klog .Errorf ("can't delete container %s: %v" , id , err )
62- }
63- }
6457 return
6558 default :
6659 }
@@ -72,27 +65,20 @@ func (c *Controller) Run(ctx context.Context) {
7265
7366 // add new ones
7467 for _ , cluster := range clusters {
68+ select {
69+ case <- ctx .Done ():
70+ return
71+ default :
72+ }
73+
7574 klog .V (3 ).Infof ("processing cluster %s" , cluster )
7675 _ , ok := c .clusters [cluster ]
7776 if ok {
7877 klog .V (3 ).Infof ("cluster %s already exist" , cluster )
7978 continue
8079 }
8180
82- // get kubeconfig
83- kconfig , err := c .kind .KubeConfig (cluster , false )
84- if err != nil {
85- klog .Errorf ("Failed to get kubeconfig for cluster %s: %v" , cluster , err )
86- continue
87- }
88-
89- config , err := clientcmd .RESTConfigFromKubeConfig ([]byte (kconfig ))
90- if err != nil {
91- klog .Errorf ("Failed to convert kubeconfig for cluster %s: %v" , cluster , err )
92- continue
93- }
94-
95- kubeClient , err := kubernetes .NewForConfig (config )
81+ kubeClient , err := c .getKubeClient (ctx , cluster )
9682 if err != nil {
9783 klog .Errorf ("Failed to create kubeClient for cluster %s: %v" , cluster , err )
9884 continue
@@ -122,6 +108,75 @@ func (c *Controller) Run(ctx context.Context) {
122108 }
123109}
124110
111+ // getKubeClient returns a kubeclient depending if the ccm runs inside a container
112+ // inside the same docker network that the kind cluster or run externally in the host
113+ // It tries first to connect to the external endpoint
114+ func (c * Controller ) getKubeClient (ctx context.Context , cluster string ) (kubernetes.Interface , error ) {
115+ httpClient := & http.Client {
116+ Timeout : 5 * time .Second ,
117+ Transport : & http.Transport {
118+ TLSClientConfig : & tls.Config {InsecureSkipVerify : true },
119+ },
120+ }
121+ // try internal first
122+ for _ , internal := range []bool {false , true } {
123+ kconfig , err := c .kind .KubeConfig (cluster , internal )
124+ if err != nil {
125+ klog .Errorf ("Failed to get kubeconfig for cluster %s: %v" , cluster , err )
126+ continue
127+ }
128+
129+ config , err := clientcmd .RESTConfigFromKubeConfig ([]byte (kconfig ))
130+ if err != nil {
131+ klog .Errorf ("Failed to convert kubeconfig for cluster %s: %v" , cluster , err )
132+ continue
133+ }
134+
135+ // check that the apiserver is reachable before continue
136+ // to fail fast and avoid waiting until the client operations timeout
137+ var ok bool
138+ for i := 0 ; i < 5 ; i ++ {
139+ select {
140+ case <- ctx .Done ():
141+ return nil , ctx .Err ()
142+ default :
143+ }
144+ if probeHTTP (httpClient , config .Host ) {
145+ ok = true
146+ break
147+ }
148+ time .Sleep (time .Second * time .Duration (i ))
149+ }
150+ if ! ok {
151+ klog .Errorf ("Failed to connect to apiserver %s: %v" , cluster , err )
152+ continue
153+ }
154+
155+ kubeClient , err := kubernetes .NewForConfig (config )
156+ if err != nil {
157+ klog .Errorf ("Failed to create kubeClient for cluster %s: %v" , cluster , err )
158+ continue
159+ }
160+ return kubeClient , err
161+ }
162+ return nil , fmt .Errorf ("can not find a working kubernetes clientset" )
163+ }
164+
165+ func probeHTTP (client * http.Client , address string ) bool {
166+ klog .Infof ("probe HTTP address %s" , address )
167+ resp , err := client .Get (address )
168+ if err != nil {
169+ klog .Infof ("Failed to connect to HTTP address %s: %v" , address , err )
170+ return false
171+ }
172+ defer resp .Body .Close ()
173+ // drain the body
174+ io .ReadAll (resp .Body ) // nolint:errcheck
175+ // we only want to verify connectivity so don't need to check the http status code
176+ // as the apiserver may not be ready
177+ return true
178+ }
179+
125180// TODO: implement leader election to not have problems with multiple providers
126181// ref: https://github.com/kubernetes/kubernetes/blob/d97ea0f705847f90740cac3bc3dd8f6a4026d0b5/cmd/kube-scheduler/app/server.go#L211
127182func startCloudControllerManager (ctx context.Context , clusterName string , kubeClient kubernetes.Interface , cloud cloudprovider.Interface ) (* ccm , error ) {
@@ -194,3 +249,16 @@ func startCloudControllerManager(ctx context.Context, clusterName string, kubeCl
194249 nodeController : nodeController ,
195250 cancelFn : cancel }, nil
196251}
252+
253+ func cleanup () {
254+ containers , err := container .ListByLabel (constants .NodeCCMLabelKey )
255+ if err != nil {
256+ klog .Errorf ("can't list containers: %v" , err )
257+ return
258+ }
259+ for _ , id := range containers {
260+ if err := container .Delete (id ); err != nil {
261+ klog .Errorf ("can't delete container %s: %v" , id , err )
262+ }
263+ }
264+ }
0 commit comments