// Runs e; will not return until stopCh is closed. workers determines how many // endpoints will be handled in parallel. func (e *endpointController) Run(workers int, stopCh <-chan struct{}) { defer util.HandleCrash() go e.serviceController.Run(stopCh) go e.podController.Run(stopCh) for i := 0; i < workers; i++ { go util.Until(e.worker, time.Second, stopCh) } go func() { defer util.HandleCrash() time.Sleep(5 * time.Minute) // give time for our cache to fill e.checkLeftoverEndpoints() }() <-stopCh e.queue.ShutDown() }
func (factory *ConfigFactory) makeDefaultErrorFunc(backoff *podBackoff, podQueue *cache.FIFO) func(pod *api.Pod, err error) { return func(pod *api.Pod, err error) { if err == scheduler.ErrNoNodesAvailable { glog.V(4).Infof("Unable to schedule %v %v: no nodes are registered to the cluster; waiting", pod.Namespace, pod.Name) } else { glog.Errorf("Error scheduling %v %v: %v; retrying", pod.Namespace, pod.Name, err) } backoff.gc() // Retry asynchronously. // Note that this is extremely rudimentary and we need a more real error handling path. go func() { defer util.HandleCrash() podID := pod.Name podNamespace := pod.Namespace backoff.wait(podID) // Get the pod again; it may have changed/been scheduled already. pod = &api.Pod{} err := factory.Client.Get().Namespace(podNamespace).Resource("pods").Name(podID).Do().Into(pod) if err != nil { if !errors.IsNotFound(err) { glog.Errorf("Error getting pod %v for retry: %v; abandoning", podID, err) } return } if pod.Spec.NodeName == "" { podQueue.Add(pod) } }() } }
// receive reads result from the decoder in a loop and sends down the result channel. func (sw *StreamWatcher) receive() { defer close(sw.result) defer sw.Stop() defer util.HandleCrash() for { action, obj, err := sw.source.Decode() if err != nil { // Ignore expected error. if sw.stopping() { return } switch err { case io.EOF: // watch closed normally case io.ErrUnexpectedEOF: glog.V(1).Infof("Unexpected EOF during watch stream event decoding: %v", err) default: msg := "Unable to decode an event from the watch stream: %v" if util.IsProbableEOF(err) { glog.V(5).Infof(msg, err) } else { glog.Errorf(msg, err) } } return } sw.result <- Event{ Type: action, Object: obj, } } }
func (cc *cadvisorClient) exportHTTP(port uint) error { mux := http.NewServeMux() err := cadvisorHttp.RegisterHandlers(mux, cc, "", "", "", "", "/metrics") if err != nil { return err } serv := &http.Server{ Addr: fmt.Sprintf(":%d", port), Handler: mux, } // TODO(vmarmol): Remove this when the cAdvisor port is once again free. // If export failed, retry in the background until we are able to bind. // This allows an existing cAdvisor to be killed before this one registers. go func() { defer util.HandleCrash() err := serv.ListenAndServe() for err != nil { glog.Infof("Failed to register cAdvisor on port %d, retrying. Error: %v", port, err) time.Sleep(time.Minute) err = serv.ListenAndServe() } }() return nil }
// translate pulls stuff from etcd, converts, and pushes out the outgoing channel. Meant to be // called as a goroutine. func (w *etcdWatcher) translate() { defer close(w.outgoing) defer util.HandleCrash() for { select { case err := <-w.etcdError: if err != nil { w.emit(watch.Event{ watch.Error, &api.Status{ Status: api.StatusFailure, Message: err.Error(), }, }) } return case <-w.userStop: w.etcdStop <- true return case res, ok := <-w.etcdIncoming: if ok { w.sendResult(res) } // If !ok, don't return here-- must wait for etcdError channel // to give an error or be closed. } } }
// addServiceOnPort starts listening for a new service, returning the serviceInfo. // Pass proxyPort=0 to allocate a random port. The timeout only applies to UDP // connections, for now. func (proxier *Proxier) addServiceOnPort(service ServicePortName, protocol api.Protocol, proxyPort int, timeout time.Duration) (*serviceInfo, error) { sock, err := newProxySocket(protocol, proxier.listenIP, proxyPort) if err != nil { return nil, err } _, portStr, err := net.SplitHostPort(sock.Addr().String()) if err != nil { sock.Close() return nil, err } portNum, err := strconv.Atoi(portStr) if err != nil { sock.Close() return nil, err } si := &serviceInfo{ proxyPort: portNum, protocol: protocol, socket: sock, timeout: timeout, sessionAffinityType: api.ServiceAffinityNone, // default stickyMaxAgeMinutes: 180, // TODO: paramaterize this in the API. } proxier.setServiceInfo(service, si) glog.V(2).Infof("Proxying for service %q on %s port %d", service, protocol, portNum) go func(service ServicePortName, proxier *Proxier) { defer util.HandleCrash() atomic.AddInt32(&proxier.numProxyLoops, 1) sock.ProxyLoop(service, si, proxier) atomic.AddInt32(&proxier.numProxyLoops, -1) }(service, proxier) return si, nil }
func (udp *udpProxySocket) getBackendConn(activeClients *clientCache, cliAddr net.Addr, proxier *Proxier, service ServicePortName, timeout time.Duration) (net.Conn, error) { activeClients.mu.Lock() defer activeClients.mu.Unlock() svrConn, found := activeClients.clients[cliAddr.String()] if !found { // TODO: This could spin up a new goroutine to make the outbound connection, // and keep accepting inbound traffic. glog.V(2).Infof("New UDP connection from %s", cliAddr) var err error svrConn, err = tryConnect(service, cliAddr, "udp", proxier) if err != nil { return nil, err } if err = svrConn.SetDeadline(time.Now().Add(timeout)); err != nil { glog.Errorf("SetDeadline failed: %v", err) return nil, err } activeClients.clients[cliAddr.String()] = svrConn go func(cliAddr net.Addr, svrConn net.Conn, activeClients *clientCache, timeout time.Duration) { defer util.HandleCrash() udp.proxyClient(cliAddr, svrConn, activeClients, timeout) }(cliAddr, svrConn, activeClients, timeout) } return svrConn, nil }
// implementation of scheduling plugin's Error func; see plugin/pkg/scheduler func (k *errorHandler) handleSchedulingError(pod *api.Pod, schedulingErr error) { if schedulingErr == noSuchPodErr { log.V(2).Infof("Not rescheduling non-existent pod %v", pod.Name) return } log.Infof("Error scheduling %v: %v; retrying", pod.Name, schedulingErr) defer util.HandleCrash() // default upstream scheduler passes pod.Name as binding.PodID ctx := api.WithNamespace(api.NewDefaultContext(), pod.Namespace) podKey, err := podtask.MakePodKey(ctx, pod.Name) if err != nil { log.Errorf("Failed to construct pod key, aborting scheduling for pod %v: %v", pod.Name, err) return } k.backoff.GC() k.api.Lock() defer k.api.Unlock() switch task, state := k.api.tasks().ForPod(podKey); state { case podtask.StateUnknown: // if we don't have a mapping here any more then someone deleted the pod log.V(2).Infof("Could not resolve pod to task, aborting pod reschdule: %s", podKey) return case podtask.StatePending: if task.Has(podtask.Launched) { log.V(2).Infof("Skipping re-scheduling for already-launched pod %v", podKey) return } breakoutEarly := queue.BreakChan(nil) if schedulingErr == noSuitableOffersErr { log.V(3).Infof("adding backoff breakout handler for pod %v", podKey) breakoutEarly = queue.BreakChan(k.api.offers().Listen(podKey, func(offer *mesos.Offer) bool { k.api.Lock() defer k.api.Unlock() switch task, state := k.api.tasks().Get(task.ID); state { case podtask.StatePending: return !task.Has(podtask.Launched) && task.AcceptOffer(offer) default: // no point in continuing to check for matching offers return true } })) } delay := k.backoff.Get(podKey) log.V(3).Infof("requeuing pod %v with delay %v", podKey, delay) k.qr.requeue(&Pod{Pod: pod, delay: &delay, notify: breakoutEarly}) default: log.V(2).Infof("Task is no longer pending, aborting reschedule for pod %v", podKey) } }
// Run begins watching and syncing. func (rm *ReplicationManager) Run(workers int, stopCh <-chan struct{}) { defer util.HandleCrash() go rm.rcController.Run(stopCh) go rm.podController.Run(stopCh) for i := 0; i < workers; i++ { go util.Until(rm.worker, time.Second, stopCh) } <-stopCh glog.Infof("Shutting down RC Manager") rm.queue.ShutDown() }
// spawn a goroutine to execute a func, immediately returns a chan that closes // upon completion of the func. returns a nil signal chan if the given func is nil. func After(f func()) Signal { ch := make(chan struct{}) go func() { defer close(ch) defer util.HandleCrash() if f != nil { f() } }() return Signal(ch) }
// Apply the new setting to the specified pod. updateComplete is called when the update is completed. func (p *podWorkers) UpdatePod(pod *api.Pod, mirrorPod *api.Pod, updateComplete func()) { uid := pod.UID var podUpdates chan workUpdate var exists bool // TODO: Pipe this through from the qinglet. Currently qinglets operating with // snapshot updates (PodConfigNotificationSnapshot) will send updates, creates // and deletes as SET operations, which makes updates indistinguishable from // creates. The intent here is to communicate to the pod worker that it can take // certain liberties, like skipping status generation, when it receives a create // event for a pod. updateType := SyncPodUpdate p.podLock.Lock() defer p.podLock.Unlock() if podUpdates, exists = p.podUpdates[uid]; !exists { // We need to have a buffer here, because checkForUpdates() method that // puts an update into channel is called from the same goroutine where // the channel is consumed. However, it is guaranteed that in such case // the channel is empty, so buffer of size 1 is enough. podUpdates = make(chan workUpdate, 1) p.podUpdates[uid] = podUpdates // Creating a new pod worker either means this is a new pod, or that the // qinglet just restarted. In either case the qinglet is willing to believe // the status of the pod for the first pod worker sync. See corresponding // comment in syncPod. updateType = SyncPodCreate go func() { defer util.HandleCrash() p.managePodLoop(podUpdates) }() } if !p.isWorking[pod.UID] { p.isWorking[pod.UID] = true podUpdates <- workUpdate{ pod: pod, mirrorPod: mirrorPod, updateCompleteFn: updateComplete, updateType: updateType, } } else { p.lastUndeliveredWorkUpdate[pod.UID] = workUpdate{ pod: pod, mirrorPod: mirrorPod, updateCompleteFn: updateComplete, updateType: updateType, } } }
// assumes that caller has obtained state lock func (k *QingYuanExecutor) doShutdown(driver bindings.ExecutorDriver) { defer func() { log.Errorf("exiting with unclean shutdown: %v", recover()) if k.exitFunc != nil { k.exitFunc(1) } }() (&k.state).transitionTo(terminalState) // signal to all listeners that this QingletExecutor is done! close(k.done) if k.shutdownAlert != nil { func() { util.HandleCrash() k.shutdownAlert() }() } log.Infoln("Stopping executor driver") _, err := driver.Stop() if err != nil { log.Warningf("failed to stop executor driver: %v", err) } log.Infoln("Shutdown the executor") // according to docs, mesos will generate TASK_LOST updates for us // if needed, so don't take extra time to do that here. k.tasks = map[string]*qingrTask{} select { // the main Run() func may still be running... wait for it to finish: it will // clear the pod configuration cleanly, telling k8s "there are no pods" and // clean up resources (pods, volumes, etc). case <-k.qingletFinished: //TODO(jdef) attempt to wait for events to propagate to API server? // TODO(jdef) extract constant, should be smaller than whatever the // slave graceful shutdown timeout period is. case <-time.After(15 * time.Second): log.Errorf("timed out waiting for qinglet Run() to die") } log.Infoln("exiting") if k.exitFunc != nil { k.exitFunc(0) } }
// runs the main qinglet loop, closing the qingletFinished chan when the loop exits. // never returns. func (kl *qingletExecutor) Run(updates <-chan qinglet.PodUpdate) { defer func() { close(kl.qingletFinished) util.HandleCrash() log.Infoln("qinglet run terminated") //TODO(jdef) turn down verbosity // important: never return! this is in our contract select {} }() // push updates through a closable pipe. when the executor indicates shutdown // via Done() we want to stop the Qinglet from processing updates. pipe := make(chan qinglet.PodUpdate) go func() { // closing pipe will cause our patched qinglet's syncLoop() to exit defer close(pipe) pipeLoop: for { select { case <-kl.executorDone: break pipeLoop default: select { case u := <-updates: select { case pipe <- u: // noop case <-kl.executorDone: break pipeLoop } case <-kl.executorDone: break pipeLoop } } } }() // we expect that Run() will complete after the pipe is closed and the // qinglet's syncLoop() has finished processing its backlog, which hopefully // will not take very long. Peeking into the future (current k8s master) it // seems that the backlog has grown from 1 to 50 -- this may negatively impact // us going forward, time will tell. util.Until(func() { kl.Qinglet.Run(pipe) }, 0, kl.executorDone) //TODO(jdef) revisit this if/when executor failover lands err := kl.SyncPods([]*api.Pod{}, nil, nil, time.Now()) if err != nil { log.Errorf("failed to cleanly remove all pods and associated state: %v", err) } }
// etcdWatch calls etcd's Watch function, and handles any errors. Meant to be called // as a goroutine. func (w *etcdWatcher) etcdWatch(client EtcdGetSet, key string, resourceVersion uint64) { defer util.HandleCrash() defer close(w.etcdError) if resourceVersion == 0 { latest, err := etcdGetInitialWatchState(client, key, w.list, w.etcdIncoming) if err != nil { w.etcdError <- err return } resourceVersion = latest + 1 } _, err := client.Watch(key, resourceVersion, w.list, w.etcdIncoming, w.etcdStop) if err != nil && err != etcd.ErrWatchStoppedByUser { w.etcdError <- err } }
// Run begins processing items, and will continue until a value is sent down stopCh. // It's an error to call Run more than once. // Run blocks; call via go. func (c *Controller) Run(stopCh <-chan struct{}) { defer util.HandleCrash() r := cache.NewReflector( c.config.ListerWatcher, c.config.ObjectType, c.config.Queue, c.config.FullResyncPeriod, ) c.reflectorMutex.Lock() c.reflector = r c.reflectorMutex.Unlock() r.RunUntil(stopCh) util.Until(c.processLoop, time.Second, stopCh) }
// getOrExpire retrieves the object from the timestampedEntry iff it hasn't // already expired. It kicks-off a go routine to delete expired objects from // the store and sets exists=false. func (c *ExpirationCache) getOrExpire(key string) (interface{}, bool) { timestampedItem, exists := c.getTimestampedEntry(key) if !exists { return nil, false } if c.expirationPolicy.IsExpired(timestampedItem) { glog.V(4).Infof("Entry %v: %+v has expired", key, timestampedItem.obj) // Since expiration happens lazily on read, don't hold up // the reader trying to acquire a write lock for the delete. // The next reader will retry the delete even if this one // fails; as long as we only return un-expired entries a // reader doesn't need to wait for the result of the delete. go func() { defer util.HandleCrash() c.cacheStorage.Delete(key) }() return nil, false } return timestampedItem.obj, true }
// periodically execute the given function, stopping once stopCh is closed. // this func blocks until stopCh is closed, it's intended to be run as a goroutine. func Until(f func(), period time.Duration, stopCh <-chan struct{}) { if f == nil { return } for { select { case <-stopCh: return default: } func() { defer util.HandleCrash() f() }() select { case <-stopCh: case <-time.After(period): } } }
// StartEventWatcher starts sending events recieved from this EventBroadcaster to the given event handler function. // The return value can be ignored or used to stop recording, if desired. func (eventBroadcaster *eventBroadcasterImpl) StartEventWatcher(eventHandler func(*api.Event)) watch.Interface { watcher := eventBroadcaster.Watch() go func() { defer util.HandleCrash() for { watchEvent, open := <-watcher.ResultChan() if !open { return } event, ok := watchEvent.Object.(*api.Event) if !ok { // This is all local, so there's no reason this should // ever happen. continue } eventHandler(event) } }() return watcher }
// Watches cadvisor for system oom's and records an event for every system oom encountered. func (ow *realOOMWatcher) Start(ref *api.ObjectReference) error { request := events.Request{ EventType: map[cadvisorApi.EventType]bool{ cadvisorApi.EventOom: true, }, ContainerName: "/", IncludeSubcontainers: false, } eventChannel, err := ow.cadvisor.WatchEvents(&request) if err != nil { return err } go func() { defer util.HandleCrash() for event := range eventChannel.GetChannel() { glog.V(2).Infof("Got sys oom event from cadvisor: %v", event) ow.recorder.PastEventf(ref, util.Time{event.Timestamp}, systemOOMEvent, "System OOM encountered") } glog.Errorf("Unexpectedly stopped receiving OOM notifications from cAdvisor") }() return nil }
// Run runs the specified APIServer. This should never exit. func (s *APIServer) Run(_ []string) error { s.verifyClusterIPFlags() // If advertise-address is not specified, use bind-address. If bind-address // is also unset (or 0.0.0.0), setDefaults() in pkg/master/master.go will // do the right thing and use the host's default interface. if s.AdvertiseAddress == nil || net.IP(s.AdvertiseAddress).IsUnspecified() { s.AdvertiseAddress = s.BindAddress } if (s.EtcdConfigFile != "" && len(s.EtcdServerList) != 0) || (s.EtcdConfigFile == "" && len(s.EtcdServerList) == 0) { glog.Fatalf("specify either --etcd-servers or --etcd-config") } capabilities.Initialize(capabilities.Capabilities{ AllowPrivileged: s.AllowPrivileged, // TODO(vmarmol): Implement support for HostNetworkSources. HostNetworkSources: []string{}, }) cloud := cloudprovider.InitCloudProvider(s.CloudProvider, s.CloudConfigFile) qingletClient, err := client.NewQingletClient(&s.QingletConfig) if err != nil { glog.Fatalf("Failure to start qinglet client: %v", err) } // "api/all=false" allows users to selectively enable specific api versions. disableAllAPIs := false allAPIFlagValue, ok := s.RuntimeConfig["api/all"] if ok && allAPIFlagValue == "false" { disableAllAPIs = true } // "api/legacy=false" allows users to disable legacy api versions. // Right now, v1beta1 and v1beta2 are considered legacy. disableLegacyAPIs := false legacyAPIFlagValue, ok := s.RuntimeConfig["api/legacy"] if ok && legacyAPIFlagValue == "false" { disableLegacyAPIs = true } _ = disableLegacyAPIs // hush the compiler while we don't have legacy APIs to disable. // "api/v1beta3={true|false} allows users to enable/disable v1beta3 API. // This takes preference over api/all and api/legacy, if specified. disableV1beta3 := disableAllAPIs disableV1beta3 = !s.getRuntimeConfigValue("api/v1beta3", !disableV1beta3) // "api/v1={true|false} allows users to enable/disable v1 API. // This takes preference over api/all and api/legacy, if specified. disableV1 := disableAllAPIs disableV1 = !s.getRuntimeConfigValue("api/v1", !disableV1) // TODO: expose same flags as client.BindClientConfigFlags but for a server clientConfig := &client.Config{ Host: net.JoinHostPort(s.InsecureBindAddress.String(), strconv.Itoa(s.InsecurePort)), Version: s.StorageVersion, } client, err := client.New(clientConfig) if err != nil { glog.Fatalf("Invalid server address: %v", err) } helper, err := newEtcd(s.EtcdConfigFile, s.EtcdServerList, s.StorageVersion, s.EtcdPathPrefix) if err != nil { glog.Fatalf("Invalid storage version or misconfigured etcd: %v", err) } // TODO Is this the right place for migration to happen? Must *both* old and // new etcd prefix params be supplied for this to be valid? if s.OldEtcdPathPrefix != "" { if err = helper.MigrateKeys(s.OldEtcdPathPrefix); err != nil { glog.Fatalf("Migration of old etcd keys failed: %v", err) } } n := net.IPNet(s.ServiceClusterIPRange) // Default to the private server key for service account token signing if s.ServiceAccountKeyFile == "" && s.TLSPrivateKeyFile != "" { if apiserver.IsValidServiceAccountKeyFile(s.TLSPrivateKeyFile) { s.ServiceAccountKeyFile = s.TLSPrivateKeyFile } else { glog.Warning("no RSA key provided, service account token authentication disabled") } } authenticator, err := apiserver.NewAuthenticator(s.BasicAuthFile, s.ClientCAFile, s.TokenAuthFile, s.ServiceAccountKeyFile, s.ServiceAccountLookup, helper) if err != nil { glog.Fatalf("Invalid Authentication Config: %v", err) } authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(s.AuthorizationMode, s.AuthorizationPolicyFile) if err != nil { glog.Fatalf("Invalid Authorization Config: %v", err) } admissionControlPluginNames := strings.Split(s.AdmissionControl, ",") admissionController := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile) if len(s.ExternalHost) == 0 { // TODO: extend for other providers if s.CloudProvider == "gce" { instances, supported := cloud.Instances() if !supported { glog.Fatalf("gce cloud provider has no instances. this shouldn't happen. exiting.") } name, err := os.Hostname() if err != nil { glog.Fatalf("failed to get hostname: %v", err) } addrs, err := instances.NodeAddresses(name) if err != nil { glog.Warningf("unable to obtain external host address from cloud provider: %v", err) } else { for _, addr := range addrs { if addr.Type == api.NodeExternalIP { s.ExternalHost = addr.Address } } } } } var installSSH master.InstallSSHKey if cloud != nil { if instances, supported := cloud.Instances(); supported { installSSH = instances.AddSSHKeyToAllInstances } } config := &master.Config{ EtcdHelper: helper, EventTTL: s.EventTTL, QingletClient: qingletClient, ServiceClusterIPRange: &n, EnableCoreControllers: true, EnableLogsSupport: s.EnableLogsSupport, EnableUISupport: true, EnableSwaggerSupport: true, EnableProfiling: s.EnableProfiling, EnableIndex: true, APIPrefix: s.APIPrefix, CorsAllowedOriginList: s.CorsAllowedOriginList, ReadWritePort: s.SecurePort, PublicAddress: net.IP(s.AdvertiseAddress), Authenticator: authenticator, SupportsBasicAuth: len(s.BasicAuthFile) > 0, Authorizer: authorizer, AdmissionControl: admissionController, DisableV1Beta3: disableV1beta3, DisableV1: disableV1, MasterServiceNamespace: s.MasterServiceNamespace, ClusterName: s.ClusterName, ExternalHost: s.ExternalHost, MinRequestTimeout: s.MinRequestTimeout, SSHUser: s.SSHUser, SSHKeyfile: s.SSHKeyfile, InstallSSHKey: installSSH, ServiceNodePortRange: s.ServiceNodePortRange, } m := master.New(config) // We serve on 2 ports. See docs/accessing_the_api.md secureLocation := "" if s.SecurePort != 0 { secureLocation = net.JoinHostPort(s.BindAddress.String(), strconv.Itoa(s.SecurePort)) } insecureLocation := net.JoinHostPort(s.InsecureBindAddress.String(), strconv.Itoa(s.InsecurePort)) // See the flag commentary to understand our assumptions when opening the read-only and read-write ports. var sem chan bool if s.MaxRequestsInFlight > 0 { sem = make(chan bool, s.MaxRequestsInFlight) } longRunningRE := regexp.MustCompile(s.LongRunningRequestRE) if secureLocation != "" { secureServer := &http.Server{ Addr: secureLocation, Handler: apiserver.MaxInFlightLimit(sem, longRunningRE, apiserver.RecoverPanics(m.Handler)), ReadTimeout: ReadWriteTimeout, WriteTimeout: ReadWriteTimeout, MaxHeaderBytes: 1 << 20, TLSConfig: &tls.Config{ // Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability) MinVersion: tls.VersionTLS10, }, } if len(s.ClientCAFile) > 0 { clientCAs, err := util.CertPoolFromFile(s.ClientCAFile) if err != nil { glog.Fatalf("unable to load client CA file: %v", err) } // Populate PeerCertificates in requests, but don't reject connections without certificates // This allows certificates to be validated by authenticators, while still allowing other auth types secureServer.TLSConfig.ClientAuth = tls.RequestClientCert // Specify allowed CAs for client certificates secureServer.TLSConfig.ClientCAs = clientCAs } glog.Infof("Serving securely on %s", secureLocation) go func() { defer util.HandleCrash() for { if s.TLSCertFile == "" && s.TLSPrivateKeyFile == "" { s.TLSCertFile = path.Join(s.CertDirectory, "apiserver.crt") s.TLSPrivateKeyFile = path.Join(s.CertDirectory, "apiserver.key") // TODO (cjcullen): Is PublicAddress the right address to sign a cert with? if err := util.GenerateSelfSignedCert(config.PublicAddress.String(), s.TLSCertFile, s.TLSPrivateKeyFile); err != nil { glog.Errorf("Unable to generate self signed cert: %v", err) } else { glog.Infof("Using self-signed cert (%s, %s)", s.TLSCertFile, s.TLSPrivateKeyFile) } } // err == systemd.SdNotifyNoSocket when not running on a systemd system if err := systemd.SdNotify("READY=1\n"); err != nil && err != systemd.SdNotifyNoSocket { glog.Errorf("Unable to send systemd daemon sucessful start message: %v\n", err) } if err := secureServer.ListenAndServeTLS(s.TLSCertFile, s.TLSPrivateKeyFile); err != nil { glog.Errorf("Unable to listen for secure (%v); will try again.", err) } time.Sleep(15 * time.Second) } }() } http := &http.Server{ Addr: insecureLocation, Handler: apiserver.RecoverPanics(m.InsecureHandler), ReadTimeout: ReadWriteTimeout, WriteTimeout: ReadWriteTimeout, MaxHeaderBytes: 1 << 20, } if secureLocation == "" { // err == systemd.SdNotifyNoSocket when not running on a systemd system if err := systemd.SdNotify("READY=1\n"); err != nil && err != systemd.SdNotifyNoSocket { glog.Errorf("Unable to send systemd daemon sucessful start message: %v\n", err) } } glog.Infof("Serving insecurely on %s", insecureLocation) glog.Fatal(http.ListenAndServe()) return nil }