// A helper function used by both the 'deploy' and 'deploy get' commands to // display a list of responses for a deployment. func displayResponses(rs api.DeploymentResponses, consolidate bool) { if consolidate { cResponses := consolidateResponses(rs) for _, r := range cResponses { var mbStr string if len(r.Mailboxes) == 1 { mbStr = r.Mailboxes[0] } else { mbStr = fmt.Sprintf("%d/%d", len(r.Mailboxes), len(rs)) } if r.IsError == true { log.Errorf("%s: %s", mbStr, r.Response) } else { log.Infof("%s: %s", mbStr, r.Response) } } } else { for _, r := range rs { if r.IsError == true { log.Errorf("%s: %s", r.Mailbox, r.Response) } else { log.Infof("%s: %s", r.Mailbox, r.Response) } } } }
// deleteMessage is used by clients to mark messages as processed. The message // remains in the database, but is marked deleted and will no longer be // presented to polling clients. func deleteMessage(w http.ResponseWriter, r *http.Request) { var request api.DeleteMessageRequest err := readRequest(r, &request) if err != nil { sendError(w, "Could not parse json request") return } accessKey, _ := mailbox.FindKeyByName(request.AccessKeyName) if accessKey == nil { sendError(w, "Access key invalid") return } if !request.Validate(accessKey.Secret) { sendError(w, "Signature is invalid") return } err = mailbox.DeleteMessage(request.Message) if err != nil { sendError(w, "Could not delete message") return } resp := api.DeleteMessageResponse{Message: request.Message} resp.Sign(accessKey.Name, accessKey.Secret) log.Infof("Message %s deleted", request.Message) writeResponse(&w, resp) }
func getAsset(w http.ResponseWriter, r *http.Request) { var ( request = api.GetAssetRequest{} ) err := readRequest(r, &request) if err != nil { sendError(w, err.Error()) return } accessKey, err := mailbox.FindKeyByName(request.AccessKeyName) if accessKey == nil { sendError(w, "Access key is invalid") return } if !request.Validate(accessKey.Secret) { sendError(w, "Could not validate signature") return } fp := filepath.Join(filesPath(), request.MD5) if _, err := os.Stat(fp); os.IsNotExist(err) { sendError(w, "Asset not found on server") return } log.Infof("Serving asset to %s", accessKey.Name) http.ServeFile(w, r, fp) }
func runMigration(version, sql string) error { log.Infof("Upgrading database to version %s", version) fullSql := ` BEGIN TRANSACTION; ` + sql + ` UPDATE properties SET value = $1 WHERE key == "dbversion"; COMMIT;` _, _, err := DB.Run(ql.NewRWCtx(), fullSql, version) return err }
// register is used by administrative clients to reigster new mailboxes. func register(w http.ResponseWriter, r *http.Request) { var request api.RegisterRequest err := readRequest(r, &request) if err != nil { sendError(w, err.Error()) return } accessKey, err := mailbox.FindKeyByName(request.AccessKeyName) if accessKey == nil { sendError(w, "Access key is invalid") return } if !request.Validate(accessKey.Secret) { sendError(w, "Could not validate signature") return } if !accessKey.CanAdmin() { sendError(w, "Not allowed to register mailboxes.") return } if mailbox.KeyExists(request.Mailbox) { sendError(w, "An access key already exists with that mailbox name") return } mb, err := mailbox.Create(request.Mailbox) if err != nil { sendError(w, err.Error()) return } mbKey := &mailbox.AccessKey{ MailboxId: mb.Id, } err = mbKey.Create() if err != nil { sendError(w, err.Error()) return } resp := api.RegisterResponse{ Mailbox: mb.Id, AccessKeyName: mbKey.Name, AccessKeySecret: mbKey.Secret, } resp.Sign(accessKey.Name, accessKey.Secret) writeResponse(&w, resp) log.Infof("Mailbox %s registered.", mb.Id) }
// getDeploymentInfo returns the information about a deployment. func getDeploymentInfo(depId string, consolidate bool) { client, _ := ClientFromConfig() stats, err := client.PollDeployment(depId, func(stats *api.DeploymentStats) bool { return false }) if err != nil { log.Debug(err.Error()) log.Error("Could not get deployment results") return } log.Info("") log.Alert(depId) log.Info("") log.Infof("Total messages: %d", stats.MessageCount) log.Infof("Pending messages: %d", stats.PendingCount) log.Infof("Total responses: %d", stats.ResponseCount) if len(stats.Responses) > 0 { log.Alert("\nResponses:") } stats.Responses.Sort() displayResponses(stats.Responses, consolidate) log.Info("") }
func (client *Client) DownloadAsset(md5 string) (string, error) { tmpFile, err := ioutil.TempFile("", "conduit.") if err != nil { return "", err } defer tmpFile.Close() req := api.GetAssetRequest{ MD5: md5, } req.Sign(client.AccessKeyName, client.AccessKey) requestBytes, err := json.Marshal(req) if err != nil { return "", err } url := fmt.Sprintf("http://%s/%s", client.Host, "asset") if client.ShowRequests { log.Infof("URL: %s\nRequest: %s", url, string(requestBytes)) } requestReader := bytes.NewReader(requestBytes) resp, err := http.Post(url, "application/json", requestReader) if resp != nil { defer resp.Body.Close() } if err != nil { return "", err } if resp.StatusCode == 200 { _, err = io.Copy(tmpFile, resp.Body) if err != nil { return "", err } return tmpFile.Name(), nil } else { return "", errors.New(fmt.Sprintf("Could not download asset (%d)", resp.StatusCode)) } }
func cleanupFiles() error { files, _ := ioutil.ReadDir(filesPath()) for _, f := range files { pending, err := mailbox.AssetPending(f.Name()) if err == nil && !pending { log.Infof("Cleaning up file %s", f.Name()) err := os.Remove(filepath.Join(filesPath(), f.Name())) if err != nil { log.Warn("File clenaup " + err.Error()) } } else if err != nil { return err } else { if time.Since(f.ModTime()) > 720*time.Hour { err := os.Remove(filepath.Join(filesPath(), f.Name())) if err != nil { log.Warn("File clenaup " + err.Error()) } } } } return nil }
// deployRespond is used by scripts to "respond" with information from the // remote system. These responses can then be reported to the admin client that // deployed the original script. func deployRespond(w http.ResponseWriter, r *http.Request) { var request api.ResponseRequest err := readRequest(r, &request) if err != nil { sendError(w, "Could not parse request") return } accessKey, err := mailbox.FindKeyByName(request.AccessKeyName) if accessKey == nil { sendError(w, "Access key is not valid") return } if !request.Validate(accessKey.Secret) { sendError(w, "Could not validate signature") return } msg, err := mailbox.FindMessage(request.Message) if err != nil { sendError(w, err.Error()) return } if msg == nil { sendError(w, "Could not find message "+request.Message) return } mb, err := mailbox.Find(msg.Mailbox) if err != nil { sendError(w, err.Error()) return } if mb == nil { sendError(w, "Mailbox not found") return } dep, err := mailbox.FindDeployment(msg.Deployment) if err != nil { sendError(w, err.Error()) return } if dep == nil { sendError(w, "Could not find deployment "+msg.Deployment) return } if !accessKey.CanGet(mb) { sendError(w, "Not allowed to respond to deploys") return } err = dep.AddResponse(msg.Mailbox, request.Response, request.Error) if err != nil { sendError(w, err.Error()) return } response := api.SimpleResponse{Success: true} response.Sign(accessKey.Name, accessKey.Secret) log.Infof("Reponse added to %s from %s", dep.Id, msg.Mailbox) writeResponse(&w, response) }
// registerCmd represents the register command var serverRegisterCmd = &cobra.Command{ Use: "register [name]", Short: "Register a new mailbox", Long: `This registers a new mailbox for the local server.`, Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { log.Fatal("No mailbox name specified") } mailbox.OpenDB() mb, err := mailbox.Create(args[0]) if err != nil { log.Debug(err.Error()) log.Fatal("Could not create mailbox") } key := &mailbox.AccessKey{MailboxId: mb.Id} err = key.Create() if err != nil { log.Debug(err.Error()) log.Fatal("Could not create mailbox access token") } log.Infof("Mailbox created: %s", mb.Id) log.Infof("Access key created: %s", key.Secret) }, } func init() { serverCmd.AddCommand(serverRegisterCmd) }
log.Debug(err.Error()) log.Fatal("Local execution error") } if res != "" && res != "undefined" { log.Info("Local: " + res) } resp, err := client.Put(mailboxes, pattern, string(data), cmd.Flag("name").Value.String(), cmd.Flag("attach").Value.String()) if err != nil { log.Debug(err.Error()) log.Error("Could not deploy script") } if err != nil { log.Fatal(err.Error()) } log.Infof("Script deployed to %d mailboxes (%d bytes)", len(resp.Mailboxes), len(data)) log.Infof("Deployment name: %s", resp.Deployment) if cmd.Flag("no-results").Value.String() == "true" { return } bar := uiprogress.AddBar(len(resp.Mailboxes)) bar.AppendCompleted() bar.PrependElapsed() uiprogress.Start() var pollStart = time.Now() stats, err := client.PollDeployment(resp.Deployment, func(stats *api.DeploymentStats) bool { messagesProcessed := stats.MessageCount - stats.PendingCount bar.Set(int(messagesProcessed))
// deployInfo is used to both report a list of recent deployments and get // details and responses on a specific deployment. func deployInfo(w http.ResponseWriter, r *http.Request) { var request api.DeploymentStatsRequest err := readRequest(r, &request) if err != nil { sendError(w, "Could not parse request") return } if request.NamePattern == "" { request.NamePattern = ".*" } if request.TokenPattern == "" { request.TokenPattern = ".*" } accessKey, err := mailbox.FindKeyByName(request.AccessKeyName) if err != nil || accessKey == nil { sendError(w, "Access key is invalid") return } if !accessKey.CanAdmin() { sendError(w, "Not allowed to list deployments") return } if !request.Validate(accessKey.Secret) { sendError(w, "Signature invalid") return } response := api.DeploymentStatsResponse{} if request.Deployment == "" { log.Infof("Listing all deploys for %s", accessKey.Name) deployments, err := mailbox.ListDeployments(request.NamePattern, int(request.Count), request.TokenPattern) if err != nil { sendError(w, err.Error()) return } for _, d := range deployments { dStats, err := d.Stats() if err != nil { sendError(w, err.Error()) return } statsResp := api.DeploymentStats{ Name: d.Name, Id: d.Id, PendingCount: dStats.PendingCount, MessageCount: dStats.MessageCount, ResponseCount: dStats.ResponseCount, CreatedAt: d.DeployedAt, DeployedBy: d.DeployedBy, } response.Deployments = append(response.Deployments, statsResp) } } else { dep, err := mailbox.FindDeployment(request.Deployment) if err != nil { sendError(w, err.Error()) return } if dep == nil { sendError(w, "Deployment not found") return } dStats, err := dep.Stats() if err != nil { sendError(w, err.Error()) return } deploymentResponses, err := dep.GetResponses() if err != nil { sendError(w, err.Error()) return } statsResp := api.DeploymentStats{ Name: dep.Name, Id: dep.Id, PendingCount: dStats.PendingCount, MessageCount: dStats.MessageCount, ResponseCount: dStats.ResponseCount, CreatedAt: dep.DeployedAt, Responses: []api.DeploymentResponse{}, } for _, r := range deploymentResponses { apiR := api.DeploymentResponse{ Mailbox: r.Mailbox, Response: r.Response, RespondedAt: r.RespondedAt, IsError: r.IsError, } statsResp.Responses = append(statsResp.Responses, apiR) } response.Deployments = append(response.Deployments, statsResp) } response.Sign(accessKey.Name, accessKey.Secret) writeResponse(&w, response) }
Long: `Remove a mailbox, its access tokens, and messages.`, Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { log.Fatal("No mailbox identifier specified.") } client, err := ClientFromConfig() if err != nil { log.Debug(err.Error()) log.Fatal("Could not configure client") } _, err = client.DeregisterMailbox(args[0]) if err != nil { log.Debug(err.Error()) log.Fatal("Could not remove mailbox.") } log.Infof("Mailbox deregistered: %s", args[0]) }, } func init() { RootCmd.AddCommand(deregisterCmd) // Here you will define your flags and configuration settings. // Cobra supports Persistent Flags which will work for this command // and all subcommands, e.g.: // deregisterCmd.PersistentFlags().String("foo", "", "A help for foo") // Cobra supports local flags which will only run when this command // is called directly, e.g.: // deregisterCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
// putMessage is used to deploy messages to mailboxes, etiher by a list of // mailboxes or a pattern. Messages are organized into a deployment and // persisted to the database. If any receiptients are currently polling the // message will be forwarded to that session. func putMessage(w http.ResponseWriter, r *http.Request) { var request api.PutMessageRequest err := readRequest(r, &request) if err != nil { sendError(w, "Could not parse request") } mailboxes := []mailbox.Mailbox{} if request.Pattern != "" { results, err := mailbox.Search(request.Pattern) if err != nil { sendError(w, err.Error()) return } for _, mb := range results { mailboxes = append(mailboxes, mb) } } for _, mbId := range request.Mailboxes { mb, err := mailbox.Find(mbId) if err != nil { sendError(w, err.Error()) } if mb == nil { sendError(w, fmt.Sprintf("Mailbox not found (%s)", mbId)) return } mailboxes = append(mailboxes, *mb) } if len(mailboxes) == 0 { sendError(w, "No mailboxes specified") return } if request.Asset != "" { assetPath := filepath.Join(filesPath(), request.Asset) if _, err := os.Stat(assetPath); os.IsNotExist(err) { sendError(w, "Asset does not exist on server") return } } mbList := []string{} // dep, err := mailbox.CreateDeployment(request.DeploymentName, request.Token, // request.Body) dep := mailbox.Deployment{ Name: request.DeploymentName, DeployedBy: request.AccessKeyName, MessageBody: request.Body, Asset: request.Asset, } err = dep.Create() if err != nil { sendError(w, err.Error()) return } accessKey, err := mailbox.FindKeyByName(request.AccessKeyName) if err != nil || accessKey == nil { sendError(w, "Access key is invalid") return } if !request.Validate(accessKey.Secret) { sendError(w, "Signature not valid") return } for _, mb := range mailboxes { if !accessKey.CanPut(&mb) { sendError(w, "Not allowed to send messages to "+mb.Id) return } var msg *mailbox.Message msg, err = dep.Deploy(&mb) mbList = append(mbList, mb.Id) if err != nil { sendError(w, err.Error()) return } if pollChannel, ok := pollingChannels[mb.Id]; ok { time.Sleep(50 * time.Millisecond) pollChannel <- msg } } resp := api.PutMessageResponse{ MessageSize: r.ContentLength, Mailboxes: mbList, Deployment: dep.Id, } resp.Sign(accessKey.Name, accessKey.Secret) log.Infof("Message received for %d mailboxes from %s", len(mbList), dep.DeployedBy) writeResponse(&w, resp) }
Short: "Purge all messages for a mailbox.", Long: `Delete all messages for the local server for a given mailbox. This purges local data from the server's database. To purge a remote server use conduit purge instead.`, Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { log.Fatal("No mailbox specified.") } mailbox.OpenDB() mailboxId := args[0] mb, err := mailbox.Find(mailboxId) if err != nil { log.Fatal("Could not lookup mailbox.") log.Debug(err.Error()) } if mb == nil { log.Fatal("Could not find the mailbox specified") } c, err := mb.Purge() if err != nil { log.Fatal("Could not purge mailbox") log.Debug(err.Error()) } log.Infof("Mailbox purged of %d messages.", c) }, } func init() { serverCmd.AddCommand(purgeCmd) }
func runClient(noLoop bool) { viper.SetDefault("script_timeout", 300) log.LogFile = true if viper.GetBool("master.enabled") { log.Info("Launching master server") go runProxy() } log.Info("Waiting for messages...") client, _ := ClientFromConfig() if viper.IsSet("master.host") { client.UseProxy = true client.ProxyAddress = "http://" + viper.GetString("master.host") } var persistantScripts = []*engine.ScriptEngine{} if viper.IsSet("agents") { engine.Agents = viper.GetStringMapString("agents") engine.AgentAccessKey = viper.GetString("access_key") } // Begin polling cycle for { time.Sleep(time.Duration(rand.Intn(1000)+2000) * time.Millisecond) resp, err := client.Get() // If an error is returned by the client we will begin an exponential back // off in retrying. The backoff caps out at 15 retries. if err != nil { log.Error("Error getting messages: " + err.Error()) if errorCount < 15 { errorCount++ } expBackoff := int(math.Pow(float64(errorCount), 2)) displacement := rand.Intn(errorCount + 1) sleepTime := expBackoff + displacement time.Sleep(time.Duration(sleepTime) * time.Second) continue } // A response was received but it might be an empty response from the // server timing out the long poll. errorCount = 0 if resp.Body != "" { log.Infof("Script receieved (%s)", resp.Message) eng := engine.New() eng.Constant("DEPLOYMENT_ID", resp.Deployment) eng.Constant("SCRIPT_ID", resp.Message) persistant, _ := eng.GetVar("$persistant", resp.Body) if p, ok := persistant.(bool); ok { if p { persistantScripts = append(persistantScripts, eng) } } if resp.Asset != "" { assetPath, err := client.DownloadAsset(resp.Asset) if err != nil { client.Respond(resp.Message, "Could not download asset", true) log.Error("Could not download asset") _, err = client.Delete(resp.Message) continue } else { log.Infof("Downloaded asset to %s", assetPath) } eng.SetAsset(assetPath) } executionStartTime := time.Now() errChan := make(chan string, 1) timeoutSeconds := viper.GetInt("script_timeout") go func() { err = eng.Execute(resp.Body) if err != nil { errChan <- err.Error() } else { errChan <- "" } }() select { case e := <-errChan: if e != "" { err = errors.New(e) } case <-time.After(time.Second * time.Duration(timeoutSeconds)): log.Warn("Timing out script") err = errors.New("Scirpt timeed out") } executionTime := time.Since(executionStartTime) log.Infof("Script executed in %s", executionTime) if err != nil { log.Error("Error executing script " + resp.Message) log.Debug(err.Error()) client.Respond(resp.Message, err.Error(), true) } _, err = client.Delete(resp.Message) if err != nil { log.Debug(err.Error()) log.Error("Could not confirm script.") } else { log.Debug("Script confirmed: " + resp.Message) } } if noLoop == true { break } } }
// getMessage is used by clients to poll for mailbox messages. This method will // search the database for messages for the given mailbox. If the mailbox is // empty and long_polling is enabled it will create a channel and add it to // pollingChannels. It will then wait for a message to be pushed to that // channel. It will then continue to output that message. Messages are pushed by // the putMessage method. func getMessage(w http.ResponseWriter, r *http.Request) { if !EnableLongPolling { time.Sleep(ThrottleDelay) } var request api.GetMessageRequest err := readRequest(r, &request) if err != nil { sendError(w, err.Error()) return } mb, err := mailbox.Find(request.Mailbox) if err != nil { sendError(w, err.Error()) return } if mb == nil { log.Warnf("Could not find a mailbox named '%s'", request.Mailbox) sendError(w, fmt.Sprintf("Mailbox %s not found.", request.Mailbox)) return } log.Debugf("Message request from %s", mb.Id) accessKey, err := mailbox.FindKeyByName(request.AccessKeyName) if accessKey == nil { log.Warnf("Could not find an access key named '%s'", request.AccessKeyName) sendError(w, "Access key is invalid") return } if !request.Validate(accessKey.Secret) { log.Warnf(fmt.Sprintf("Signature for %s invalid", mb.Id)) sendError(w, "Signature is invalid") return } if !accessKey.CanGet(mb) { sendError(w, "Not allowed to get messages from this mailbox.") return } if err := mb.Checkin(r.RemoteAddr, request.Version); err != nil { sendError(w, err.Error()) return } msg, err := mb.GetMessage() if err != nil { sendError(w, err.Error()) log.Errorf("Error retrieving messages: %s", err.Error()) return } if EnableLongPolling == true && msg == nil { if _, ok := pollingChannels[mb.Id]; ok { delete(pollingChannels, mb.Id) } // Create a channel for the client. This channel has a message pushed to it // from the putMessage function. When a message gets delivered. pollingChannels[mb.Id] = make(chan *mailbox.Message) timeout := make(chan bool, 1) // This goroutine will create a timeout to close the long polling connection // and force the client to reconnect. go func() { // Randomize the polling timeout in order to stagger client reconnects. sleepTime := rand.Intn(500) + 200 time.Sleep(time.Duration(sleepTime) * time.Second) timeout <- true }() // Wait for either a timeout or a message to be sent to a channel. select { case m := <-pollingChannels[mb.Id]: msg = m case <-timeout: response := api.GetMessageResponse{} response.Sign(accessKey.Name, accessKey.Secret) writeResponse(&w, response) delete(pollingChannels, mb.Id) return } delete(pollingChannels, mb.Id) } if msg == nil { writeResponse(&w, nil) return } dep, err := msg.GetDeployment() if err != nil { sendError(w, err.Error()) return } response := api.GetMessageResponse{ Message: msg.Id, Body: msg.Body, CreatedAt: msg.CreatedAt, ReceiveCount: msg.ReceiveCount, Deployment: msg.Deployment, Asset: dep.Asset, } response.Sign(accessKey.Name, accessKey.Secret) log.Infof("Delivering message %s to %s", response.Message, mb.Id) writeResponse(&w, response) }
func (client *Client) Upload(fpath string) (*api.SimpleResponse, error) { var ( body = &bytes.Buffer{} writer = multipart.NewWriter(body) req = api.UploadFileRequest{Filename: filepath.Base(fpath)} err error ) defer writer.Close() req.MD5, err = client.hashFile(fpath) if err != nil { return nil, err } log.Infof("Uploading file %s", fpath) hClient, err := client.getHttpClient() if err != nil { return nil, err } url := fmt.Sprintf("http://%s/%s", client.Host, "upload") // Write JSON data as post param req.Sign(client.AccessKeyName, client.AccessKey) requestBytes, err := json.Marshal(req) err = writer.WriteField("data", string(requestBytes)) if err != nil { return nil, err } // Write file to request file, err := os.Open(fpath) if file != nil { defer file.Close() } if err != nil { return nil, err } part, err := writer.CreateFormFile("file", fpath) if err != nil { return nil, err } _, err = io.Copy(part, file) writer.Close() // Make the request hReq, err := http.NewRequest("POST", url, body) hReq.Header.Set("Content-Type", writer.FormDataContentType()) resp, err := hClient.Do(hReq) if resp != nil { defer resp.Body.Close() } if err != nil { return nil, err } // Read the response responseData, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 200 { var errorResponse api.ApiError json.Unmarshal(responseData, &errorResponse) return nil, errors.New(errorResponse.Error) } res := &api.SimpleResponse{} json.Unmarshal(responseData, res) return res, err }
Long: `This will register a new mailbox on the configured Conduit server. An access key will also be generated and bound to the mailbox. This key can be used by Conduit clients to receive messages from this mailbox. Mailboxes can be locally generated as well. Use "conduit help server register" for more information. `, Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { log.Fatal("No mailbox identifier specified.") } client, err := ClientFromConfig() if err != nil { log.Debug(err.Error()) log.Fatal("Could not configure client") } resp, err := client.RegisterMailbox(args[0]) if err != nil { log.Debug(err.Error()) log.Fatal("Could not register mailbox.") } log.Infof("Mailbox registered: %s", resp.Mailbox) log.Infof("Access key: %s", resp.AccessKeySecret) }, } func init() { RootCmd.AddCommand(registerCmd) }
func acceptFile(w http.ResponseWriter, r *http.Request) { var ( request = api.UploadFileRequest{} ) defer r.Body.Close() // parse form post data err := r.ParseMultipartForm(1000000) if err != nil { sendError(w, err.Error()) return } // Get reuqest data and unmarshal reqData := r.FormValue("data") err = json.Unmarshal([]byte(reqData), &request) if err != nil { sendError(w, "Could not parse request!") return } accessKey, _ := mailbox.FindKeyByName(request.AccessKeyName) if accessKey == nil { sendError(w, "Invalid access key") return } if !accessKey.CanAdmin() { sendError(w, "Not allowed to upload files") return } if !request.Validate(accessKey.Secret) { sendError(w, "Invalid signature") return } // Save the posted file file, _, err := r.FormFile("file") if file != nil { defer file.Close() } if err != nil { sendError(w, err.Error()) return } path := filepath.Join(filesPath(), request.MD5) out, err := os.Create(path) if out != nil { defer out.Close() } if err != nil { sendError(w, err.Error()) return } _, err = io.Copy(out, file) if err != nil { sendError(w, err.Error()) return } fileHash, err := hashFile(path) if err != nil { sendError(w, err.Error()) return } if request.MD5 != fileHash { defer os.Remove(filepath.Join(filesPath(), request.MD5)) sendError(w, fmt.Sprintf("MD5 missmatch %s != %s", request.MD5, fileHash)) return } log.Infof("File uploaded %s", request.MD5) response := api.SimpleResponse{Success: true} response.Sign(accessKey.Name, accessKey.Secret) writeResponse(&w, response) }
} limitToken := (cmd.Flag("all").Value.String() == "false") count, _ := strconv.ParseInt(cmd.Flag("count").Value.String(), 10, 64) resp, err := client.ListDeploys(cmd.Flag("name").Value.String(), limitToken, int(count)) if err != nil { log.Debug(err.Error()) log.Error("Could not list deploys") return } if len(resp.Deployments) == 0 { log.Warn("There are no open deployments") } for _, dep := range resp.Deployments { log.Alertf("%s:", dep.Name) log.Infof(" Deployed at: %s", dep.CreatedAt.Format("01/02 03:04 PM")) log.Infof(" Deployed by: %s", dep.DeployedBy) log.Infof(" Executions: %d/%d", dep.MessageCount-dep.PendingCount, dep.MessageCount) log.Infof(" Repsonses: %d/%d", dep.ResponseCount, dep.MessageCount) } }, } func init() { deployCmd.AddCommand(listCmd) listCmd.Flags().IntP("count", "c", 10, "The maximum number of deployments to return") listCmd.Flags().BoolP("all", "a", false, "Return all deployments") listCmd.Flags().StringP("name", "n", ".*", "A search pattern to limit deployment names")