Example #1
0
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
}
Example #2
0
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
}