/* Copyright 2014 Google Inc. All rights reserved. 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 standalone import ( "fmt" "math" "net" "net/http" "os" "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" minionControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller" "github.com/GoogleCloudPlatform/kubernetes/pkg/controller" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools" "github.com/GoogleCloudPlatform/kubernetes/pkg/master" "github.com/GoogleCloudPlatform/kubernetes/pkg/resources" "github.com/GoogleCloudPlatform/kubernetes/pkg/service" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler" "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory" "github.com/golang/glog" ) type delegateHandler struct { delegate http.Handler } func (h *delegateHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { if h.delegate != nil { h.delegate.ServeHTTP(w, req) return } w.WriteHeader(http.StatusNotFound) } // Get a docker endpoint, either from the string passed in, or $DOCKER_HOST environment variables func GetDockerEndpoint(dockerEndpoint string) string { var endpoint string if len(dockerEndpoint) > 0 { endpoint = dockerEndpoint } else if len(os.Getenv("DOCKER_HOST")) > 0 { endpoint = os.Getenv("DOCKER_HOST") } else { endpoint = "unix:///var/run/docker.sock" } glog.Infof("Connecting to docker on %s", endpoint) return endpoint } // RunApiServer starts an API server in a go routine. func RunApiServer(cl *client.Client, etcdClient tools.EtcdClient, addr string, port int) { handler := delegateHandler{} helper, err := master.NewEtcdHelper(etcdClient, "") if err != nil { glog.Fatalf("Unable to get etcd helper: %v", err) } // Create a master and install handlers into mux. m := master.New(&master.Config{ Client: cl, EtcdHelper: helper, KubeletClient: &client.HTTPKubeletClient{ Client: http.DefaultClient, Port: 10250, }, EnableLogsSupport: false, APIPrefix: "/api", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), ReadWritePort: port, ReadOnlyPort: port, PublicAddress: addr, }) handler.delegate = m.InsecureHandler go http.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), &handler) } // RunScheduler starts up a scheduler in it's own goroutine func RunScheduler(cl *client.Client) { // Scheduler schedulerConfigFactory := &factory.ConfigFactory{cl} schedulerConfig := schedulerConfigFactory.Create() scheduler.New(schedulerConfig).Run() } // RunControllerManager starts a controller func RunControllerManager(machineList []string, cl *client.Client, nodeMilliCPU, nodeMemory int64) { if int64(int(nodeMilliCPU)) != nodeMilliCPU { glog.Warningf("node_milli_cpu is too big for platform. Clamping: %d -> %d", nodeMilliCPU, math.MaxInt32) nodeMilliCPU = math.MaxInt32 } if int64(int(nodeMemory)) != nodeMemory { glog.Warningf("node_memory is too big for platform. Clamping: %d -> %d", nodeMemory, math.MaxInt32) nodeMemory = math.MaxInt32 } nodeResources := &api.NodeResources{ Capacity: api.ResourceList{ resources.CPU: util.NewIntOrStringFromInt(int(nodeMilliCPU)), resources.Memory: util.NewIntOrStringFromInt(int(nodeMemory)), }, } minionController := minionControllerPkg.NewMinionController(nil, "", machineList, nodeResources, cl) minionController.Run(10 * time.Second) endpoints := service.NewEndpointController(cl) go util.Forever(func() { endpoints.SyncServiceEndpoints() }, time.Second*10) controllerManager := controller.NewReplicationManager(cl) controllerManager.Run(10 * time.Second) } // SimpleRunKubelet is a simple way to start a Kubelet talking to dockerEndpoint, using an etcdClient. // Under the hood it calls RunKubelet (below) func SimpleRunKubelet(etcdClient tools.EtcdClient, dockerClient dockertools.DockerInterface, hostname, rootDir, manifestURL, address string, port uint) { kcfg := KubeletConfig{ EtcdClient: etcdClient, DockerClient: dockerClient, HostnameOverride: hostname, RootDirectory: rootDir, ManifestURL: manifestURL, NetworkContainerImage: kubelet.NetworkContainerImage, Port: port, Address: util.IP(net.ParseIP(address)), EnableServer: true, EnableDebuggingHandlers: true, } RunKubelet(&kcfg) } // RunKubelet is responsible for setting up and running a kubelet. It is used in three different applications: // 1 Integration tests // 2 Kubelet binary // 3 Standalone 'kubernetes' binary // Eventually, #2 will be replaced with instances of #3 func RunKubelet(kcfg *KubeletConfig) { kubelet.SetupEventSending(kcfg.AuthPath, kcfg.ApiServerList) kubelet.SetupLogging() kubelet.SetupCapabilities(kcfg.AllowPrivileged) kcfg.Hostname = kubelet.GetHostname(kcfg.HostnameOverride) if len(kcfg.RootDirectory) > 0 { kubelet.SetupRootDirectoryOrDie(kcfg.RootDirectory) } cfg := makePodSourceConfig(kcfg) k := createAndInitKubelet(kcfg) // process pods and exit. if kcfg.Runonce { if _, err := k.RunOnce(cfg.Updates()); err != nil { glog.Errorf("--runonce failed: %v", err) } } else { startKubelet(k, cfg, kcfg) } } func startKubelet(k *kubelet.Kubelet, cfg *config.PodConfig, kc *KubeletConfig) { // start the kubelet go util.Forever(func() { k.Run(cfg.Updates()) }, 0) // start the kubelet server if kc.EnableServer { go util.Forever(func() { kubelet.ListenAndServeKubeletServer(k, cfg.Channel("http"), net.IP(kc.Address), kc.Port, kc.EnableDebuggingHandlers) }, 0) } } func makePodSourceConfig(kc *KubeletConfig) *config.PodConfig { // source of all configuration cfg := config.NewPodConfig(config.PodConfigNotificationSnapshotAndUpdates) // define file config source if kc.ConfigFile != "" { config.NewSourceFile(kc.ConfigFile, kc.FileCheckFrequency, cfg.Channel("file")) } // define url config source if kc.ManifestURL != "" { config.NewSourceURL(kc.ManifestURL, kc.HttpCheckFrequency, cfg.Channel("http")) } if kc.EtcdClient != nil { glog.Infof("Watching for etcd configs at %v", kc.EtcdClient.GetCluster()) config.NewSourceEtcd(config.EtcdKeyForHost(kc.Hostname), kc.EtcdClient, cfg.Channel("etcd")) } return cfg } type KubeletConfig struct { EtcdClient tools.EtcdClient DockerClient dockertools.DockerInterface Address util.IP AuthPath string ApiServerList util.StringList AllowPrivileged bool HostnameOverride string RootDirectory string ConfigFile string ManifestURL string FileCheckFrequency time.Duration HttpCheckFrequency time.Duration Hostname string NetworkContainerImage string SyncFrequency time.Duration RegistryPullQPS float64 RegistryBurst int MinimumGCAge time.Duration MaxContainerCount int EnableServer bool EnableDebuggingHandlers bool Port uint Runonce bool } func createAndInitKubelet(kc *KubeletConfig) *kubelet.Kubelet { // TODO: block until all sources have delivered at least one update to the channel, or break the sync loop // up into "per source" synchronizations k := kubelet.NewMainKubelet( kc.Hostname, kc.DockerClient, kc.EtcdClient, kc.RootDirectory, kc.NetworkContainerImage, kc.SyncFrequency, float32(kc.RegistryPullQPS), kc.RegistryBurst, kc.MinimumGCAge, kc.MaxContainerCount) k.BirthCry() go kubelet.GarbageCollectLoop(k) go kubelet.MonitorCAdvisor(k) kubelet.InitHealthChecking(k) return k }