mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-15 06:43:54 +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("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().Bool("dry-run", false, "dry run without sending commands to server.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -142,6 +143,7 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
||||
image := cmdutil.GetFlagString(cmd, "image")
|
||||
dnsProvider := cmdutil.GetFlagString(cmd, "dns-provider")
|
||||
etcdPVCapacity := cmdutil.GetFlagString(cmd, "etcd-pv-capacity")
|
||||
dryRun := cmdutil.GetDryRunFlag(cmd)
|
||||
|
||||
hostFactory := config.HostFactory(initFlags.Host, initFlags.Kubeconfig)
|
||||
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)
|
||||
|
||||
// 1. Create a namespace for federation system components
|
||||
_, err = createNamespace(hostClientset, initFlags.FederationSystemNamespace)
|
||||
_, err = createNamespace(hostClientset, initFlags.FederationSystemNamespace, dryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
ips, hostnames, err := waitForLoadBalancerAddress(hostClientset, svc)
|
||||
ips, hostnames, err := waitForLoadBalancerAddress(hostClientset, svc, dryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -176,13 +178,13 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = createAPIServerCredentialsSecret(hostClientset, initFlags.FederationSystemNamespace, serverCredName, entKeyPairs)
|
||||
_, err = createAPIServerCredentialsSecret(hostClientset, initFlags.FederationSystemNamespace, serverCredName, entKeyPairs, dryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
// API server's state. This is where federation API server's etcd
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
@ -208,38 +210,46 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
// 8. Write the federation API server endpoint info, credentials
|
||||
// and context to kubeconfig
|
||||
err = updateKubeconfig(config, initFlags.Name, endpoint, entKeyPairs)
|
||||
err = updateKubeconfig(config, initFlags.Name, endpoint, entKeyPairs, dryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
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{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
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)
|
||||
}
|
||||
|
||||
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{}
|
||||
hostnames := []string{}
|
||||
|
||||
if dryRun {
|
||||
return ips, hostnames, nil
|
||||
}
|
||||
|
||||
err := wait.PollImmediateInfinite(lbAddrRetryInterval, func() (bool, error) {
|
||||
pollSvc, err := clientset.Core().Services(svc.Namespace).Get(svc.Name)
|
||||
if err != nil {
|
||||
@ -319,7 +337,7 @@ func genCerts(svcNamespace, name, svcName, localDNSZoneName string, ips, hostnam
|
||||
}, 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.
|
||||
secret := &api.Secret{
|
||||
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.
|
||||
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(
|
||||
name,
|
||||
fmt.Sprintf("https://%s", svcName),
|
||||
@ -352,10 +373,10 @@ func createControllerManagerKubeconfigSecret(clientset *client.Clientset, namesp
|
||||
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)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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{
|
||||
"/hyperkube",
|
||||
"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)
|
||||
}
|
||||
|
||||
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{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
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)
|
||||
}
|
||||
|
||||
@ -555,7 +587,7 @@ func printSuccess(cmdOut io.Writer, ips, hostnames []string) error {
|
||||
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()
|
||||
kubeconfig, err := po.GetStartingConfig()
|
||||
if err != nil {
|
||||
@ -588,10 +620,12 @@ func updateKubeconfig(config util.AdminConfig, name, endpoint string, entKeyPair
|
||||
kubeconfig.AuthInfos[name] = authInfo
|
||||
kubeconfig.Contexts[name] = context
|
||||
|
||||
if !dryRun {
|
||||
// Write the update kubeconfig.
|
||||
if err := clientcmd.ModifyConfig(po, *kubeconfig, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ func TestInitFederation(t *testing.T) {
|
||||
etcdPVCapacity string
|
||||
expectedErr string
|
||||
dnsProvider string
|
||||
dryRun string
|
||||
}{
|
||||
{
|
||||
federation: "union",
|
||||
@ -90,6 +91,7 @@ func TestInitFederation(t *testing.T) {
|
||||
etcdPVCapacity: "5Gi",
|
||||
expectedErr: "",
|
||||
dnsProvider: "test-dns-provider",
|
||||
dryRun: "",
|
||||
},
|
||||
{
|
||||
federation: "union",
|
||||
@ -101,9 +103,24 @@ func TestInitFederation(t *testing.T) {
|
||||
etcdPVCapacity: "", //test for default value of pvc-size
|
||||
expectedErr: "",
|
||||
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 {
|
||||
cmdErrMsg = ""
|
||||
dnsProvider = ""
|
||||
@ -130,12 +147,16 @@ func TestInitFederation(t *testing.T) {
|
||||
cmd.Flags().Set("host-cluster-context", "substrate")
|
||||
cmd.Flags().Set("dns-zone-name", tc.dnsZoneName)
|
||||
cmd.Flags().Set("image", tc.image)
|
||||
if "" != tc.dnsProvider {
|
||||
if tc.dnsProvider != "" {
|
||||
cmd.Flags().Set("dns-provider", tc.dnsProvider)
|
||||
}
|
||||
if "" != tc.etcdPVCapacity {
|
||||
if 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})
|
||||
|
||||
if tc.expectedErr == "" {
|
||||
@ -143,6 +164,10 @@ func TestInitFederation(t *testing.T) {
|
||||
// Actual data passed are tested in the fake secret and cluster
|
||||
// REST clients.
|
||||
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 {
|
||||
t.Errorf("[%d] unexpected output: got: %s, want: %s", i, got, want)
|
||||
if cmdErrMsg != "" {
|
||||
|
Loading…
Reference in New Issue
Block a user