kubernetes/cmd/kubeadm/app/cmd/init.go

197 lines
6.7 KiB
Go

/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"fmt"
"io"
"net"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/api"
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
netutil "k8s.io/kubernetes/pkg/util/net"
)
var (
initDoneMsgf = dedent.Dedent(`
Kubernetes master initialised successfully!
You can now join any number of machines by running the following on each node:
kubeadm join --token %s %s
`)
)
// NewCmdInit returns "kubeadm init" command.
func NewCmdInit(out io.Writer, s *kubeadmapi.KubeadmConfig) *cobra.Command {
advertiseAddrs := &[]string{}
cmd := &cobra.Command{
Use: "init",
Short: "Run this on the first machine.",
Run: func(cmd *cobra.Command, args []string) {
err := RunInit(out, cmd, args, s, advertiseAddrs)
cmdutil.CheckErr(err)
},
}
cmd.PersistentFlags().StringVar(
&s.Secrets.GivenToken, "token", "",
`(optional) Shared secret used to secure cluster bootstrap. If none is provided, one will be generated for you.`,
)
cmd.PersistentFlags().StringSliceVar(
advertiseAddrs, "api-advertise-addresses", []string{},
`(optional) The IP addresses to advertise, in case autodetection fails.`,
)
cmd.PersistentFlags().StringSliceVar(
&s.InitFlags.API.ExternalDNSNames, "api-external-dns-names", []string{},
`(optional) The DNS names to advertise, in case you have configured them yourself.`,
)
cmd.PersistentFlags().IPNetVar(
&s.InitFlags.Services.CIDR, "service-cidr", *kubeadmapi.DefaultServicesCIDR,
`(optional) Use alterantive range of IP address for service VIPs, defaults to `+
kubeadmapi.DefaultServicesCIDRString,
)
cmd.PersistentFlags().IPNetVar(
&s.InitFlags.PodNetwork.CIDR, "pod-network-cidr", net.IPNet{},
`(optional) Specify range of IP addresses for the pod network. If set, the control plane will automatically allocate CIDRs for every node.`,
)
cmd.PersistentFlags().StringVar(
&s.InitFlags.Services.DNSDomain, "service-dns-domain", kubeadmapi.DefaultServiceDNSDomain,
`(optional) Use alternative domain for services, e.g. "myorg.internal"`,
)
cmd.PersistentFlags().StringVar(
&s.InitFlags.CloudProvider, "cloud-provider", "",
`(optional) Enable a specific cloud provider features (external load-balancers, storage, etc), e.g. "gce"`,
)
cmd.PersistentFlags().BoolVar(
&s.InitFlags.Schedulable, "schedule-pods-here", false,
`(optional) Allow to schedule workload to the node`,
)
// TODO (phase1+) @errordeveloper make the flags below not show up in --help but rather on --advanced-help
cmd.PersistentFlags().StringSliceVar(
&s.InitFlags.API.Etcd.ExternalEndpoints, "external-etcd-endpoints", []string{},
`(optional) etcd endpoints to use, in case you have an external cluster.`,
)
cmd.PersistentFlags().StringVar(
&s.InitFlags.API.Etcd.ExternalCAFile, "external-etcd-cafile", "",
`(optional) etcd certificate authority certificate file."`,
)
cmd.PersistentFlags().StringVar(
&s.InitFlags.API.Etcd.ExternalCertFile, "external-etcd-certfile", "",
`(optional) etcd client certificate file."`,
)
cmd.PersistentFlags().StringVar(
&s.InitFlags.API.Etcd.ExternalKeyFile, "external-etcd-keyfile", "",
`(optional) etcd client key file."`,
)
return cmd
}
// RunInit executes master node provisioning, including certificates, needed static pod manifests, etc.
func RunInit(out io.Writer, cmd *cobra.Command, args []string, s *kubeadmapi.KubeadmConfig, advertiseAddrs *[]string) error {
// Auto-detect the IP
if len(*advertiseAddrs) == 0 {
// TODO(phase1+) perhaps we could actually grab eth0 and eth1
ip, err := netutil.ChooseHostInterface()
if err != nil {
return err
}
s.InitFlags.API.AdvertiseAddrs = []net.IP{ip}
} else {
for _, i := range *advertiseAddrs {
addr := net.ParseIP(i)
if addr == nil {
return fmt.Errorf("<cmd/init> failed to parse flag (%q) as an IP address", "--api-advertise-addresses="+i)
}
s.InitFlags.API.AdvertiseAddrs = append(s.InitFlags.API.AdvertiseAddrs, addr)
}
}
if s.InitFlags.CloudProvider != "" {
// TODO(phase2) we should be able to auto-detect it and check whether things like IAM roles are correct
if _, ok := kubeadmapi.SupportedCloudProviders[s.InitFlags.CloudProvider]; !ok {
return fmt.Errorf("<cmd/init> cloud provider %q is not supported, you can use any of %v, or leave it unset", s.InitFlags.CloudProvider, kubeadmapi.ListOfCloudProviders)
}
}
if err := kubemaster.CreateTokenAuthFile(s); err != nil {
return err
}
if err := kubemaster.WriteStaticPodManifests(s); err != nil {
return err
}
caKey, caCert, err := kubemaster.CreatePKIAssets(s)
if err != nil {
return err
}
kubeconfigs, err := kubemaster.CreateCertsAndConfigForClients(s, []string{"kubelet", "admin"}, caKey, caCert)
if err != nil {
return err
}
// kubeadm is responsible for writing the following kubeconfig file, which
// kubelet should be waiting for. Help user avoid foot-shooting by refusing to
// write a file that has already been written (the kubelet will be up and
// running in that case - they'd need to stop the kubelet, remove the file, and
// start it again in that case).
// TODO(phase1+) this is no longer the right place to guard agains foo-shooting,
// we need to decide how to handle existing files (it may be handy to support
// importing existing files, may be we could even make our command idempotant,
// or at least allow for external PKI and stuff)
for name, kubeconfig := range kubeconfigs {
if err := kubeadmutil.WriteKubeconfigIfNotExists(s, name, kubeconfig); err != nil {
return err
}
}
client, err := kubemaster.CreateClientAndWaitForAPI(kubeconfigs["admin"])
if err != nil {
return err
}
if err := kubemaster.UpdateMasterRoleLabelsAndTaints(client, s.Schedulable); err != nil {
return err
}
if err := kubemaster.CreateDiscoveryDeploymentAndSecret(s, client, caCert); err != nil {
return err
}
if err := kubemaster.CreateEssentialAddons(s, client); err != nil {
return err
}
// TODO(phase1+) use templates to reference struct fields directly as order of args is fragile
fmt.Fprintf(out, initDoneMsgf,
s.Secrets.GivenToken,
s.InitFlags.API.AdvertiseAddrs[0].String(),
)
return nil
}