Skip to content

Commit 3ed7fb6

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request kubernetes#39230 from irfanurrehman/fed-init-5
Automatic merge from submit-queue (batch tested with PRs 39230, 39718) [Federation] Kubefed init verifies if control plane pods are up before returning success This PR updates the functionality as needed in issue kubernetes#37841. cc @kubernetes/sig-cluster-federation @nikhiljindal @madhusudancs @shashidharatd
2 parents 14e322c + d6cfd82 commit 3ed7fb6

File tree

6 files changed

+127
-7
lines changed

6 files changed

+127
-7
lines changed

federation/pkg/kubefed/init/init.go

+52
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const (
6363
HostClusterLocalDNSZoneName = "cluster.local."
6464

6565
lbAddrRetryInterval = 5 * time.Second
66+
podWaitInterval = 2 * time.Second
6667
)
6768

6869
var (
@@ -230,6 +231,15 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
230231
}
231232

232233
if !dryRun {
234+
fedPods := []string{serverName, cmName}
235+
err = waitForPods(hostClientset, fedPods, initFlags.FederationSystemNamespace)
236+
if err != nil {
237+
return err
238+
}
239+
err = waitSrvHealthy(config, initFlags.Name, initFlags.Kubeconfig)
240+
if err != nil {
241+
return err
242+
}
233243
return printSuccess(cmdOut, ips, hostnames)
234244
}
235245
_, err = fmt.Fprintf(cmdOut, "Federation control plane runs (dry run)\n")
@@ -576,6 +586,48 @@ func createControllerManager(clientset *client.Clientset, namespace, name, svcNa
576586
return clientset.Extensions().Deployments(namespace).Create(dep)
577587
}
578588

589+
func waitForPods(clientset *client.Clientset, fedPods []string, namespace string) error {
590+
err := wait.PollInfinite(podWaitInterval, func() (bool, error) {
591+
podCheck := len(fedPods)
592+
podList, err := clientset.Core().Pods(namespace).List(api.ListOptions{})
593+
if err != nil {
594+
return false, nil
595+
}
596+
for _, pod := range podList.Items {
597+
for _, fedPod := range fedPods {
598+
if strings.HasPrefix(pod.Name, fedPod) && pod.Status.Phase == "Running" {
599+
podCheck -= 1
600+
}
601+
}
602+
//ensure that all pods are in running state or keep waiting
603+
if podCheck == 0 {
604+
return true, nil
605+
}
606+
}
607+
return false, nil
608+
})
609+
return err
610+
}
611+
612+
func waitSrvHealthy(config util.AdminConfig, context, kubeconfig string) error {
613+
fedClientSet, err := config.FederationClientset(context, kubeconfig)
614+
if err != nil {
615+
return err
616+
}
617+
fedDiscoveryClient := fedClientSet.Discovery()
618+
err = wait.PollInfinite(podWaitInterval, func() (bool, error) {
619+
body, err := fedDiscoveryClient.RESTClient().Get().AbsPath("/healthz").Do().Raw()
620+
if err != nil {
621+
return false, nil
622+
}
623+
if strings.EqualFold(string(body), "ok") {
624+
return true, nil
625+
}
626+
return false, nil
627+
})
628+
return err
629+
}
630+
579631
func printSuccess(cmdOut io.Writer, ips, hostnames []string) error {
580632
svcEndpoints := append(ips, hostnames...)
581633
_, err := fmt.Fprintf(cmdOut, "Federation API server is running at: %s\n", strings.Join(svcEndpoints, ", "))

federation/pkg/kubefed/init/init_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,38 @@ func fakeInitHostFactory(federationName, namespaceName, ip, dnsZoneName, image,
693693
},
694694
}
695695

696+
podList := v1.PodList{}
697+
apiServerPod := v1.Pod{
698+
TypeMeta: metav1.TypeMeta{
699+
Kind: "Pod",
700+
APIVersion: testapi.Extensions.GroupVersion().String(),
701+
},
702+
ObjectMeta: v1.ObjectMeta{
703+
Name: svcName,
704+
Namespace: namespaceName,
705+
},
706+
Status: v1.PodStatus{
707+
Phase: "Running",
708+
},
709+
}
710+
711+
cmPod := v1.Pod{
712+
TypeMeta: metav1.TypeMeta{
713+
Kind: "Pod",
714+
APIVersion: testapi.Extensions.GroupVersion().String(),
715+
},
716+
ObjectMeta: v1.ObjectMeta{
717+
Name: cmName,
718+
Namespace: namespaceName,
719+
},
720+
Status: v1.PodStatus{
721+
Phase: "Running",
722+
},
723+
}
724+
725+
podList.Items = append(podList.Items, apiServerPod)
726+
podList.Items = append(podList.Items, cmPod)
727+
696728
f, tf, codec, _ := cmdtesting.NewAPIFactory()
697729
extCodec := testapi.Extensions.Codec()
698730
ns := dynamic.ContentConfig().NegotiatedSerializer
@@ -701,6 +733,8 @@ func fakeInitHostFactory(federationName, namespaceName, ip, dnsZoneName, image,
701733
NegotiatedSerializer: ns,
702734
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
703735
switch p, m := req.URL.Path, req.Method; {
736+
case p == "/healthz":
737+
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte("ok")))}, nil
704738
case p == "/api/v1/namespaces" && m == http.MethodPost:
705739
body, err := ioutil.ReadAll(req.Body)
706740
if err != nil {
@@ -794,6 +828,8 @@ func fakeInitHostFactory(federationName, namespaceName, ip, dnsZoneName, image,
794828
return nil, fmt.Errorf("Unexpected deployment object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, want))
795829
}
796830
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(extCodec, &want)}, nil
831+
case p == "/api/v1/namespaces/federation-system/pods" && m == http.MethodGet:
832+
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &podList)}, nil
797833
default:
798834
return nil, fmt.Errorf("unexpected request: %#v\n%#v", req.URL, req)
799835
}

