Skip to content

Commit

Permalink
Merge pull request #754 from ystia/feature/GH-753_Workflows_steps_rep…
Browse files Browse the repository at this point in the history
…lays_on_error

Workflows steps replays on error
  • Loading branch information
laurentganne committed Sep 9, 2021
2 parents 7bd38d6 + a540b9b commit f95572a
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 38 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## UNRELEASED

### FEATURES

* Workflows steps replays on error ([GH-753](https://github.com/ystia/yorc/issues/753))

### ENHANCEMENTS

* Slurm: Use sacct to retrieve job status when scontrol show job does not show the job anymore ([GH-757](https://github.com/ystia/yorc/issues/757))
Expand Down
39 changes: 4 additions & 35 deletions commands/deployments/tasks/dep_fix_task_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,9 @@
package tasks

import (
"bytes"
"encoding/json"
"log"
"net/http"
"path"
"strings"

"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/ystia/yorc/v4/commands/deployments"
"github.com/ystia/yorc/v4/commands/httputil"
"github.com/ystia/yorc/v4/tasks"
)

Expand All @@ -35,40 +26,18 @@ func init() {
}

var updateTaskStepCmd = &cobra.Command{
Use: "fix <DeploymentId> <TaskId> <StepName>",
Short: "Fix a deployment task step on error",
Deprecated: "use 'yorc deployments tasks update-step-state' instead.",
Use: "fix <DeploymentId> <TaskId> <StepName>",
Short: "Fix a deployment task step on error",
Long: `Fix a task step specifying the deployment id, the task id and the step name.
The task step must be on error to be fixed.`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 3 {
return errors.Errorf("Expecting a deployment id, a task id and a step name(got %d parameters)", len(args))
}
client, err := httputil.GetClient(deployments.ClientConfig)
if err != nil {
httputil.ErrExit(err)
}

// The task step status is set to "done"
step := &tasks.TaskStep{Name: args[2], Status: strings.ToLower(tasks.TaskStepStatusDONE.String())}
body, err := json.Marshal(step)
if err != nil {
log.Panic(err)
}

url := path.Join("/deployments", args[0], "tasks", args[1], "steps", args[2])
request, err := client.NewRequest("PUT", url, bytes.NewBuffer(body))
if err != nil {
httputil.ErrExit(err)
}
updateTaskStepState(args[0], args[1], args[2], tasks.TaskStepStatusDONE.String())

request.Header.Add("Content-Type", "application/json")
response, err := client.Do(request)
if err != nil {
httputil.ErrExit(err)
}
defer response.Body.Close()
ids := args[0] + "/" + args[1]
httputil.HandleHTTPStatusCode(response, ids, "deployment/task/step", http.StatusOK)
return nil
},
}
83 changes: 83 additions & 0 deletions commands/deployments/tasks/dep_update_task_step_state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2021 Bull S.A.S. Atos Technologies - Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois, France.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tasks

import (
"bytes"
"encoding/json"
"net/http"
"path"
"strings"

"github.com/spf13/cobra"

"github.com/ystia/yorc/v4/commands/deployments"
"github.com/ystia/yorc/v4/commands/httputil"
"github.com/ystia/yorc/v4/tasks"
)

func init() {
tasksCmd.AddCommand(updateTaskStepStateCmd)
}

func updateTaskStepState(deploymentID, taskID, stepName, statusStr string) {
client, err := httputil.GetClient(deployments.ClientConfig)
if err != nil {
httputil.ErrExit(err)
}

statusStr = strings.ToLower(statusStr)

// Safety check
_, err = tasks.ParseTaskStepStatus(statusStr)
if err != nil {
httputil.ErrExit(err)
}

step := &tasks.TaskStep{Status: statusStr}
body, err := json.Marshal(step)
if err != nil {
httputil.ErrExit(err)
}

url := path.Join("/deployments", deploymentID, "tasks", taskID, "steps", stepName)
request, err := client.NewRequest("PUT", url, bytes.NewBuffer(body))
if err != nil {
httputil.ErrExit(err)
}

request.Header.Add("Content-Type", "application/json")
response, err := client.Do(request)
if err != nil {
httputil.ErrExit(err)
}
defer response.Body.Close()
ids := deploymentID + "/" + taskID
httputil.HandleHTTPStatusCode(response, ids, "deployment/task/step", http.StatusOK)
}

var updateTaskStepStateCmd = &cobra.Command{
Use: "update-step-state <DeploymentId> <TaskId> <StepName> <state>",
Aliases: []string{"update-state", "update-step", "uss", "us"},
Short: "Update a deployment task step on error",
Args: cobra.ExactArgs(4),
Long: `Update a task step specifying the deployment id, the task id, the step name and a valid state for the step.
The task step must be on error to be update.
This allows to bypass or retry errors happening in a workflow. So accepted state are either DONE or INITIAL.
Use 'yorc deployment tasks resume' command to restart a workflow after having updated it's errors`,
Run: func(cmd *cobra.Command, args []string) {
updateTaskStepState(args[0], args[1], args[2], args[3])
},
}
60 changes: 60 additions & 0 deletions commands/deployments/tasks/dep_update_task_step_state_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2021 Bull S.A.S. Atos Technologies - Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois, France.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tasks

import (
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/ystia/yorc/v4/commands/deployments"
"github.com/ystia/yorc/v4/config"
)

func Test_updateTaskStepState(t *testing.T) {

s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.Path, "fail") {
rw.WriteHeader(http.StatusBadRequest)
rw.Write([]byte("Some user error"))
return
}
rw.WriteHeader(http.StatusOK)
}))
defer s.Close()

