// Checks if iptables version has a "wait" flag func getIptablesWaitFlag(vstring string) []string { version, err := semver.NewVersion(vstring) if err != nil { glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err) return nil } minVersion, err := semver.NewVersion(MinWaitVersion) if err != nil { glog.Errorf("MinWaitVersion (%s) is not a valid version string: %v", MinWaitVersion, err) return nil } if version.LessThan(*minVersion) { return nil } minVersion, err = semver.NewVersion(MinWait2Version) if err != nil { glog.Errorf("MinWait2Version (%s) is not a valid version string: %v", MinWait2Version, err) return nil } if version.LessThan(*minVersion) { return []string{"-w"} } else { return []string{"-w2"} } }
// UpgradeResponse upgrades an HTTP response to one that supports multiplexed // streams. newStreamHandler will be called synchronously whenever the // other end of the upgraded connection creates a new stream. func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection { connectionHeader := strings.ToLower(req.Header.Get(httpstream.HeaderConnection)) upgradeHeader := strings.ToLower(req.Header.Get(httpstream.HeaderUpgrade)) if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) { w.Write([]byte(fmt.Sprintf("Unable to upgrade: missing upgrade headers in request: %#v", req.Header))) w.WriteHeader(http.StatusBadRequest) return nil } hijacker, ok := w.(http.Hijacker) if !ok { w.Write([]byte("Unable to upgrade: unable to hijack response")) w.WriteHeader(http.StatusInternalServerError) return nil } w.Header().Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade) w.Header().Add(httpstream.HeaderUpgrade, HeaderSpdy31) w.WriteHeader(http.StatusSwitchingProtocols) conn, _, err := hijacker.Hijack() if err != nil { glog.Errorf("Unable to upgrade: error hijacking response: %v", err) return nil } spdyConn, err := NewServerConnection(conn, newStreamHandler) if err != nil { glog.Errorf("Unable to upgrade: error creating SPDY server connection: %v", err) return nil } return spdyConn }
func (writer *NsenterWriter) WriteFile(filename string, data []byte, perm os.FileMode) error { cmd := "nsenter" base_args := []string{ "--mount=/rootfs/proc/1/ns/mnt", "--", } echo_args := append(base_args, "sh", "-c", fmt.Sprintf("echo %q | cat > %s", data, filename)) glog.V(5).Infof("Command to write data to file: %v %v", cmd, echo_args) outputBytes, err := exec.Command(cmd, echo_args...).CombinedOutput() if err != nil { glog.Errorf("Output from writing to %q: %v", filename, string(outputBytes)) return err } chmod_args := append(base_args, "chmod", fmt.Sprintf("%o", perm), filename) glog.V(5).Infof("Command to change permissions to file: %v %v", cmd, chmod_args) outputBytes, err = exec.Command(cmd, chmod_args...).CombinedOutput() if err != nil { glog.Errorf("Output from chmod command: %v", string(outputBytes)) return err } return nil }
// Checks if iptables has the "-C" flag func getIptablesHasCheckCommand(vstring string) bool { minVersion, err := semver.NewVersion(MinCheckVersion) if err != nil { glog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinCheckVersion, err) return true } version, err := semver.NewVersion(vstring) if err != nil { glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err) return true } if version.LessThan(*minVersion) { return false } return true }
// Writes 'value' to /proc/<pid>/oom_score_adj for all processes in cgroup cgroupName. // Keeps trying to write until the process list of the cgroup stabilizes, or until maxTries tries. func (oomAdjuster *OOMAdjuster) applyOOMScoreAdjContainer(cgroupName string, oomScoreAdj, maxTries int) error { adjustedProcessSet := make(map[int]bool) for i := 0; i < maxTries; i++ { continueAdjusting := false pidList, err := oomAdjuster.pidLister(cgroupName) if err != nil { continueAdjusting = true glog.Errorf("Error getting process list for cgroup %s: %+v", cgroupName, err) } else if len(pidList) == 0 { continueAdjusting = true } else { for _, pid := range pidList { if !adjustedProcessSet[pid] { continueAdjusting = true if err = oomAdjuster.ApplyOOMScoreAdj(pid, oomScoreAdj); err == nil { adjustedProcessSet[pid] = true } } } } if !continueAdjusting { return nil } // There's a slight race. A process might have forked just before we write its OOM score adjust. // The fork might copy the parent process's old OOM score, then this function might execute and // update the parent's OOM score, but the forked process id might not be reflected in cgroup.procs // for a short amount of time. So this function might return without changing the forked process's // OOM score. Very unlikely race, so ignoring this for now. } return fmt.Errorf("exceeded maxTries, some processes might not have desired OOM score") }
// rewriteResponse modifies an HTML response by updating absolute links referring // to the original host to instead refer to the proxy transport. func (t *Transport) rewriteResponse(req *http.Request, resp *http.Response) (*http.Response, error) { origBody := resp.Body defer origBody.Close() newContent := &bytes.Buffer{} var reader io.Reader = origBody var writer io.Writer = newContent encoding := resp.Header.Get("Content-Encoding") switch encoding { case "gzip": var err error reader, err = gzip.NewReader(reader) if err != nil { return nil, fmt.Errorf("errorf making gzip reader: %v", err) } gzw := gzip.NewWriter(writer) defer gzw.Close() writer = gzw // TODO: support flate, other encodings. case "": // This is fine default: // Some encoding we don't understand-- don't try to parse this glog.Errorf("Proxy encountered encoding %v for text/html; can't understand this so not fixing links.", encoding) return resp, nil } urlRewriter := func(targetUrl string) string { return t.rewriteURL(targetUrl, req.URL) } err := rewriteHTML(reader, writer, urlRewriter) if err != nil { glog.Errorf("Failed to rewrite URLs: %v", err) return resp, err } resp.Body = ioutil.NopCloser(newContent) // Update header node with new content-length // TODO: Remove any hash/signature headers here? resp.Header.Del("Content-Length") resp.ContentLength = int64(newContent.Len()) return resp, err }
// Replace will delete the contents of 'f', using instead the given map. // 'f' takes ownership of the map, you should not reference the map again // after calling this function. f's queue is reset, too; upon return, it // will contain the items in the map, in no particular order. func (f *DeltaFIFO) Replace(list []interface{}, resourceVersion string) error { f.lock.Lock() defer f.lock.Unlock() for _, item := range list { if err := f.queueActionLocked(Sync, item); err != nil { return fmt.Errorf("couldn't enqueue object: %v", err) } } if f.knownObjectKeys == nil { return nil } keySet := make(sets.String, len(list)) for _, item := range list { key, err := f.KeyOf(item) if err != nil { return KeyError{item, err} } keySet.Insert(key) } // Detect deletions not already in the queue. knownKeys := f.knownObjectKeys.ListKeys() for _, k := range knownKeys { if _, exists := keySet[k]; exists { continue } // This key isn't in the complete set we got, so it must have been deleted. if d, exists := f.items[k]; exists { // Don't issue a delete delta if we have one enqueued as the most // recent delta. if d.Newest().Type == Deleted { continue } } var deletedObj interface{} if keyGetter, ok := f.knownObjectKeys.(KeyGetter); ok { var exists bool var err error deletedObj, exists, err = keyGetter.GetByKey(k) if err != nil || !exists { deletedObj = nil if err != nil { glog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k) } else { glog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k) } } } if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { return err } } return nil }
// logPanic logs the caller tree when a panic occurs. func logPanic(r interface{}) { callers := "" for i := 0; true; i++ { _, file, line, ok := runtime.Caller(i) if !ok { break } callers = callers + fmt.Sprintf("%v:%v\n", file, line) } glog.Errorf("Recovered from panic: %#v (%v)\n%v", r, r, callers) }
// Close asynchronously closes all tunnels in the list after waiting for 1 // minute. Tunnels will still be open upon this function's return, but should // no longer be used. func (l *SSHTunnelList) Close() { for ix := range l.entries { entry := l.entries[ix] go func() { defer HandleCrash() time.Sleep(1 * time.Minute) if err := entry.Tunnel.Close(); err != nil { glog.Errorf("Failed to close tunnel %v: %v", entry, err) } }() } }
func MakeSSHTunnels(user, keyfile string, addresses []string) *SSHTunnelList { tunnels := []SSHTunnelEntry{} for ix := range addresses { addr := addresses[ix] tunnel, err := NewSSHTunnel(user, keyfile, addr) if err != nil { glog.Errorf("Failed to create tunnel for %q: %v", addr, err) continue } tunnels = append(tunnels, SSHTunnelEntry{addr, tunnel}) } return &SSHTunnelList{tunnels} }
// Open attempts to open all tunnels in the list, and removes any tunnels that // failed to open. func (l *SSHTunnelList) Open() error { var openTunnels []SSHTunnelEntry for ix := range l.entries { if err := l.entries[ix].Tunnel.Open(); err != nil { glog.Errorf("Failed to open tunnel %v: %v", l.entries[ix], err) } else { openTunnels = append(openTunnels, l.entries[ix]) } } l.entries = openTunnels if len(l.entries) == 0 { return errors.New("Failed to open any tunnels.") } return nil }
// InClusterConfig returns a config object which uses the service account // kubernetes gives to pods. It's intended for clients that expect to be // running inside a pod running on kuberenetes. It will return an error if // called from a process not running in a kubernetes environment. func InClusterConfig() (*Config, error) { token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountTokenKey) if err != nil { return nil, err } tlsClientConfig := TLSClientConfig{} rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountRootCAKey if _, err := util.CertPoolFromFile(rootCAFile); err != nil { glog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err) } else { tlsClientConfig.CAFile = rootCAFile } return &Config{ // TODO: switch to using cluster DNS. Host: "https://" + net.JoinHostPort(os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")), BearerToken: string(token), TLSClientConfig: tlsClientConfig, }, nil }
func (s *SSHTunnel) copyBytes(out io.Writer, in io.Reader) { if _, err := io.Copy(out, in); err != nil { glog.Errorf("Error in SSH tunnel: %v", err) } }