federation/pkg/kubefed/testing/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ go_library(
1212
srcs = ["testing.go"],
1313
tags = ["automanaged"],
1414
deps = [
15+
"//federation/client/clientset_generated/federation_clientset:go_default_library",
1516
"//federation/pkg/kubefed/util:go_default_library",
1617
"//pkg/api:go_default_library",
1718
"//pkg/apimachinery/registered:go_default_library",

federation/pkg/kubefed/testing/testing.go

+13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"net/http"
2424
"os"
2525

26+
fedclient "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
2627
"k8s.io/kubernetes/federation/pkg/kubefed/util"
2728
"k8s.io/kubernetes/pkg/api"
2829
"k8s.io/kubernetes/pkg/apimachinery/registered"
@@ -53,6 +54,18 @@ func (f *fakeAdminConfig) PathOptions() *clientcmd.PathOptions {
5354
return f.pathOptions
5455
}
5556

57+
func (f *fakeAdminConfig) FederationClientset(context, kubeconfigPath string) (*fedclient.Clientset, error) {
58+
fakeRestClient, err := f.hostFactory.RESTClient()
59+
if err != nil {
60+
return nil, err
61+
}
62+
63+
// we ignore the function params and use the client from
64+
// the same fakefactory to create a federation clientset
65+
// our fake factory exposes only the healthz api for this client
66+
return fedclient.New(fakeRestClient), nil
67+
}
68+
5669
func (f *fakeAdminConfig) HostFactory(host, kubeconfigPath string) cmdutil.Factory {
5770
return f.hostFactory
5871
}

federation/pkg/kubefed/util/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ go_library(
1212
srcs = ["util.go"],
1313
tags = ["automanaged"],
1414
deps = [
15+
"//federation/client/clientset_generated/federation_clientset:go_default_library",
1516
"//pkg/api:go_default_library",
1617
"//pkg/client/clientset_generated/internalclientset:go_default_library",
1718
"//pkg/client/unversioned/clientcmd:go_default_library",

federation/pkg/kubefed/util/util.go

+24-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package util
1818

1919
import (
20+
fedclient "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
2021
"k8s.io/kubernetes/pkg/api"
2122
client "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
2223
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
@@ -39,13 +40,16 @@ const (
3940

4041
// AdminConfig provides a filesystem based kubeconfig (via
4142
// `PathOptions()`) and a mechanism to talk to the federation
42-
// host cluster.
43+
// host cluster and the federation control plane api server.
4344
type AdminConfig interface {
4445
// PathOptions provides filesystem based kubeconfig access.
4546
PathOptions() *clientcmd.PathOptions
47+
// FedClientSet provides a federation API compliant clientset
48+
// to communicate with the federation control plane api server
49+
FederationClientset(context, kubeconfigPath string) (*fedclient.Clientset, error)
4650
// HostFactory provides a mechanism to communicate with the
4751
// cluster where federation control plane is hosted.
48-
HostFactory(host, kubeconfigPath string) cmdutil.Factory
52+
HostFactory(hostcontext, kubeconfigPath string) cmdutil.Factory
4953
}
5054

5155
// adminConfig implements the AdminConfig interface.
@@ -64,17 +68,30 @@ func (a *adminConfig) PathOptions() *clientcmd.PathOptions {
6468
return a.pathOptions
6569
}
6670

67-
func (a *adminConfig) HostFactory(host, kubeconfigPath string) cmdutil.Factory {
71+
func (a *adminConfig) FederationClientset(context, kubeconfigPath string) (*fedclient.Clientset, error) {
72+
fedConfig := a.getClientConfig(context, kubeconfigPath)
73+
fedClientConfig, err := fedConfig.ClientConfig()
74+
if err != nil {
75+
return nil, err
76+
}
77+
78+
return fedclient.NewForConfigOrDie(fedClientConfig), nil
79+
}
80+
81+
func (a *adminConfig) HostFactory(hostcontext, kubeconfigPath string) cmdutil.Factory {
82+
hostClientConfig := a.getClientConfig(hostcontext, kubeconfigPath)
83+
return cmdutil.NewFactory(hostClientConfig)
84+
}
85+
86+
func (a *adminConfig) getClientConfig(context, kubeconfigPath string) clientcmd.ClientConfig {
6887
loadingRules := *a.pathOptions.LoadingRules
6988
loadingRules.Precedence = a.pathOptions.GetLoadingPrecedence()
7089
loadingRules.ExplicitPath = kubeconfigPath
7190
overrides := &clientcmd.ConfigOverrides{
72-
CurrentContext: host,
91+
CurrentContext: context,
7392
}
7493

75-
hostClientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides)
76-
77-
return cmdutil.NewFactory(hostClientConfig)
94+
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides)
7895
}
7996

8097
// SubcommandFlags holds the flags required by the subcommands of

0 commit comments

Comments
 (0)