func (s *apiServer) ListJobs(ctx context.Context, r *types.ListJobsRequest) (*types.ListJobsResponse, error) { var jobs []*types.Job if err := s.DB.View(func(tx *bolt.Tx) error { // Retrieve the jobs bucket. b := tx.Bucket(jobsDBBucket) return b.ForEach(func(k, v []byte) error { // ignore state keys if strings.HasSuffix(string(k), stateSuffix) { return nil } var job types.Job if err := json.Unmarshal(v, &job); err != nil { return fmt.Errorf("Unmarshal job failed: %v\nRaw: %s", err, v) } job.Status = string(b.Get(jobStateByte(job.Id))) jobs = append(jobs, &job) return nil }) }); err != nil { return nil, fmt.Errorf("Getting jobs from db failed: %v", err) } return &types.ListJobsResponse{Jobs: jobs}, nil }
func (s *apiServer) StartJob(ctx context.Context, c *types.StartJobRequest) (*types.StartJobResponse, error) { job := types.Job{ Name: c.Name, Args: c.Args, Artifacts: c.Artifacts, EmailRecipient: c.EmailRecipient, } addJob := func(tx *bolt.Tx) error { // Retrieve the jobs bucket. b := tx.Bucket(jobsDBBucket) // Generate ID for the job. // This returns an error only if the Tx is closed or not writeable. // That can't happen in an Update() call so ignore the error check. id, _ := b.NextSequence() job.Id = uint32(id) // Marshal job data into bytes. buf, err := json.Marshal(job) if err != nil { return fmt.Errorf("Marshal job failed: %v", err) } // put the job into the bucket if err := b.Put(jobIDByte(job.Id), buf); err != nil { return fmt.Errorf("Putting job into bucket failed: %v", err) } // put the job state into the bucket // we will keep these seperate for updating more easily if err := b.Put(jobStateByte(job.Id), jobCreated); err != nil { return fmt.Errorf("Putting job state into bucket failed: %v", err) } return nil } if err := s.DB.Update(addJob); err != nil { return nil, fmt.Errorf("Adding job to database failed: %v", err) } // create the job runner j, err := createJob(job.Id, s.StateDir, job.Args) if err != nil { return nil, err } // start the command if err := j.cmd.Start(); err != nil { if err := s.updateState(job.Id, jobFailed); err != nil { logrus.Warn(err) } return nil, fmt.Errorf("Starting cmd [%s] failed: %v", j.cmdStr, err) } if err := s.updateState(job.Id, jobStarted); err != nil { return nil, err } // run and wait for the command, in a go routine go func() { state := jobCompleted if err := j.run(); err != nil { logrus.Error(err) state = jobFailed } job.Status = string(state) if err := s.updateState(job.Id, state); err != nil { logrus.Error(err) } // send an email with the logs if job.EmailRecipient != "" { // get the logs if err := s.sendEmail(job); err != nil { logrus.Errorf("Sending email to %s for job name %s failed: %v", job.EmailRecipient, job.Name, err) } } }() if err := s.updateState(job.Id, jobRunning); err != nil { return nil, err } return &types.StartJobResponse{ Id: job.Id, }, nil }