123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- /*
- Copyright 2014 The Kubernetes Authors.
- 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 main
- import (
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "net/url"
- "os"
- "os/exec"
- "strconv"
- "strings"
- "sync/atomic"
- "time"
- utilnet "k8s.io/apimachinery/pkg/util/net"
- )
- var (
- httpPort = 8080
- udpPort = 8081
- shellPath = "/bin/sh"
- serverReady = &atomicBool{0}
- )
- // atomicBool uses load/store operations on an int32 to simulate an atomic boolean.
- type atomicBool struct {
- v int32
- }
- // set sets the int32 to the given boolean.
- func (a *atomicBool) set(value bool) {
- if value {
- atomic.StoreInt32(&a.v, 1)
- return
- }
- atomic.StoreInt32(&a.v, 0)
- }
- // get returns true if the int32 == 1
- func (a *atomicBool) get() bool {
- return atomic.LoadInt32(&a.v) == 1
- }
- func init() {
- flag.IntVar(&httpPort, "http-port", 8080, "HTTP Listen Port")
- flag.IntVar(&udpPort, "udp-port", 8081, "UDP Listen Port")
- }
- func main() {
- flag.Parse()
- go startUDPServer(udpPort)
- startHTTPServer(httpPort)
- }
- func startHTTPServer(httpPort int) {
- http.HandleFunc("/", rootHandler)
- http.HandleFunc("/clientip", clientIPHandler)
- http.HandleFunc("/echo", echoHandler)
- http.HandleFunc("/exit", exitHandler)
- http.HandleFunc("/hostname", hostnameHandler)
- http.HandleFunc("/shell", shellHandler)
- http.HandleFunc("/upload", uploadHandler)
- http.HandleFunc("/dial", dialHandler)
- http.HandleFunc("/healthz", healthzHandler)
- // older handlers
- http.HandleFunc("/hostName", hostNameHandler)
- http.HandleFunc("/shutdown", shutdownHandler)
- log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", httpPort), nil))
- }
- func rootHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /")
- fmt.Fprintf(w, "NOW: %v", time.Now())
- }
- func echoHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /echo?msg=%s", r.FormValue("msg"))
- fmt.Fprintf(w, "%s", r.FormValue("msg"))
- }
- func clientIPHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /clientip")
- fmt.Fprintf(w, r.RemoteAddr)
- }
- func exitHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /exit?code=%s", r.FormValue("code"))
- code, err := strconv.Atoi(r.FormValue("code"))
- if err == nil || r.FormValue("code") == "" {
- os.Exit(code)
- }
- fmt.Fprintf(w, "argument 'code' must be an integer [0-127] or empty, got %q", r.FormValue("code"))
- }
- func hostnameHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /hostname")
- fmt.Fprintf(w, getHostName())
- }
- // healthHandler response with a 200 if the UDP server is ready. It also serves
- // as a health check of the HTTP server by virtue of being a HTTP handler.
- func healthzHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /healthz")
- if serverReady.get() {
- w.WriteHeader(200)
- return
- }
- w.WriteHeader(http.StatusPreconditionFailed)
- }
- func shutdownHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /shutdown")
- os.Exit(0)
- }
- func dialHandler(w http.ResponseWriter, r *http.Request) {
- values, err := url.Parse(r.URL.RequestURI())
- if err != nil {
- http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest)
- return
- }
- host := values.Query().Get("host")
- port := values.Query().Get("port")
- request := values.Query().Get("request") // hostName
- protocol := values.Query().Get("protocol")
- tryParam := values.Query().Get("tries")
- log.Printf("GET /dial?host=%s&protocol=%s&port=%s&request=%s&tries=%s", host, protocol, port, request, tryParam)
- tries := 1
- if len(tryParam) > 0 {
- tries, err = strconv.Atoi(tryParam)
- }
- if err != nil {
- http.Error(w, fmt.Sprintf("tries parameter is invalid. %v", err), http.StatusBadRequest)
- return
- }
- if len(request) == 0 {
- http.Error(w, fmt.Sprintf("request parameter not specified. %v", err), http.StatusBadRequest)
- return
- }
- if len(protocol) == 0 {
- protocol = "http"
- } else {
- protocol = strings.ToLower(protocol)
- }
- if protocol != "http" && protocol != "udp" {
- http.Error(w, fmt.Sprintf("unsupported protocol. %s", protocol), http.StatusBadRequest)
- return
- }
- hostPort := net.JoinHostPort(host, port)
- var udpAddress *net.UDPAddr
- if protocol == "udp" {
- udpAddress, err = net.ResolveUDPAddr("udp", hostPort)
- if err != nil {
- http.Error(w, fmt.Sprintf("host and/or port param are invalid. %v", err), http.StatusBadRequest)
- return
- }
- } else {
- _, err = net.ResolveTCPAddr("tcp", hostPort)
- if err != nil {
- http.Error(w, fmt.Sprintf("host and/or port param are invalid. %v", err), http.StatusBadRequest)
- return
- }
- }
- errors := make([]string, 0)
- responses := make([]string, 0)
- var response string
- for i := 0; i < tries; i++ {
- if protocol == "udp" {
- response, err = dialUDP(request, udpAddress)
- } else {
- response, err = dialHTTP(request, hostPort)
- }
- if err != nil {
- errors = append(errors, fmt.Sprintf("%v", err))
- } else {
- responses = append(responses, response)
- }
- }
- output := map[string][]string{}
- if len(response) > 0 {
- output["responses"] = responses
- }
- if len(errors) > 0 {
- output["errors"] = errors
- }
- bytes, err := json.Marshal(output)
- if err == nil {
- fmt.Fprintf(w, string(bytes))
- } else {
- http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed)
- }
- }
- func dialHTTP(request, hostPort string) (string, error) {
- transport := utilnet.SetTransportDefaults(&http.Transport{})
- httpClient := createHTTPClient(transport)
- resp, err := httpClient.Get(fmt.Sprintf("http://%s/%s", hostPort, request))
- defer transport.CloseIdleConnections()
- if err == nil {
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err == nil {
- return string(body), nil
- }
- }
- return "", err
- }
- func createHTTPClient(transport *http.Transport) *http.Client {
- client := &http.Client{
- Transport: transport,
- Timeout: 5 * time.Second,
- }
- return client
- }
- func dialUDP(request string, remoteAddress *net.UDPAddr) (string, error) {
- Conn, err := net.DialUDP("udp", nil, remoteAddress)
- if err != nil {
- return "", fmt.Errorf("udp dial failed. err:%v", err)
- }
- defer Conn.Close()
- buf := []byte(request)
- _, err = Conn.Write(buf)
- if err != nil {
- return "", fmt.Errorf("udp connection write failed. err:%v", err)
- }
- udpResponse := make([]byte, 1024)
- Conn.SetReadDeadline(time.Now().Add(5 * time.Second))
- count, err := Conn.Read(udpResponse)
- if err != nil || count == 0 {
- return "", fmt.Errorf("reading from udp connection failed. err:'%v'", err)
- }
- return string(udpResponse[0:count]), nil
- }
- func shellHandler(w http.ResponseWriter, r *http.Request) {
- cmd := r.FormValue("shellCommand")
- if cmd == "" {
- cmd = r.FormValue("cmd")
- }
- log.Printf("GET /shell?cmd=%s", cmd)
- cmdOut, err := exec.Command(shellPath, "-c", cmd).CombinedOutput()
- output := map[string]string{}
- if len(cmdOut) > 0 {
- output["output"] = string(cmdOut)
- }
- if err != nil {
- output["error"] = fmt.Sprintf("%v", err)
- }
- log.Printf("Output: %s", output)
- bytes, err := json.Marshal(output)
- if err == nil {
- fmt.Fprintf(w, string(bytes))
- } else {
- http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed)
- }
- }
- func uploadHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /upload")
- result := map[string]string{}
- file, _, err := r.FormFile("file")
- if err != nil {
- result["error"] = "Unable to upload file."
- bytes, err := json.Marshal(result)
- if err == nil {
- fmt.Fprintf(w, string(bytes))
- } else {
- http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError)
- }
- log.Printf("Unable to upload file: %s", err)
- return
- }
- defer file.Close()
- f, err := ioutil.TempFile("/uploads", "upload")
- if err != nil {
- result["error"] = "Unable to open file for write"
- bytes, err := json.Marshal(result)
- if err == nil {
- fmt.Fprintf(w, string(bytes))
- } else {
- http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError)
- }
- log.Printf("Unable to open file for write: %s", err)
- return
- }
- defer f.Close()
- if _, err = io.Copy(f, file); err != nil {
- result["error"] = "Unable to write file."
- bytes, err := json.Marshal(result)
- if err == nil {
- fmt.Fprintf(w, string(bytes))
- } else {
- http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError)
- }
- log.Printf("Unable to write file: %s", err)
- return
- }
- UploadFile := f.Name()
- if err := os.Chmod(UploadFile, 0700); err != nil {
- result["error"] = "Unable to chmod file."
- bytes, err := json.Marshal(result)
- if err == nil {
- fmt.Fprintf(w, string(bytes))
- } else {
- http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError)
- }
- log.Printf("Unable to chmod file: %s", err)
- return
- }
- log.Printf("Wrote upload to %s", UploadFile)
- result["output"] = UploadFile
- w.WriteHeader(http.StatusCreated)
- bytes, err := json.Marshal(result)
- fmt.Fprintf(w, string(bytes))
- }
- func hostNameHandler(w http.ResponseWriter, r *http.Request) {
- log.Printf("GET /hostName")
- fmt.Fprintf(w, getHostName())
- }
- // udp server supports the hostName, echo and clientIP commands.
- func startUDPServer(udpPort int) {
- serverAddress, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", udpPort))
- assertNoError(err)
- serverConn, err := net.ListenUDP("udp", serverAddress)
- assertNoError(err)
- defer serverConn.Close()
- buf := make([]byte, 1024)
- log.Printf("Started UDP server")
- // Start responding to readiness probes.
- serverReady.set(true)
- defer func() {
- log.Printf("UDP server exited")
- serverReady.set(false)
- }()
- for {
- n, clientAddress, err := serverConn.ReadFromUDP(buf)
- assertNoError(err)
- receivedText := strings.ToLower(strings.TrimSpace(string(buf[0:n])))
- if receivedText == "hostname" {
- log.Println("Sending udp hostName response")
- _, err = serverConn.WriteToUDP([]byte(getHostName()), clientAddress)
- assertNoError(err)
- } else if strings.HasPrefix(receivedText, "echo ") {
- parts := strings.SplitN(receivedText, " ", 2)
- resp := ""
- if len(parts) == 2 {
- resp = parts[1]
- }
- log.Printf("Echoing %v\n", resp)
- _, err = serverConn.WriteToUDP([]byte(resp), clientAddress)
- assertNoError(err)
- } else if receivedText == "clientip" {
- log.Printf("Sending back clientip to %s", clientAddress.String())
- _, err = serverConn.WriteToUDP([]byte(clientAddress.String()), clientAddress)
- assertNoError(err)
- } else if len(receivedText) > 0 {
- log.Printf("Unknown udp command received: %v\n", receivedText)
- }
- }
- }
- func getHostName() string {
- hostName, err := os.Hostname()
- assertNoError(err)
- return hostName
- }
- func assertNoError(err error) {
- if err != nil {
- log.Fatal("Error occurred. error:", err)
- }
- }
|