// sendHTTP sends the Response via HTTP/HTTPS func sendHTTP(addr *url.URL, payload interface{}) error { payloadJSON, err := json.Marshal(payload) if err != nil { log.WithFields(log.Fields{ "error": err, "payload": payload, }).Error("failed to marshal payload json") return err } httpResp, err := http.Post(addr.String(), "application/json", bytes.NewReader(payloadJSON)) if err != nil { log.WithFields(log.Fields{ "error": err, "addr": addr, "payload": payload, }).Error("failed to send payload") return err } defer logx.LogReturnedErr(httpResp.Body.Close, nil, "failed to close http body") body, _ := ioutil.ReadAll(httpResp.Body) resp := &Response{} if err := json.Unmarshal(body, resp); err != nil { log.WithFields(log.Fields{ "error": err, "body": string(body), }).Error(err) return err } return resp.Error }
// request is the generic way to hit an agent endpoint with minimal response // checking. It returns the body string for later parsing and an optional jobID. // Generally don't use directly; other, more convenient methods will wrap this func (agent *MistifyAgent) request(url, httpMethod string, expectedCode int, dataObj interface{}) ([]byte, string, error) { httpClient := &http.Client{ Timeout: 15 * time.Second, } // Make the request. POST sends JSON data, GET doesn't var resp *http.Response var reqErr error if httpMethod == "POST" { dataJSON, err := json.Marshal(dataObj) if err != nil { return nil, "", err } resp, reqErr = httpClient.Post(url, "application/json", bytes.NewReader(dataJSON)) } else { resp, reqErr = httpClient.Get(url) } if reqErr != nil { return nil, "", reqErr } defer logx.LogReturnedErr(resp.Body.Close, nil, "failed to close response body") if resp.StatusCode != expectedCode { return nil, "", ErrorHTTPCode{expectedCode, resp.StatusCode} } body, err := ioutil.ReadAll(resp.Body) return body, resp.Header.Get("X-Guest-Job-ID"), err }
// Notify sends a message to the init daemon. It is common to ignore the error. func Notify(state string) error { socketAddr := &net.UnixAddr{ Name: os.Getenv("NOTIFY_SOCKET"), Net: "unixgram", } if socketAddr.Name == "" { return ErrNotifyNoSocket } switch socketAddr.Name[0] { case '@', '/': default: return ErrNotifyNoSocket } conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr) if err != nil { return err } defer logx.LogReturnedErr(conn.Close, nil, "failed to close connection") _, err = conn.Write([]byte(state)) if err != nil { return err } return nil }
// sendUnix sends a request or response via a Unix socket. func sendUnix(addr *url.URL, payload interface{}) error { conn, err := net.Dial("unix", addr.RequestURI()) if err != nil { log.WithFields(log.Fields{ "error": err, "addr": addr, "payload": payload, }).Error("failed to connect to unix socket") return err } defer logx.LogReturnedErr(conn.Close, log.Fields{"addr": addr}, "failed to close unix connection", ) if err := SendConnData(conn, payload); err != nil { return err } resp := &Response{} if err := UnmarshalConnData(conn, resp); err != nil { return err } return resp.Error }
func ExampleSplit() { examples := []string{ "localhost", "localhost:1234", "[localhost]", "[localhost]:1234", "2001:db8:85a3:8d3:1319:8a2e:370:7348", "[2001:db8:85a3:8d3:1319:8a2e:370:7348]", "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443", "2001:db8:85a3:8d3:1319:8a2e:370:7348:443", ":1234", "", ":::", "foo:1234:bar", "[2001:db8:85a3:8d3:1319:8a2e:370:7348", "[localhost", "2001:db8:85a3:8d3:1319:8a2e:370:7348]", "localhost]", "[loca[lhost]:1234", "[loca]lhost]:1234", "[localhost]:1234]", } w := new(tabwriter.Writer) w.Init(os.Stdout, 0, 8, 0, '\t', 0) fmt.Fprintln(w, "HOSTPORT\tHOST\tPORT\tERR") fmt.Fprintln(w, "========\t====\t====\t===") for _, hp := range examples { host, port, err := hostport.Split(hp) fmt.Fprintf(w, "%s\t%s\t%s\t%v\n", hp, host, port, err) } logx.LogReturnedErr(w.Flush, nil, "failed to flush tabwriter") // Output: // HOSTPORT HOST PORT ERR // ======== ==== ==== === // localhost localhost <nil> // localhost:1234 localhost 1234 <nil> // [localhost] localhost <nil> // [localhost]:1234 localhost 1234 <nil> // 2001:db8:85a3:8d3:1319:8a2e:370:7348 2001:db8:85a3:8d3:1319:8a2e:370 7348 <nil> // [2001:db8:85a3:8d3:1319:8a2e:370:7348] 2001:db8:85a3:8d3:1319:8a2e:370:7348 <nil> // [2001:db8:85a3:8d3:1319:8a2e:370:7348]:443 2001:db8:85a3:8d3:1319:8a2e:370:7348 443 <nil> // 2001:db8:85a3:8d3:1319:8a2e:370:7348:443 2001:db8:85a3:8d3:1319:8a2e:370:7348 443 <nil> // :1234 1234 <nil> // <nil> // ::: :: <nil> // foo:1234:bar foo:1234 bar <nil> // [2001:db8:85a3:8d3:1319:8a2e:370:7348 missing ']' // [localhost missing ']' // 2001:db8:85a3:8d3:1319:8a2e:370:7348] missing '[' // localhost] missing '[' // [loca[lhost]:1234 too many '[' // [loca]lhost]:1234 too many ']' // [localhost]:1234] too many ']' }
// DoneConn completes the handling of a connection. func (ul *UnixListener) DoneConn(conn net.Conn) { if conn == nil { return } defer ul.waitgroup.Done() defer logx.LogReturnedErr(conn.Close, log.Fields{ "addr": ul.addr, }, "failed to close unix connection", ) }
// listen continuously listens and accepts new connections up to the accept // limit. func (ul *UnixListener) listen() { defer ul.waitgroup.Done() defer logx.LogReturnedErr(ul.listener.Close, log.Fields{ "addr": ul.Addr(), }, "failed to close listener") for i := ul.acceptLimit; i != 0; { select { case <-ul.stopChan: log.WithFields(log.Fields{ "addr": ul.Addr(), }).Info("stop listening") return default: } if err := ul.listener.SetDeadline(time.Now().Add(time.Second)); err != nil { log.WithFields(log.Fields{ "addr": ul.Addr(), "error": err, }).Error("failed to set listener deadline") } conn, err := ul.listener.Accept() if nil != err { // Don't worry about a timeout if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() { continue } log.WithFields(log.Fields{ "addr": ul.Addr(), "error": err, }).Error("failed to accept new connection") continue } ul.waitgroup.Add(1) ul.connChan <- conn // Only decrement i when there's a limit it is counting down if i > 0 { i-- } } }
// streamHTTP streams data from an http connection to a destination writer. func streamHTTP(dest io.Writer, addr *url.URL) error { httpResp, err := http.Get(addr.String()) if err != nil { log.WithFields(log.Fields{ "addr": addr, "error": err, }).Error("failed to GET stream") return err } defer logx.LogReturnedErr(httpResp.Body.Close, log.Fields{"addr": addr}, "failed to close stream response body", ) if _, err := io.Copy(dest, httpResp.Body); err != nil { log.WithFields(log.Fields{ "addr": addr, "error": err, }).Error("failed to stream data") return err } return nil }
// streamUnix streams data from a unix socket to a destination writer. func streamUnix(dest io.Writer, addr *url.URL) error { conn, err := net.Dial("unix", addr.RequestURI()) if err != nil { log.WithFields(log.Fields{ "addr": addr, "error": err, }).Error("failed to connect to stream socket") return err } defer logx.LogReturnedErr(conn.Close, log.Fields{"addr": addr}, "failed to close stream connection", ) if _, err := io.Copy(dest, conn); err != nil { log.WithFields(log.Fields{ "addr": addr, "error": err, }).Error("failed to stream data") return err } return nil }
func upload(cmd *cobra.Command, specs []string) { if len(specs) == 0 { specs = cli.Read(os.Stdin) } uploadURL := getServerURL() + "/images" for _, spec := range specs { uploadImage := &metadata.Image{} if err := json.Unmarshal([]byte(spec), uploadImage); err != nil { log.WithFields(log.Fields{ "error": err, "json": spec, "func": "json.Unmarshal", }).Fatal("invalid spec") } sourcePath, err := filepath.Abs(uploadImage.Source) if err != nil { log.WithFields(log.Fields{ "error": err, "image": uploadImage, "func": "filepath.Abs", }).Fatal("failed determine absolute source path") } file, err := os.Open(sourcePath) if err != nil { log.WithFields(log.Fields{ "error": err, "path": sourcePath, "func": "os.Open", }).Fatal("failed to open file") } // File remains open until function exit defer logx.LogReturnedErr(file.Close, log.Fields{ "filename": sourcePath, }, "failed to close image source file") info, err := file.Stat() if err != nil { log.WithFields(log.Fields{ "error": err, "file": file.Name(), "func": "file.Stat", }).Fatal("failed to stat file") } req, err := http.NewRequest("PUT", uploadURL, file) if err != nil { log.WithFields(log.Fields{ "error": err, "url": uploadURL, "file": file.Name(), "func": "http.NewRequest", }).Fatal("failed to create request") } req.Header.Add("Content-Length", fmt.Sprintf("%d", info.Size())) req.Header.Add("X-Image-Type", uploadImage.Type) req.Header.Add("X-Image-Comment", uploadImage.Comment) req.Header.Add("Content-Type", "application/octet-stream") res, err := http.DefaultClient.Do(req) if err != nil { log.WithFields(log.Fields{ "error": err, "url": uploadURL, "file": file.Name(), "func": "http.DefaultClient.Do", }).Fatal("request error") } image := &cli.JMap{} cli.ProcessResponse(res, "image", "upload", []int{http.StatusOK}, image) image.Print(jsonout) } }
func download(cmd *cobra.Command, ids []string) { if len(ids) == 0 { ids = cli.Read(os.Stdin) } for _, id := range ids { success := false tempDest, err := ioutil.TempFile(downloadDir, "incompleteImage-") if err != nil { log.WithFields(log.Fields{ "error": err, "dir": downloadDir, "func": "ioutil.TempFile", }).Fatal("could not create temporary file") } defer func() { if !success { if err := os.Remove(tempDest.Name()); err != nil { log.WithFields(log.Fields{ "error": err, "tempfile": tempDest.Name(), "func": "os.Remove", }).Error("failed to remove temporary file") } } }() defer logx.LogReturnedErr(tempDest.Close, log.Fields{ "filename": tempDest.Name(), }, "failed to close temp dest") sourceURL := fmt.Sprintf("%s/images/%s/download", getServerURL(), id) resp, err := http.Get(sourceURL) if err != nil { log.WithFields(log.Fields{ "error": err, "sourceURL": sourceURL, "func": "http.Get", }).Error("request error") return } defer logx.LogReturnedErr(resp.Body.Close, nil, "failed to close response body") if resp.StatusCode != http.StatusOK { log.WithFields(log.Fields{ "sourceURL": sourceURL, "statusCode": resp.StatusCode, "func": "http.Get", }).Error("bad response code") return } if _, err := io.Copy(tempDest, resp.Body); err != nil { log.WithFields(log.Fields{ "error": err, "sourceURL": sourceURL, "tempFile": tempDest.Name(), "func": "io.Copy", }).Error("failed to download image") return } if _, err := tempDest.Seek(0, 0); err != nil { log.WithFields(log.Fields{ "error": err, "tempFile": tempDest.Name(), "func": "tempDest.Seek", }).Error("failed to seek to beginning of file") } fileBuffer := bufio.NewReader(tempDest) filetypeBytes, err := fileBuffer.Peek(512) if err != nil { log.WithFields(log.Fields{ "error": err, "tempFile": tempDest.Name(), "func": "tempDest.Peek", }).Error("failed to read image filetype bytes") return } extension := ".tar" if http.DetectContentType(filetypeBytes) == "application/x-gzip" { extension = extension + ".gz" } imagePath := filepath.Join(downloadDir, id+extension) if err := os.Rename(tempDest.Name(), imagePath); err != nil { log.WithFields(log.Fields{ "tempFile": tempDest.Name(), "imagePath": imagePath, "func": "os.Rename", }).Error("failed to rename image file") return } fmt.Println(imagePath) success = true } }