-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
1,138 additions
and
838 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package server | ||
|
||
import ( | ||
"fmt" | ||
_ "net/http/pprof" | ||
|
||
"github.com/golang/glog" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/openshift/origin/pkg/cmd/flagtypes" | ||
cmdutil "github.com/openshift/origin/pkg/cmd/util" | ||
"github.com/openshift/origin/pkg/cmd/util/docker" | ||
"github.com/openshift/origin/pkg/cmd/util/variable" | ||
) | ||
|
||
const longCommandDesc = ` | ||
Start an OpenShift server | ||
This command helps you launch an OpenShift server. The default mode is all-in-one, which allows | ||
you to run all of the components of an OpenShift system on a server with Docker. Running | ||
$ openshift start | ||
will start OpenShift listening on all interfaces, launch an etcd server to store persistent | ||
data, and launch the Kubernetes system components. The server will run in the foreground until | ||
you terminate the process. | ||
Note: starting OpenShift without passing the --master address will attempt to find the IP | ||
address that will be visible inside running Docker containers. This is not always successful, | ||
so if you have problems tell OpenShift what public address it will be via --master=<ip>. | ||
You may also pass an optional argument to the start command to start OpenShift in one of the | ||
following roles: | ||
$ openshift start master --nodes=<host1,host2,host3,...> | ||
Launches the server and control plane for OpenShift. You may pass a list of the node | ||
hostnames you want to use, or create nodes via the REST API or 'openshift kube'. | ||
$ openshift start node --master=<masterIP> | ||
Launches a new node and attempts to connect to the master on the provided IP. | ||
You may also pass --etcd=<address> to connect to an external etcd server instead of running an | ||
integrated instance, or --kubernetes=<addr> and --kubeconfig=<path> to connect to an existing | ||
Kubernetes cluster. | ||
` | ||
|
||
// NewCommandStartServer provides a CLI handler for 'start' command | ||
func NewCommandStartServer(name string) *cobra.Command { | ||
hostname, err := defaultHostname() | ||
if err != nil { | ||
hostname = "localhost" | ||
glog.Warningf("Unable to lookup hostname, using %q: %v", hostname, err) | ||
} | ||
|
||
cfg := &config{ | ||
Docker: docker.NewHelper(), | ||
|
||
MasterAddr: flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(), | ||
BindAddr: flagtypes.Addr{Value: "0.0.0.0:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(), | ||
EtcdAddr: flagtypes.Addr{Value: "0.0.0.0:4001", DefaultScheme: "http", DefaultPort: 4001}.Default(), | ||
KubernetesAddr: flagtypes.Addr{DefaultScheme: "https", DefaultPort: 8443}.Default(), | ||
PortalNet: flagtypes.DefaultIPNet("172.30.17.0/24"), | ||
MasterPublicAddr: flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(), | ||
KubernetesPublicAddr: flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(), | ||
|
||
ImageTemplate: variable.NewDefaultImageTemplate(), | ||
|
||
Hostname: hostname, | ||
NodeList: flagtypes.StringList{"127.0.0.1"}, | ||
} | ||
|
||
cmd := &cobra.Command{ | ||
Use: fmt.Sprintf("%s [master|node]", name), | ||
Short: "Launch OpenShift", | ||
Long: longCommandDesc, | ||
Run: func(c *cobra.Command, args []string) { | ||
cfg.Complete(args) | ||
|
||
if err := start(*cfg, args); err != nil { | ||
glog.Fatal(err) | ||
} | ||
}, | ||
} | ||
|
||
flag := cmd.Flags() | ||
|
||
flag.Var(&cfg.BindAddr, "listen", "The address to listen for connections on (host, host:port, or URL).") | ||
flag.Var(&cfg.MasterAddr, "master", "The master address for use by OpenShift components (host, host:port, or URL). Scheme and port default to the --listen scheme and port.") | ||
flag.Var(&cfg.MasterPublicAddr, "public-master", "The master address for use by public clients, if different (host, host:port, or URL). Defaults to same as --master.") | ||
flag.Var(&cfg.EtcdAddr, "etcd", "The address of the etcd server (host, host:port, or URL). If specified, no built-in etcd will be started.") | ||
flag.Var(&cfg.KubernetesAddr, "kubernetes", "The address of the Kubernetes server (host, host:port, or URL). If specified, no Kubernetes components will be started.") | ||
flag.Var(&cfg.KubernetesPublicAddr, "public-kubernetes", "The Kubernetes server address for use by public clients, if different. (host, host:port, or URL). Defaults to same as --kubernetes.") | ||
flag.Var(&cfg.PortalNet, "portal-net", "A CIDR notation IP range from which to assign portal IPs. This must not overlap with any IP ranges assigned to nodes for pods.") | ||
|
||
flag.StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "When fetching images used by the cluster for important components, use this format on both master and nodes. The latest release will be used by default.") | ||
flag.BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest images for the cluster instead of the latest release.") | ||
|
||
flag.StringVar(&cfg.VolumeDir, "volume-dir", "openshift.local.volumes", "The volume storage directory.") | ||
flag.StringVar(&cfg.EtcdDir, "etcd-dir", "openshift.local.etcd", "The etcd data directory.") | ||
flag.StringVar(&cfg.CertDir, "cert-dir", "openshift.local.certificates", "The certificate data directory.") | ||
|
||
flag.StringVar(&cfg.Hostname, "hostname", cfg.Hostname, "The hostname to identify this node with the master.") | ||
flag.Var(&cfg.NodeList, "nodes", "The hostnames of each node. This currently must be specified up front. Comma delimited list") | ||
flag.Var(&cfg.CORSAllowedOrigins, "cors-allowed-origins", "List of allowed origins for CORS, comma separated. An allowed origin can be a regular expression to support subdomain matching. CORS is enabled for localhost, 127.0.0.1, and the asset server by default.") | ||
|
||
cfg.ClientConfig = cmdutil.DefaultClientConfig(flag) | ||
|
||
cfg.Docker.InstallFlags(flag) | ||
|
||
return cmd | ||
} | ||
|
||
const startMaster = "master" | ||
const startNode = "startNode" | ||
|
||
// Complete takes the args and fills in information for the start config | ||
func (cfg *config) Complete(args []string) { | ||
cfg.ExplicitStartMaster = (len(args) == 1) && (args[0] == startMaster) | ||
cfg.ExplicitStartNode = (len(args) == 1) && (args[0] == startNode) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
package server | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
_ "net/http/pprof" | ||
"net/url" | ||
"os/exec" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools" | ||
etcdclient "github.com/coreos/go-etcd/etcd" | ||
"github.com/golang/glog" | ||
|
||
"github.com/openshift/origin/pkg/api/latest" | ||
"github.com/openshift/origin/pkg/cmd/flagtypes" | ||
"github.com/openshift/origin/pkg/cmd/util" | ||
"github.com/openshift/origin/pkg/cmd/util/docker" | ||
"github.com/openshift/origin/pkg/cmd/util/variable" | ||
) | ||
|
||
// config is a struct that the command stores flag values into. | ||
type config struct { | ||
Docker *docker.Helper | ||
|
||
ExplicitStartNode bool | ||
ExplicitStartMaster bool | ||
|
||
MasterAddr flagtypes.Addr | ||
BindAddr flagtypes.Addr | ||
EtcdAddr flagtypes.Addr | ||
KubernetesAddr flagtypes.Addr | ||
PortalNet flagtypes.IPNet | ||
// addresses for external clients | ||
MasterPublicAddr flagtypes.Addr | ||
KubernetesPublicAddr flagtypes.Addr | ||
|
||
ImageFormat string | ||
LatestReleaseImages bool | ||
|
||
ImageTemplate variable.ImageTemplate | ||
|
||
Hostname string | ||
VolumeDir string | ||
|
||
EtcdDir string | ||
|
||
CertDir string | ||
|
||
StorageVersion string | ||
|
||
NodeList flagtypes.StringList | ||
|
||
// ClientConfig is used when connecting to Kubernetes from the master, or | ||
// when connecting to the master from a detached node. If the server is an | ||
// all-in-one, this value is not used. | ||
ClientConfig clientcmd.ClientConfig | ||
|
||
CORSAllowedOrigins flagtypes.StringList | ||
} | ||
|
||
// GetMasterAddress checks for an unset master address and then attempts to use the first | ||
// public IPv4 non-loopback address registered on this host. It will also update the | ||
// EtcdAddr after if it was not provided. | ||
// TODO: make me IPv6 safe | ||
func (cfg config) GetMasterAddress() (*url.URL, error) { | ||
if cfg.MasterAddr.Provided { | ||
return cfg.MasterAddr.URL, nil | ||
} | ||
|
||
if cfg.IsStartMaster() { | ||
// If the user specifies a bind address, and the master is not provided, use the bind port by default | ||
port := cfg.MasterAddr.Port | ||
if cfg.BindAddr.Provided { | ||
port = cfg.BindAddr.Port | ||
} | ||
|
||
// If the user specifies a bind address, and the master is not provided, use the bind scheme by default | ||
scheme := cfg.MasterAddr.URL.Scheme | ||
if cfg.BindAddr.Provided { | ||
scheme = cfg.BindAddr.URL.Scheme | ||
} | ||
|
||
// use the default ip address for the system | ||
addr, err := util.DefaultLocalIP4() | ||
if err != nil { | ||
return nil, fmt.Errorf("Unable to find the public address of this master: %v", err) | ||
} | ||
|
||
masterAddr := scheme + "://" + net.JoinHostPort(addr.String(), strconv.Itoa(port)) | ||
return url.Parse(masterAddr) | ||
} | ||
|
||
// if we didn't specify and we aren't starting the master, read .kubeconfig to locate the master | ||
// TODO client config currently doesn't let you override the defaults | ||
// so it is defaulting to https://localhost:8443 for MasterAddr if | ||
// it isn't set by --master or --kubeconfig | ||
config, err := cfg.ClientConfig.ClientConfig() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return url.Parse(config.Host) | ||
} | ||
|
||
func (cfg config) GetMasterPublicAddress() (*url.URL, error) { | ||
if cfg.MasterPublicAddr.Provided { | ||
return cfg.MasterPublicAddr.URL, nil | ||
} | ||
|
||
return cfg.GetMasterAddress() | ||
} | ||
|
||
func (cfg config) GetEtcdAddress() (*url.URL, error) { | ||
if cfg.EtcdAddr.Provided { | ||
return cfg.EtcdAddr.URL, nil | ||
} | ||
|
||
// Etcd should be reachable on the same address that the master is (for simplicity) | ||
masterAddr, err := cfg.GetMasterAddress() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
etcdAddr := net.JoinHostPort(getHost(*masterAddr), strconv.Itoa(cfg.EtcdAddr.DefaultPort)) | ||
return url.Parse("http://" + etcdAddr) | ||
} | ||
|
||
func (cfg config) GetKubernetesAddress() (*url.URL, error) { | ||
if cfg.KubernetesAddr.Provided { | ||
return cfg.KubernetesAddr.URL, nil | ||
} | ||
|
||
return cfg.GetMasterAddress() | ||
} | ||
|
||
func (cfg config) GetKubernetesPublicAddress() (*url.URL, error) { | ||
if cfg.KubernetesPublicAddr.Provided { | ||
return cfg.KubernetesPublicAddr.URL, nil | ||
} | ||
|
||
return cfg.GetMasterPublicAddress() | ||
} | ||
|
||
func (cfg config) GetNodeList() []string { | ||
nodeList := []string{} | ||
for _, curr := range cfg.NodeList { | ||
nodeList = append(curr) | ||
} | ||
|
||
if len(nodeList) == 1 && nodeList[0] == "127.0.0.1" { | ||
nodeList[0] = cfg.Hostname | ||
} | ||
for i, s := range nodeList { | ||
s = strings.ToLower(s) | ||
nodeList[i] = s | ||
glog.Infof(" Node: %s", s) | ||
} | ||
|
||
return nodeList | ||
} | ||
|
||
func (cfg config) IsStartNode() bool { | ||
if cfg.ExplicitStartMaster { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (cfg config) IsStartMaster() bool { | ||
if cfg.ExplicitStartNode { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (cfg config) IsStartKube() bool { | ||
if cfg.ExplicitStartNode { | ||
return false | ||
} | ||
if cfg.KubernetesAddr.Provided { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (cfg config) IsStartEtcd() bool { | ||
if cfg.ExplicitStartNode { | ||
return false | ||
} | ||
if cfg.EtcdAddr.Provided { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
// getEtcdClient creates an etcd client based on the provided config and waits | ||
// until etcd server is reachable. It errors out and exits if the server cannot | ||
// be reached for a certain amount of time. | ||
func (cfg config) getEtcdClient() (*etcdclient.Client, error) { | ||
address, err := cfg.GetEtcdAddress() | ||
if err != nil { | ||
return nil, err | ||
} | ||
etcdServers := []string{address.String()} | ||
etcdClient := etcdclient.NewClient(etcdServers) | ||
|
||
for i := 0; ; i++ { | ||
_, err := etcdClient.Get("/", false, false) | ||
if err == nil || tools.IsEtcdNotFound(err) { | ||
break | ||
} | ||
if i > 100 { | ||
return nil, fmt.Errorf("Could not reach etcd: %v", err) | ||
} | ||
time.Sleep(50 * time.Millisecond) | ||
} | ||
|
||
return etcdClient, nil | ||
} | ||
|
||
// newOpenShiftEtcdHelper returns an EtcdHelper for the provided arguments or an error if the version | ||
// is incorrect. | ||
func (cfg config) newOpenShiftEtcdHelper() (helper tools.EtcdHelper, err error) { | ||
// Connect and setup etcd interfaces | ||
client, err := cfg.getEtcdClient() | ||
if err != nil { | ||
return tools.EtcdHelper{}, err | ||
} | ||
|
||
version := cfg.StorageVersion | ||
if len(version) == 0 { | ||
version = latest.Version | ||
} | ||
interfaces, err := latest.InterfacesFor(version) | ||
if err != nil { | ||
return helper, err | ||
} | ||
return tools.EtcdHelper{client, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.MetadataAccessor}}, nil | ||
} | ||
|
||
// defaultHostname returns the default hostname for this system. | ||
func defaultHostname() (string, error) { | ||
// Note: We use exec here instead of os.Hostname() because we | ||
// want the FQDN, and this is the easiest way to get it. | ||
fqdn, err := exec.Command("hostname", "-f").Output() | ||
if err != nil { | ||
return "", fmt.Errorf("Couldn't determine hostname: %v", err) | ||
} | ||
return strings.TrimSpace(string(fqdn)), nil | ||
} |
Oops, something went wrong.