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)
} |