pkg/cmd/server/start/start_master.go
30380339
 package start
 
 import (
 	"errors"
 	"fmt"
94085a36
 	"io"
30380339
 	"io/ioutil"
86e9e5c0
 	"os"
40be29be
 	"path"
86e9e5c0
 	"path/filepath"
30380339
 	"strings"
 
 	"github.com/coreos/go-systemd/daemon"
 	"github.com/golang/glog"
 	"github.com/spf13/cobra"
 
83c702b4
 	kerrors "k8s.io/kubernetes/pkg/api/errors"
 	"k8s.io/kubernetes/pkg/capabilities"
 	"k8s.io/kubernetes/pkg/kubelet"
 	"k8s.io/kubernetes/pkg/util"
30380339
 
ad916a82
 	"github.com/openshift/origin/pkg/cmd/server/admin"
30380339
 	configapi "github.com/openshift/origin/pkg/cmd/server/api"
 	configapilatest "github.com/openshift/origin/pkg/cmd/server/api/latest"
a611e136
 	"github.com/openshift/origin/pkg/cmd/server/api/validation"
ad916a82
 	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
30380339
 	"github.com/openshift/origin/pkg/cmd/server/etcd"
 	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
 	"github.com/openshift/origin/pkg/cmd/server/origin"
 	cmdutil "github.com/openshift/origin/pkg/cmd/util"
5d5f852e
 	"github.com/openshift/origin/pkg/version"
30380339
 )
 
 type MasterOptions struct {
 	MasterArgs *MasterArgs
 
40be29be
 	CreateCertificates bool
 	ConfigFile         string
7b23556d
 	Output             io.Writer
642797f7
 	DisabledFeatures   []string
30380339
 }
 
c3e1dbba
 const masterLong = `Start a master server
30380339
 
c3e1dbba
 This command helps you launch a master server.  Running
30380339
 
c3e1dbba
   $ %[1]s start master
30380339
 
c3e1dbba
 will start a master listening on all interfaces, launch an etcd server to store
97e7c5ce
 persistent data, and launch the Kubernetes system components. The server will run in the
30380339
 foreground until you terminate the process.
 
c3e1dbba
 Note: starting the master without passing the --master address will attempt to find the IP
30380339
 address that will be visible inside running Docker containers. This is not always successful,
c3e1dbba
 so if you have problems tell the master what public address it should use via --master=<ip>.
30380339
 
c3e1dbba
 You may also pass an optional argument to the start command to start in one of the
30380339
 following roles:
 
c3e1dbba
   // Launches the server and control plane. You may pass a list of the node
   // hostnames you want to use, or create nodes via the REST API or '%[1]s kube'.
   $ %[1]s start master --nodes=<host1,host2,host3,...>
30380339
 
 You may also pass --etcd=<address> to connect to an external etcd server.
 
1558f2d9
 You may also pass --kubeconfig=<path> to connect to an external Kubernetes cluster.`
30380339
 
5f3f5b85
 // NewCommandStartMaster provides a CLI handler for 'start master' command
