Merge pull request #2907 from hashicorp/b-promote-err

Expose FSM errors into deployment watcher and API
This commit is contained in:
Alex Dadgar 2017-07-25 17:31:33 -07:00 committed by GitHub
commit 8dc8e17c1b
3 changed files with 63 additions and 10 deletions

View file

@ -89,12 +89,25 @@ type deploymentWatcherRaftShim struct {
apply raftApplyFn
}
// convertApplyErrors parses the results of a raftApply and returns the index at
// which it was applied and any error that occured. Raft Apply returns two
// seperate errors, Raft library errors and user returned errors from the FSM.
// This helper, joins the errors by inspecting the applyResponse for an error.
func (d *deploymentWatcherRaftShim) convertApplyErrors(applyResp interface{}, index uint64, err error) (uint64, error) {
if applyResp != nil {
if fsmErr, ok := applyResp.(error); ok && fsmErr != nil {
return index, fsmErr
}
}
return index, err
}
func (d *deploymentWatcherRaftShim) UpsertEvals(evals []*structs.Evaluation) (uint64, error) {
update := &structs.EvalUpdateRequest{
Evals: evals,
}
_, index, err := d.apply(structs.EvalUpdateRequestType, update)
return index, err
fsmErrIntf, index, raftErr := d.apply(structs.EvalUpdateRequestType, update)
return d.convertApplyErrors(fsmErrIntf, index, raftErr)
}
func (d *deploymentWatcherRaftShim) UpsertJob(job *structs.Job) (uint64, error) {
@ -102,21 +115,21 @@ func (d *deploymentWatcherRaftShim) UpsertJob(job *structs.Job) (uint64, error)
update := &structs.JobRegisterRequest{
Job: job,
}
_, index, err := d.apply(structs.JobRegisterRequestType, update)
return index, err
fsmErrIntf, index, raftErr := d.apply(structs.JobRegisterRequestType, update)
return d.convertApplyErrors(fsmErrIntf, index, raftErr)
}
func (d *deploymentWatcherRaftShim) UpdateDeploymentStatus(u *structs.DeploymentStatusUpdateRequest) (uint64, error) {
_, index, err := d.apply(structs.DeploymentStatusUpdateRequestType, u)
return index, err
fsmErrIntf, index, raftErr := d.apply(structs.DeploymentStatusUpdateRequestType, u)
return d.convertApplyErrors(fsmErrIntf, index, raftErr)
}
func (d *deploymentWatcherRaftShim) UpdateDeploymentPromotion(req *structs.ApplyDeploymentPromoteRequest) (uint64, error) {
_, index, err := d.apply(structs.DeploymentPromoteRequestType, req)
return index, err
fsmErrIntf, index, raftErr := d.apply(structs.DeploymentPromoteRequestType, req)
return d.convertApplyErrors(fsmErrIntf, index, raftErr)
}
func (d *deploymentWatcherRaftShim) UpdateDeploymentAllocHealth(req *structs.ApplyDeploymentAllocHealthRequest) (uint64, error) {
_, index, err := d.apply(structs.DeploymentAllocHealthRequestType, req)
return index, err
fsmErrIntf, index, raftErr := d.apply(structs.DeploymentAllocHealthRequestType, req)
return d.convertApplyErrors(fsmErrIntf, index, raftErr)
}

View file

@ -1973,6 +1973,7 @@ func (s *StateStore) UpdateDeploymentPromotion(index uint64, req *structs.ApplyD
}
}
haveCanaries := false
var unhealthyErr multierror.Error
for {
raw := iter.Next()
@ -1997,12 +1998,18 @@ func (s *StateStore) UpdateDeploymentPromotion(index uint64, req *structs.ApplyD
multierror.Append(&unhealthyErr, fmt.Errorf("Canary allocation %q for group %q is not healthy", alloc.ID, alloc.TaskGroup))
continue
}
haveCanaries = true
}
if err := unhealthyErr.ErrorOrNil(); err != nil {
return err
}
if !haveCanaries {
return fmt.Errorf("no canaries to promote")
}
// Update deployment
copy := deployment.Copy()
copy.ModifyIndex = index

View file

@ -5030,6 +5030,39 @@ func TestStateStore_UpsertDeploymentPromotion_Unhealthy(t *testing.T) {
}
}
// Test promoting a deployment with no canaries
func TestStateStore_UpsertDeploymentPromotion_NoCanaries(t *testing.T) {
state := testStateStore(t)
// Create a job
j := mock.Job()
if err := state.UpsertJob(1, j); err != nil {
t.Fatalf("bad: %v", err)
}
// Create a deployment
d := mock.Deployment()
d.JobID = j.ID
if err := state.UpsertDeployment(2, d); err != nil {
t.Fatalf("bad: %v", err)
}
// Promote the canaries
req := &structs.ApplyDeploymentPromoteRequest{
DeploymentPromoteRequest: structs.DeploymentPromoteRequest{
DeploymentID: d.ID,
All: true,
},
}
err := state.UpdateDeploymentPromotion(4, req)
if err == nil {
t.Fatalf("bad: %v", err)
}
if !strings.Contains(err.Error(), "no canaries to promote") {
t.Fatalf("expect error promoting non-existant canaries: %v", err)
}
}
// Test promoting all canaries in a deployment.
func TestStateStore_UpsertDeploymentPromotion_All(t *testing.T) {
state := testStateStore(t)