mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-15 14:53:44 +00:00
Merge pull request #36447 from irfanurrehman/federation-kubefed-todo-2
Automatic merge from submit-queue [Federation] Implement dry run support in kubefed init This one implements one of the TODO items pending in the previous set of kubefed PRs. This one is done on top of another todo PR https://github.com/kubernetes/kubernetes/pull/36310 which is being reviewed separately. Please review only the last 2 commits in this one. The design doc PR for kubefed is at https://github.com/kubernetes/kubernetes/pull/34484. cc @kubernetes/sig-cluster-federation @madhusudancs @nikhiljindal **Release note**: <!-- Steps to write your release note: 1. Use the release-note-* labels to set the release note state (if you have access) 2. Enter your extended release note in the below block; leaving it blank means using the PR title as the release note. If no release note is required, just write `NONE`. --> ``` [Federation] `kubefed init` now supports dry run mode. ```
This commit is contained in:
commit
8182f178f6
@ -120,6 +120,7 @@ func NewCmdInit(cmdOut io.Writer, config util.AdminConfig) *cobra.Command {
|
|||||||
cmd.Flags().String("image", defaultImage, "Image to use for federation API server and controller manager binaries.")
|
cmd.Flags().String("image", defaultImage, "Image to use for federation API server and controller manager binaries.")
|
||||||
cmd.Flags().String("dns-provider", "google-clouddns", "Dns provider to be used for this deployment.")
|
cmd.Flags().String("dns-provider", "google-clouddns", "Dns provider to be used for this deployment.")
|
||||||
cmd.Flags().String("etcd-pv-capacity", "10Gi", "Size of persistent volume claim to be used for etcd.")
|
cmd.Flags().String("etcd-pv-capacity", "10Gi", "Size of persistent volume claim to be used for etcd.")
|
||||||
|
cmd.Flags().Bool("dry-run", false, "dry run without sending commands to server.")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +143,7 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
|||||||
image := cmdutil.GetFlagString(cmd, "image")
|
image := cmdutil.GetFlagString(cmd, "image")
|
||||||
dnsProvider := cmdutil.GetFlagString(cmd, "dns-provider")
|
dnsProvider := cmdutil.GetFlagString(cmd, "dns-provider")
|
||||||
etcdPVCapacity := cmdutil.GetFlagString(cmd, "etcd-pv-capacity")
|
etcdPVCapacity := cmdutil.GetFlagString(cmd, "etcd-pv-capacity")
|
||||||
|
dryRun := cmdutil.GetDryRunFlag(cmd)
|
||||||
|
|
||||||
hostFactory := config.HostFactory(initFlags.Host, initFlags.Kubeconfig)
|
hostFactory := config.HostFactory(initFlags.Host, initFlags.Kubeconfig)
|
||||||
hostClientset, err := hostFactory.ClientSet()
|
hostClientset, err := hostFactory.ClientSet()
|
||||||
@ -155,17 +157,17 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
|||||||
cmKubeconfigName := fmt.Sprintf("%s-kubeconfig", cmName)
|
cmKubeconfigName := fmt.Sprintf("%s-kubeconfig", cmName)
|
||||||
|
|
||||||
// 1. Create a namespace for federation system components
|
// 1. Create a namespace for federation system components
|
||||||
_, err = createNamespace(hostClientset, initFlags.FederationSystemNamespace)
|
_, err = createNamespace(hostClientset, initFlags.FederationSystemNamespace, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Expose a network endpoint for the federation API server
|
// 2. Expose a network endpoint for the federation API server
|
||||||
svc, err := createService(hostClientset, initFlags.FederationSystemNamespace, serverName)
|
svc, err := createService(hostClientset, initFlags.FederationSystemNamespace, serverName, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ips, hostnames, err := waitForLoadBalancerAddress(hostClientset, svc)
|
ips, hostnames, err := waitForLoadBalancerAddress(hostClientset, svc, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -176,13 +178,13 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = createAPIServerCredentialsSecret(hostClientset, initFlags.FederationSystemNamespace, serverCredName, entKeyPairs)
|
_, err = createAPIServerCredentialsSecret(hostClientset, initFlags.FederationSystemNamespace, serverCredName, entKeyPairs, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Create a kubeconfig secret
|
// 4. Create a kubeconfig secret
|
||||||
_, err = createControllerManagerKubeconfigSecret(hostClientset, initFlags.FederationSystemNamespace, initFlags.Name, svc.Name, cmKubeconfigName, entKeyPairs)
|
_, err = createControllerManagerKubeconfigSecret(hostClientset, initFlags.FederationSystemNamespace, initFlags.Name, svc.Name, cmKubeconfigName, entKeyPairs, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -190,7 +192,7 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
|||||||
// 5. Create a persistent volume and a claim to store the federation
|
// 5. Create a persistent volume and a claim to store the federation
|
||||||
// API server's state. This is where federation API server's etcd
|
// API server's state. This is where federation API server's etcd
|
||||||
// stores its data.
|
// stores its data.
|
||||||
pvc, err := createPVC(hostClientset, initFlags.FederationSystemNamespace, svc.Name, etcdPVCapacity)
|
pvc, err := createPVC(hostClientset, initFlags.FederationSystemNamespace, svc.Name, etcdPVCapacity, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -208,38 +210,46 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 6. Create federation API server
|
// 6. Create federation API server
|
||||||
_, err = createAPIServer(hostClientset, initFlags.FederationSystemNamespace, serverName, image, serverCredName, pvc.Name, advertiseAddress)
|
_, err = createAPIServer(hostClientset, initFlags.FederationSystemNamespace, serverName, image, serverCredName, pvc.Name, advertiseAddress, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Create federation controller manager
|
// 7. Create federation controller manager
|
||||||
_, err = createControllerManager(hostClientset, initFlags.FederationSystemNamespace, initFlags.Name, svc.Name, cmName, image, cmKubeconfigName, dnsZoneName, dnsProvider)
|
_, err = createControllerManager(hostClientset, initFlags.FederationSystemNamespace, initFlags.Name, svc.Name, cmName, image, cmKubeconfigName, dnsZoneName, dnsProvider, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Write the federation API server endpoint info, credentials
|
// 8. Write the federation API server endpoint info, credentials
|
||||||
// and context to kubeconfig
|
// and context to kubeconfig
|
||||||
err = updateKubeconfig(config, initFlags.Name, endpoint, entKeyPairs)
|
err = updateKubeconfig(config, initFlags.Name, endpoint, entKeyPairs, dryRun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return printSuccess(cmdOut, ips, hostnames)
|
if !dryRun {
|
||||||
|
return printSuccess(cmdOut, ips, hostnames)
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintf(cmdOut, "Federation control plane runs (dry run)\n")
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNamespace(clientset *client.Clientset, namespace string) (*api.Namespace, error) {
|
func createNamespace(clientset *client.Clientset, namespace string, dryRun bool) (*api.Namespace, error) {
|
||||||
ns := &api.Namespace{
|
ns := &api.Namespace{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Name: namespace,
|
Name: namespace,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
|
||||||
return clientset.Core().Namespaces().Create(ns)
|
return clientset.Core().Namespaces().Create(ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createService(clientset *client.Clientset, namespace, svcName string) (*api.Service, error) {
|
func createService(clientset *client.Clientset, namespace, svcName string, dryRun bool) (*api.Service, error) {
|
||||||
svc := &api.Service{
|
svc := &api.Service{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Name: svcName,
|
Name: svcName,
|
||||||
@ -260,13 +270,21 @@ func createService(clientset *client.Clientset, namespace, svcName string) (*api
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return svc, nil
|
||||||
|
}
|
||||||
|
|
||||||
return clientset.Core().Services(namespace).Create(svc)
|
return clientset.Core().Services(namespace).Create(svc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForLoadBalancerAddress(clientset *client.Clientset, svc *api.Service) ([]string, []string, error) {
|
func waitForLoadBalancerAddress(clientset *client.Clientset, svc *api.Service, dryRun bool) ([]string, []string, error) {
|
||||||
ips := []string{}
|
ips := []string{}
|
||||||
hostnames := []string{}
|
hostnames := []string{}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return ips, hostnames, nil
|
||||||
|
}
|
||||||
|
|
||||||
err := wait.PollImmediateInfinite(lbAddrRetryInterval, func() (bool, error) {
|
err := wait.PollImmediateInfinite(lbAddrRetryInterval, func() (bool, error) {
|
||||||
pollSvc, err := clientset.Core().Services(svc.Namespace).Get(svc.Name)
|
pollSvc, err := clientset.Core().Services(svc.Namespace).Get(svc.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -319,7 +337,7 @@ func genCerts(svcNamespace, name, svcName, localDNSZoneName string, ips, hostnam
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAPIServerCredentialsSecret(clientset *client.Clientset, namespace, credentialsName string, entKeyPairs *entityKeyPairs) (*api.Secret, error) {
|
func createAPIServerCredentialsSecret(clientset *client.Clientset, namespace, credentialsName string, entKeyPairs *entityKeyPairs, dryRun bool) (*api.Secret, error) {
|
||||||
// Build the secret object with API server credentials.
|
// Build the secret object with API server credentials.
|
||||||
secret := &api.Secret{
|
secret := &api.Secret{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
@ -333,11 +351,14 @@ func createAPIServerCredentialsSecret(clientset *client.Clientset, namespace, cr
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return secret, nil
|
||||||
|
}
|
||||||
// Boilerplate to create the secret in the host cluster.
|
// Boilerplate to create the secret in the host cluster.
|
||||||
return clientset.Core().Secrets(namespace).Create(secret)
|
return clientset.Core().Secrets(namespace).Create(secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createControllerManagerKubeconfigSecret(clientset *client.Clientset, namespace, name, svcName, kubeconfigName string, entKeyPairs *entityKeyPairs) (*api.Secret, error) {
|
func createControllerManagerKubeconfigSecret(clientset *client.Clientset, namespace, name, svcName, kubeconfigName string, entKeyPairs *entityKeyPairs, dryRun bool) (*api.Secret, error) {
|
||||||
basicClientConfig := kubeadmutil.CreateBasicClientConfig(
|
basicClientConfig := kubeadmutil.CreateBasicClientConfig(
|
||||||
name,
|
name,
|
||||||
fmt.Sprintf("https://%s", svcName),
|
fmt.Sprintf("https://%s", svcName),
|
||||||
@ -352,10 +373,10 @@ func createControllerManagerKubeconfigSecret(clientset *client.Clientset, namesp
|
|||||||
certutil.EncodeCertPEM(entKeyPairs.controllerManager.Cert),
|
certutil.EncodeCertPEM(entKeyPairs.controllerManager.Cert),
|
||||||
)
|
)
|
||||||
|
|
||||||
return util.CreateKubeconfigSecret(clientset, config, namespace, kubeconfigName, false)
|
return util.CreateKubeconfigSecret(clientset, config, namespace, kubeconfigName, dryRun)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPVC(clientset *client.Clientset, namespace, svcName, etcdPVCapacity string) (*api.PersistentVolumeClaim, error) {
|
func createPVC(clientset *client.Clientset, namespace, svcName, etcdPVCapacity string, dryRun bool) (*api.PersistentVolumeClaim, error) {
|
||||||
capacity, err := resource.ParseQuantity(etcdPVCapacity)
|
capacity, err := resource.ParseQuantity(etcdPVCapacity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -382,10 +403,14 @@ func createPVC(clientset *client.Clientset, namespace, svcName, etcdPVCapacity s
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return pvc, nil
|
||||||
|
}
|
||||||
|
|
||||||
return clientset.Core().PersistentVolumeClaims(namespace).Create(pvc)
|
return clientset.Core().PersistentVolumeClaims(namespace).Create(pvc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAPIServer(clientset *client.Clientset, namespace, name, image, credentialsName, pvcName, advertiseAddress string) (*extensions.Deployment, error) {
|
func createAPIServer(clientset *client.Clientset, namespace, name, image, credentialsName, pvcName, advertiseAddress string, dryRun bool) (*extensions.Deployment, error) {
|
||||||
command := []string{
|
command := []string{
|
||||||
"/hyperkube",
|
"/hyperkube",
|
||||||
"federation-apiserver",
|
"federation-apiserver",
|
||||||
@ -480,10 +505,14 @@ func createAPIServer(clientset *client.Clientset, namespace, name, image, creden
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return dep, nil
|
||||||
|
}
|
||||||
|
|
||||||
return clientset.Extensions().Deployments(namespace).Create(dep)
|
return clientset.Extensions().Deployments(namespace).Create(dep)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createControllerManager(clientset *client.Clientset, namespace, name, svcName, cmName, image, kubeconfigName, dnsZoneName, dnsProvider string) (*extensions.Deployment, error) {
|
func createControllerManager(clientset *client.Clientset, namespace, name, svcName, cmName, image, kubeconfigName, dnsZoneName, dnsProvider string, dryRun bool) (*extensions.Deployment, error) {
|
||||||
dep := &extensions.Deployment{
|
dep := &extensions.Deployment{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Name: cmName,
|
Name: cmName,
|
||||||
@ -546,6 +575,9 @@ func createControllerManager(clientset *client.Clientset, namespace, name, svcNa
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
return dep, nil
|
||||||
|
}
|
||||||
return clientset.Extensions().Deployments(namespace).Create(dep)
|
return clientset.Extensions().Deployments(namespace).Create(dep)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,7 +587,7 @@ func printSuccess(cmdOut io.Writer, ips, hostnames []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateKubeconfig(config util.AdminConfig, name, endpoint string, entKeyPairs *entityKeyPairs) error {
|
func updateKubeconfig(config util.AdminConfig, name, endpoint string, entKeyPairs *entityKeyPairs, dryRun bool) error {
|
||||||
po := config.PathOptions()
|
po := config.PathOptions()
|
||||||
kubeconfig, err := po.GetStartingConfig()
|
kubeconfig, err := po.GetStartingConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -588,9 +620,11 @@ func updateKubeconfig(config util.AdminConfig, name, endpoint string, entKeyPair
|
|||||||
kubeconfig.AuthInfos[name] = authInfo
|
kubeconfig.AuthInfos[name] = authInfo
|
||||||
kubeconfig.Contexts[name] = context
|
kubeconfig.Contexts[name] = context
|
||||||
|
|
||||||
// Write the update kubeconfig.
|
if !dryRun {
|
||||||
if err := clientcmd.ModifyConfig(po, *kubeconfig, true); err != nil {
|
// Write the update kubeconfig.
|
||||||
return err
|
if err := clientcmd.ModifyConfig(po, *kubeconfig, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -79,6 +79,7 @@ func TestInitFederation(t *testing.T) {
|
|||||||
etcdPVCapacity string
|
etcdPVCapacity string
|
||||||
expectedErr string
|
expectedErr string
|
||||||
dnsProvider string
|
dnsProvider string
|
||||||
|
dryRun string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
federation: "union",
|
federation: "union",
|
||||||
@ -90,6 +91,7 @@ func TestInitFederation(t *testing.T) {
|
|||||||
etcdPVCapacity: "5Gi",
|
etcdPVCapacity: "5Gi",
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
dnsProvider: "test-dns-provider",
|
dnsProvider: "test-dns-provider",
|
||||||
|
dryRun: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
federation: "union",
|
federation: "union",
|
||||||
@ -101,9 +103,24 @@ func TestInitFederation(t *testing.T) {
|
|||||||
etcdPVCapacity: "", //test for default value of pvc-size
|
etcdPVCapacity: "", //test for default value of pvc-size
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
dnsProvider: "", //test for default value of dns provider
|
dnsProvider: "", //test for default value of dns provider
|
||||||
|
dryRun: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
federation: "union",
|
||||||
|
kubeconfigGlobal: fakeKubeFiles[0],
|
||||||
|
kubeconfigExplicit: "",
|
||||||
|
dnsZoneName: "example.test.",
|
||||||
|
lbIP: "10.20.30.40",
|
||||||
|
image: "example.test/foo:bar",
|
||||||
|
etcdPVCapacity: "",
|
||||||
|
expectedErr: "",
|
||||||
|
dnsProvider: "test-dns-provider",
|
||||||
|
dryRun: "valid-run",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: implement a negative case for dry run
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
cmdErrMsg = ""
|
cmdErrMsg = ""
|
||||||
dnsProvider = ""
|
dnsProvider = ""
|
||||||
@ -130,12 +147,16 @@ func TestInitFederation(t *testing.T) {
|
|||||||
cmd.Flags().Set("host-cluster-context", "substrate")
|
cmd.Flags().Set("host-cluster-context", "substrate")
|
||||||
cmd.Flags().Set("dns-zone-name", tc.dnsZoneName)
|
cmd.Flags().Set("dns-zone-name", tc.dnsZoneName)
|
||||||
cmd.Flags().Set("image", tc.image)
|
cmd.Flags().Set("image", tc.image)
|
||||||
if "" != tc.dnsProvider {
|
if tc.dnsProvider != "" {
|
||||||
cmd.Flags().Set("dns-provider", tc.dnsProvider)
|
cmd.Flags().Set("dns-provider", tc.dnsProvider)
|
||||||
}
|
}
|
||||||
if "" != tc.etcdPVCapacity {
|
if tc.etcdPVCapacity != "" {
|
||||||
cmd.Flags().Set("etcd-pv-capacity", tc.etcdPVCapacity)
|
cmd.Flags().Set("etcd-pv-capacity", tc.etcdPVCapacity)
|
||||||
}
|
}
|
||||||
|
if tc.dryRun == "valid-run" {
|
||||||
|
cmd.Flags().Set("dry-run", "true")
|
||||||
|
}
|
||||||
|
|
||||||
cmd.Run(cmd, []string{tc.federation})
|
cmd.Run(cmd, []string{tc.federation})
|
||||||
|
|
||||||
if tc.expectedErr == "" {
|
if tc.expectedErr == "" {
|
||||||
@ -143,6 +164,10 @@ func TestInitFederation(t *testing.T) {
|
|||||||
// Actual data passed are tested in the fake secret and cluster
|
// Actual data passed are tested in the fake secret and cluster
|
||||||
// REST clients.
|
// REST clients.
|
||||||
want := fmt.Sprintf("Federation API server is running at: %s\n", tc.lbIP)
|
want := fmt.Sprintf("Federation API server is running at: %s\n", tc.lbIP)
|
||||||
|
if tc.dryRun != "" {
|
||||||
|
want = fmt.Sprintf("Federation control plane runs (dry run)\n")
|
||||||
|
}
|
||||||
|
|
||||||
if got := buf.String(); got != want {
|
if got := buf.String(); got != want {
|
||||||
t.Errorf("[%d] unexpected output: got: %s, want: %s", i, got, want)
|
t.Errorf("[%d] unexpected output: got: %s, want: %s", i, got, want)
|
||||||
if cmdErrMsg != "" {
|
if cmdErrMsg != "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user