c3e1dbba
 func NewCommandStartMaster(fullName string, out io.Writer) (*cobra.Command, *MasterOptions) {
7b23556d
 	options := &MasterOptions{Output: out}
30380339
 
642797f7
 	switch fullName {
 	case "atomic-enterprise":
 		options.DisabledFeatures = configapi.AtomicDisabledFeatures
 	}
 
30380339
 	cmd := &cobra.Command{
 		Use:   "master",
 		Short: "Launch OpenShift master",
c3e1dbba
 		Long:  fmt.Sprintf(masterLong, fullName),
30380339
 		Run: func(c *cobra.Command, args []string) {
 			if err := options.Complete(); err != nil {
 				fmt.Println(err.Error())
 				c.Help()
 				return
 			}
 
 			if err := options.Validate(args); err != nil {
 				fmt.Println(err.Error())
 				c.Help()
 				return
 			}
 
c54050fc
 			startProfiler()
 
30380339
 			if err := options.StartMaster(); err != nil {
59d1d06e
 				if kerrors.IsInvalid(err) {
 					if details := err.(*kerrors.StatusError).ErrStatus.Details; details != nil {
d3282b30
 						fmt.Fprintf(options.Output, "Invalid %s %s\n", details.Kind, details.Name)
59d1d06e
 						for _, cause := range details.Causes {
1c28113c
 							fmt.Fprintf(options.Output, "  %s: %s\n", cause.Field, cause.Message)
59d1d06e
 						}
 						os.Exit(255)
 					}
 				}
30380339
 				glog.Fatal(err)
 			}
 		},
 	}
 
40be29be
 	options.MasterArgs = NewDefaultMasterArgs()
 
30380339
 	flags := cmd.Flags()
 
40be29be
 	flags.Var(options.MasterArgs.ConfigDir, "write-config", "Directory to write an initial config into.  After writing, exit without starting the server.")
 	flags.StringVar(&options.ConfigFile, "config", "", "Location of the master configuration file to run from. When running from a configuration file, all other command-line arguments are ignored.")
 	flags.BoolVar(&options.CreateCertificates, "create-certs", true, "Indicates whether missing certs should be created")
30380339
 
 	BindMasterArgs(options.MasterArgs, flags, "")
620cbcfc
 	BindListenArg(options.MasterArgs.ListenArg, flags, "")
30380339
 	BindImageFormatArgs(options.MasterArgs.ImageFormatArgs, flags, "")
 	BindKubeConnectionArgs(options.MasterArgs.KubeConnectionArgs, flags, "")
b7d2fe8a
 	BindNetworkArgs(options.MasterArgs.NetworkArgs, flags, "")
30380339
 
ea725ace
 	// autocompletion hints
 	cmd.MarkFlagFilename("write-config")
 	cmd.MarkFlagFilename("config", "yaml", "yml")
 
30380339
 	return cmd, options
 }
 
 func (o MasterOptions) Validate(args []string) error {
 	if len(args) != 0 {
 		return errors.New("no arguments are supported for start master")
 	}
40be29be
 	if o.IsWriteConfigOnly() {
 		if o.IsRunFromConfig() {
 			return errors.New("--config may not be set if --write-config is set")
30380339
 		}
 	}
 
40be29be
 	if len(o.MasterArgs.ConfigDir.Value()) == 0 {
 		return errors.New("configDir must have a value")
 	}
 
b017dbad
 	// if we are not starting up using a config file, run the argument validation
40be29be
 	if !o.IsRunFromConfig() {
b017dbad
 		if err := o.MasterArgs.Validate(); err != nil {
 			return err
 		}
 
2a811d88
 	}
 
30380339
 	return nil
 }
 
