// dash runs the given method and command on the dashboard. // If args is non-nil it is encoded as the URL query string. // If req is non-nil it is JSON-encoded and passed as the body of the HTTP POST. // If resp is non-nil the server's response is decoded into the value pointed // to by resp (resp must be a pointer). func dash(meth, cmd string, args url.Values, req, resp interface{}) error { var r *http.Response var err error if *verbose { log.Println("dash", meth, cmd, args, req) } cmd = "http://" + *dashboard + "/" + cmd if len(args) > 0 { cmd += "?" + args.Encode() } switch meth { case "GET": if req != nil { log.Panicf("%s to %s with req", meth, cmd) } r, err = http.Get(cmd) case "POST": var body io.Reader if req != nil { b, err := json.Marshal(req) if err != nil { return err } body = bytes.NewBuffer(b) } r, err = http.Post(cmd, "text/json", body) default: log.Panicf("%s: invalid method %q", cmd, meth) panic("invalid method: " + meth) } if err != nil { return err } defer r.Body.Close() body := new(bytes.Buffer) if _, err := body.ReadFrom(r.Body); err != nil { return err } // Read JSON-encoded Response into provided resp // and return an error if present. var result = struct { Response interface{} Error string }{ // Put the provided resp in here as it can be a pointer to // some value we should unmarshal into. Response: resp, } if err = json.Unmarshal(body.Bytes(), &result); err != nil { log.Printf("json unmarshal %#q: %s\n", body.Bytes(), err) return err } if result.Error != "" { return errors.New(result.Error) } return nil }
func readFull(data io.Reader) ([]byte, error) { switch data := data.(type) { case memoryBuffer: return data.Bytes(), nil default: return ioutil.ReadAll(data) } panic("not reached") return nil, nil }
// Upload uploads a file to a B2 bucket. If mimeType is "", "b2/x-auto" will be used. // // Concurrent calls to Upload will use separate upload URLs, but consequent ones // will attempt to reuse previously obtained ones to save b2_get_upload_url calls. // Upload URL failures are handled transparently. // // Since the B2 API requires a SHA1 header, normally the file will first be read // entirely into a memory buffer. Two cases avoid the memory copy: if r is a // bytes.Buffer, the SHA1 will be computed in place; otherwise, if r implements io.Seeker // (like *os.File and *bytes.Reader), the file will be read twice, once to compute // the SHA1 and once to upload. // // If a file by this name already exist, a new version will be created. func (b *Bucket) Upload(r io.Reader, name, mimeType string) (*FileInfo, error) { var body io.ReadSeeker switch r := r.(type) { case *bytes.Buffer: defer r.Reset() // we are expected to consume it body = bytes.NewReader(r.Bytes()) case io.ReadSeeker: body = r default: b, err := ioutil.ReadAll(r) if err != nil { return nil, err } body = bytes.NewReader(b) } h := sha1.New() length, err := io.Copy(h, body) if err != nil { return nil, err } sha1Sum := hex.EncodeToString(h.Sum(nil)) var fi *FileInfo for i := 0; i < 5; i++ { if _, err = body.Seek(0, io.SeekStart); err != nil { return nil, err } fi, err = b.UploadWithSHA1(body, name, mimeType, sha1Sum, length) if err == nil { break } } return fi, err }
// dash is copied from the builder binary. It runs the given method and command on the dashboard. // // TODO(bradfitz,adg): unify this somewhere? // // If args is non-nil it is encoded as the URL query string. // If req is non-nil it is JSON-encoded and passed as the body of the HTTP POST. // If resp is non-nil the server's response is decoded into the value pointed // to by resp (resp must be a pointer). func dash(meth, cmd string, args url.Values, req, resp interface{}) error { const builderVersion = 1 // keep in sync with dashboard/app/build/handler.go argsCopy := url.Values{"version": {fmt.Sprint(builderVersion)}} for k, v := range args { if k == "version" { panic(`dash: reserved args key: "version"`) } argsCopy[k] = v } var r *http.Response var err error cmd = dashBase() + cmd + "?" + argsCopy.Encode() switch meth { case "GET": if req != nil { log.Panicf("%s to %s with req", meth, cmd) } r, err = http.Get(cmd) case "POST": var body io.Reader if req != nil { b, err := json.Marshal(req) if err != nil { return err } body = bytes.NewBuffer(b) } r, err = http.Post(cmd, "text/json", body) default: log.Panicf("%s: invalid method %q", cmd, meth) panic("invalid method: " + meth) } if err != nil { return err } defer r.Body.Close() if r.StatusCode != http.StatusOK { return fmt.Errorf("bad http response: %v", r.Status) } body := new(bytes.Buffer) if _, err := body.ReadFrom(r.Body); err != nil { return err } // Read JSON-encoded Response into provided resp // and return an error if present. var result = struct { Response interface{} Error string }{ // Put the provided resp in here as it can be a pointer to // some value we should unmarshal into. Response: resp, } if err = json.Unmarshal(body.Bytes(), &result); err != nil { log.Printf("json unmarshal %#q: %s\n", body.Bytes(), err) return err } if result.Error != "" { return errors.New(result.Error) } return nil }