deployments.ClientConfig = config.Client{
YorcAPI: strings.Replace(s.URL, "http://", "", 1),
}

type args struct {
deploymentID string
taskID string
stepName string
statusStr string
}
tests := []struct {
name string
args args
}{
{"TestOK", args{"depOK", "taskID", "step", "initial"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
updateTaskStepState(tt.args.deploymentID, tt.args.taskID, tt.args.stepName, tt.args.statusStr)
})
}
}
1 change: 1 addition & 0 deletions rest/dep_tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ func (s *Server) updateTaskStepStatusHandler(w http.ResponseWriter, r *http.Requ
if err != nil {
log.Panic(err)
}
step.Name = stepID

// Check taskStep status change
allowed, err := tasks.CheckTaskStepStatusChange(stepBefore.Status, step.Status)
Expand Down
12 changes: 10 additions & 2 deletions rest/http_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,11 +605,19 @@ Content-Type: application/json

### Update a task step status <a name="task-step-update"></a>

Update a task step status for given deployment and task. For the moment, only step status change from "ERROR" to "DONE" is allowed otherwise an HTTP 401
(Forbidden) error is returned.
Update a task step status for given deployment and task. For the moment, only step status change from "ERROR"
to "DONE" or "INITIAL" is allowed otherwise an HTTP 401 (Forbidden) error is returned.

`PUT /deployments/<deployment_id>/tasks/<taskId>/steps/<stepId>`

**Request body**:

```json
{
"status": "step_status"
}
```

**Response**:

```HTTP
Expand Down
8 changes: 8 additions & 0 deletions tasks/collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ func (c *Collector) ResumeTask(ctx context.Context, taskID string) error {
Key: path.Join(taskPath, "errorMessage"),
Value: []byte(""),
},
&api.KVTxnOp{
Verb: api.KVDelete,
Key: path.Join(taskPath, ".errorFlag"),
},
&api.KVTxnOp{
Verb: api.KVDelete,
Key: path.Join(taskPath, ".cancelFlag"),
},
}
// Set deployment status to initial for some task types
switch taskType {
Expand Down
2 changes: 1 addition & 1 deletion tasks/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ func CheckTaskStepStatusChange(before, after string) (bool, error) {
return false, err
}

if stBefore != TaskStepStatusERROR || stAfter != TaskStepStatusDONE {
if stBefore != TaskStepStatusERROR || (stAfter != TaskStepStatusDONE && stAfter != TaskStepStatusINITIAL) {
return false, nil
}
return true, nil
Expand Down

0 comments on commit f95572a

Please sign in to comment.