40be29be
 func (o *MasterOptions) Complete() error {
 	if !o.MasterArgs.ConfigDir.Provided() {
 		o.MasterArgs.ConfigDir.Default("openshift.local.config/master")
 	}
 
30380339
 	nodeList := util.NewStringSet()
 	// take everything toLower
 	for _, s := range o.MasterArgs.NodeList {
 		nodeList.Insert(strings.ToLower(s))
 	}
 
 	o.MasterArgs.NodeList = nodeList.List()
 
 	return nil
 }
 
 // StartMaster calls RunMaster and then waits forever
 func (o MasterOptions) StartMaster() error {
 	if err := o.RunMaster(); err != nil {
 		return err
 	}
 
40be29be
 	if o.IsWriteConfigOnly() {
30380339
 		return nil
 	}
 
97e7c5ce
 	go daemon.SdNotify("READY=1")
30380339
 	select {}
 
 	return nil
 }
 
 // RunMaster takes the options and:
 // 1.  Creates certs if needed
 // 2.  Reads fully specified master config OR builds a fully specified master config from the args
 // 3.  Writes the fully specified master config and exits if needed
 // 4.  Starts the master based on the fully specified config
 func (o MasterOptions) RunMaster() error {
40be29be
 	startUsingConfigFile := !o.IsWriteConfigOnly() && o.IsRunFromConfig()
30380339
 
40be29be
 	if !startUsingConfigFile && o.CreateCertificates {
7b23556d
 		glog.V(2).Infof("Generating master configuration")
30380339
 		if err := o.CreateCerts(); err != nil {
a52c9c16
 			return err
30380339
 		}
ad916a82
 		if err := o.CreateBootstrapPolicy(); err != nil {
a52c9c16
 			return err
ad916a82
 		}
 	}
30380339
 
 	var masterConfig *configapi.MasterConfig
 	var err error
 	if startUsingConfigFile {
1ce5be2c
 		masterConfig, err = configapilatest.ReadAndResolveMasterConfig(o.ConfigFile)
30380339
 	} else {
 		masterConfig, err = o.MasterArgs.BuildSerializeableMasterConfig()
 	}
 	if err != nil {
 		return err
 	}
 
40be29be
 	if o.IsWriteConfigOnly() {
86e9e5c0
 		// Resolve relative to CWD
 		cwd, err := os.Getwd()
 		if err != nil {
 			return err
 		}
 		if err := configapi.ResolveMasterConfigPaths(masterConfig, cwd); err != nil {
 			return err
 		}
 
 		// Relativize to config file dir
40be29be
 		base, err := cmdutil.MakeAbs(filepath.Dir(o.MasterArgs.GetConfigFileToWrite()), cwd)
86e9e5c0
 		if err != nil {
 			return err
 		}
 		if err := configapi.RelativizeMasterConfigPaths(masterConfig, base); err != nil {
 			return err
 		}
 
4457e6fd
 		content, err := configapilatest.WriteYAML(masterConfig)
30380339
 		if err != nil {
 			return err
 		}
40be29be
 
 		if err := os.MkdirAll(path.Dir(o.MasterArgs.GetConfigFileToWrite()), os.FileMode(0755)); err != nil {
 			return err
 		}
 		if err := ioutil.WriteFile(o.MasterArgs.GetConfigFileToWrite(), content, 0644); err != nil {
30380339
 			return err
 		}
40be29be
 
7b23556d
 		fmt.Fprintf(o.Output, "Wrote master config to: %s\n", o.MasterArgs.GetConfigFileToWrite())
40be29be
 
30380339
 		return nil
 	}
 
642797f7
 	// Inject disabled feature flags based on distribution being used and
 	// regardless of configuration. They aren't written to config file to
 	// prevent upgrade path issues.
 	masterConfig.DisabledFeatures.Add(o.DisabledFeatures...)
26c990fe
 	validationResults := validation.ValidateMasterConfig(masterConfig)
 	if len(validationResults.Warnings) != 0 {
 		for _, warning := range validationResults.Warnings {
 			glog.Warningf("%v", warning)
 		}
 	}
 	if len(validationResults.Errors) != 0 {
 		return kerrors.NewInvalid("MasterConfig", o.ConfigFile, validationResults.Errors)
a611e136
 	}
 
30380339
 	if err := StartMaster(masterConfig); err != nil {
 		return err
 	}
 
 	return nil
 }
 
ad916a82
 func (o MasterOptions) CreateBootstrapPolicy() error {
 	writeBootstrapPolicy := admin.CreateBootstrapPolicyFileOptions{
40be29be
 		File: o.MasterArgs.GetPolicyFile(),
ad916a82
 		OpenShiftSharedResourcesNamespace: bootstrappolicy.DefaultOpenShiftSharedResourcesNamespace,
 	}
 
 	return writeBootstrapPolicy.CreateBootstrapPolicyFile()
 }
 
30380339
 func (o MasterOptions) CreateCerts() error {
84222395
 	masterAddr, err := o.MasterArgs.GetMasterAddress()
 	if err != nil {
 		return err
 	}
 	publicMasterAddr, err := o.MasterArgs.GetMasterPublicAddress()
 	if err != nil {
 		return err
 	}
 
ad916a82
 	signerName := admin.DefaultSignerName()
30380339
 	hostnames, err := o.MasterArgs.GetServerCertHostnames()
 	if err != nil {
 		return err
 	}
28e73694
 	mintAllCertsOptions := admin.CreateMasterCertsOptions{
40be29be
 		CertDir:            o.MasterArgs.ConfigDir.Value(),
84222395
 		SignerName:         signerName,
 		Hostnames:          hostnames.List(),
 		APIServerURL:       masterAddr.String(),
 		PublicAPIServerURL: publicMasterAddr.String(),
94085a36
 		Output:             o.Output,
30380339
 	}
8e37e3dd
 	if err := mintAllCertsOptions.Validate(nil); err != nil {
 		return err
 	}
28e73694
 	if err := mintAllCertsOptions.CreateMasterCerts(); err != nil {
30380339
 		return err
 	}
 
 	return nil
 }
 
 func StartMaster(openshiftMasterConfig *configapi.MasterConfig) error {
c3e1dbba
 	glog.Infof("Starting master on %s (%s)", openshiftMasterConfig.ServingInfo.BindAddress, version.Get().String())
5d5f852e
 	glog.Infof("Public master address is %s", openshiftMasterConfig.AssetConfig.MasterPublicURL)
642797f7
 	if len(openshiftMasterConfig.DisabledFeatures) > 0 {
 		glog.V(4).Infof("Disabled features: %s", strings.Join(openshiftMasterConfig.DisabledFeatures, ", "))
 	}
30380339
 
 	if openshiftMasterConfig.EtcdConfig != nil {
53c7aa34
 		etcd.RunEtcd(openshiftMasterConfig.EtcdConfig)
30380339
 	}
 
 	// Allow privileged containers
 	// TODO: make this configurable and not the default https://github.com/openshift/origin/issues/662
 	capabilities.Initialize(capabilities.Capabilities{
2af8e2d2
 		AllowPrivileged:    true,
 		HostNetworkSources: []string{kubelet.ApiserverSource, kubelet.FileSource},
30380339
 	})
 
 	openshiftConfig, err := origin.BuildMasterConfig(*openshiftMasterConfig)
 	if err != nil {
 		return err
 	}
1f379f5d
 
 	go func() {
 		openshiftConfig.ControllerPlug.WaitForStop()
 		glog.Fatalf("Master shutdown requested")
 	}()
 
abd33eda
 	// Must start policy caching immediately
f4a107c4
 	openshiftConfig.RunGroupCache()
30380339
 	openshiftConfig.RunPolicyCache()
abd33eda
 	openshiftConfig.RunProjectCache()
30380339
 
ef2cc5f8
 	unprotectedInstallers := []origin.APIInstaller{}
 
4457e6fd
 	if openshiftMasterConfig.OAuthConfig != nil {
 		authConfig, err := origin.BuildAuthConfig(*openshiftMasterConfig)
 		if err != nil {
 			return err
 		}
 		unprotectedInstallers = append(unprotectedInstallers, authConfig)
30380339
 	}
ef2cc5f8
 
22045ca3
 	var standaloneAssetConfig *origin.AssetConfig
ef2cc5f8
 	if openshiftMasterConfig.AssetConfig != nil {
22045ca3
 		config, err := origin.BuildAssetConfig(*openshiftMasterConfig.AssetConfig)
ef2cc5f8
 		if err != nil {
 			return err
 		}
 
 		if openshiftMasterConfig.AssetConfig.ServingInfo.BindAddress == openshiftMasterConfig.ServingInfo.BindAddress {
22045ca3
 			unprotectedInstallers = append(unprotectedInstallers, config)
 		} else {
 			standaloneAssetConfig = config
ef2cc5f8
 		}
 	}
30380339
 
1f379f5d
 	var kubeConfig *kubernetes.MasterConfig
30380339
 	if openshiftMasterConfig.KubernetesMasterConfig != nil {
1f379f5d
 		kubeConfig, err = kubernetes.BuildKubernetesMasterConfig(*openshiftMasterConfig, openshiftConfig.RequestContextMapper, openshiftConfig.KubeClient())
30380339
 		if err != nil {
 			return err
 		}
 
ef2cc5f8
 		openshiftConfig.Run([]origin.APIInstaller{kubeConfig}, unprotectedInstallers)
30380339
 
 	} else {
da1980d3
 		_, kubeConfig, err := configapi.GetKubeClient(openshiftMasterConfig.MasterClients.ExternalKubernetesKubeConfig)
30380339
 		if err != nil {
 			return err
 		}
 
 		proxy := &kubernetes.ProxyConfig{
 			ClientConfig: kubeConfig,
 		}
 
ef2cc5f8
 		openshiftConfig.Run([]origin.APIInstaller{proxy}, unprotectedInstallers)
30380339
 	}
 
 	glog.Infof("Using images from %q", openshiftConfig.ImageFor("<component>"))
 
22045ca3
 	if standaloneAssetConfig != nil {
 		standaloneAssetConfig.Run()
ef2cc5f8
 	}
30380339
 	if openshiftMasterConfig.DNSConfig != nil {
 		openshiftConfig.RunDNSServer()
 	}
1f379f5d
 
30380339
 	openshiftConfig.RunProjectAuthorizationCache()
 
1f379f5d
 	if openshiftMasterConfig.Controllers != configapi.ControllersDisabled {
 		go func() {
 			openshiftConfig.ControllerPlug.WaitForStart()
 			glog.Infof("Master controllers starting (%s)", openshiftMasterConfig.Controllers)
1ae53e70
 
 			// Start these first, because they provide credentials for other controllers' clients
 			openshiftConfig.RunServiceAccountsController()
 			openshiftConfig.RunServiceAccountTokensController()
97e7c5ce
 			// used by admission controllers
 			openshiftConfig.RunServiceAccountPullSecretsControllers()
 			openshiftConfig.RunSecurityAllocationController()
1ae53e70
 
1f379f5d
 			if kubeConfig != nil {
1ae53e70
 				_, rcClient, err := openshiftConfig.GetServiceAccountClients(openshiftConfig.ReplicationControllerServiceAccount)
 				if err != nil {
 					glog.Fatalf("Could not get client for replication controller: %v", err)
 				}
 
97e7c5ce
 				// called by admission control
 				kubeConfig.RunResourceQuotaManager()
 
 				// no special order
 				kubeConfig.RunNodeController()
1f379f5d
 				kubeConfig.RunScheduler()
1ae53e70
 				kubeConfig.RunReplicationController(rcClient)
1f379f5d
 				kubeConfig.RunEndpointController()
 				kubeConfig.RunNamespaceController()
 				kubeConfig.RunPersistentVolumeClaimBinder()
4bb9bec3
 				kubeConfig.RunPersistentVolumeClaimRecycler(openshiftConfig.ImageFor("deployer"))
1f379f5d
 			}
 
97e7c5ce
 			// no special order
1f379f5d
 			openshiftConfig.RunBuildController()
 			openshiftConfig.RunBuildPodController()
 			openshiftConfig.RunBuildImageChangeTriggerController()
 			openshiftConfig.RunDeploymentController()
 			openshiftConfig.RunDeployerPodController()
 			openshiftConfig.RunDeploymentConfigController()
 			openshiftConfig.RunDeploymentConfigChangeController()
 			openshiftConfig.RunDeploymentImageChangeTriggerController()
 			openshiftConfig.RunImageImportController()
 			openshiftConfig.RunOriginNamespaceController()
 			openshiftConfig.RunSDNController()
 		}()
 	}
b7d2fe8a
 
30380339
 	return nil
 }
40be29be
 
 func (o MasterOptions) IsWriteConfigOnly() bool {
 	return o.MasterArgs.ConfigDir.Provided()
 }
 
 func (o MasterOptions) IsRunFromConfig() bool {
 	return (len(o.ConfigFile) > 0)
 }