-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a synchronous purge endpoint
- Added a new API endpoint `POST /deployments/<id>/purge[?force]` - Added a purge command on CLI - Added a PURGE_FAILED deployment status
- Loading branch information
1 parent
5e1c9fb
commit 947175b
Showing
13 changed files
with
330 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// Copyright 2018 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 deployments | ||
|
||
import ( | ||
"encoding/json" | ||
"io/ioutil" | ||
"net/http" | ||
"path" | ||
"strconv" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/ystia/yorc/v4/commands/httputil" | ||
"github.com/ystia/yorc/v4/log" | ||
"github.com/ystia/yorc/v4/rest" | ||
) | ||
|
||
func init() { | ||
var force bool | ||
var purgeCmd = &cobra.Command{ | ||
Use: "purge <id>", | ||
Short: "purge a deployment", | ||
Long: `Purge a deployment <id>. This deployment should be in UNDEPLOYED status. | ||
If an error is encountered the purge process is stopped and the deployment status is set | ||
to PURGE_FAILED. | ||
A purge may be run in force mode. In this mode Yorc does not check if the deployment is in | ||
UNDEPLOYED status or even if the deployment exist. Moreover, in force mode the purge process | ||
doesn't fail-fast and try to delete as much as it can. An report with encountered errors is | ||
produced at the end of the process.`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if len(args) != 1 { | ||
return errors.Errorf("Expecting a deployment id (got %d parameters)", len(args)) | ||
} | ||
|
||
client, err := httputil.GetClient(ClientConfig) | ||
if err != nil { | ||
httputil.ErrExit(err) | ||
} | ||
deploymentID := args[0] | ||
|
||
return postPurgeRequest(client, deploymentID, force) | ||
|
||
}, | ||
} | ||
purgeCmd.Flags().BoolVarP(&force, "force", "f", false, "Force purge of a deployment ignoring states checks and any errors. This should be use with extrem caution to cleanup environment.") | ||
DeploymentsCmd.AddCommand(purgeCmd) | ||
} | ||
|
||
func postPurgeRequest(client httputil.HTTPClient, deploymentID string, force bool) error { | ||
request, err := client.NewRequest("POST", path.Join("/deployments", deploymentID, "purge"), nil) | ||
if err != nil { | ||
httputil.ErrExit(errors.Wrap(err, httputil.YorcAPIDefaultErrorMsg)) | ||
} | ||
|
||
query := request.URL.Query() | ||
if force { | ||
query.Set("force", strconv.FormatBool(force)) | ||
} | ||
request.URL.RawQuery = query.Encode() | ||
request.Header.Add("Accept", "application/json") | ||
log.Debugf("POST: %s", request.URL.String()) | ||
|
||
response, err := client.Do(request) | ||
if err != nil { | ||
httputil.ErrExit(errors.Wrap(err, httputil.YorcAPIDefaultErrorMsg)) | ||
} | ||
defer response.Body.Close() | ||
|
||
if response.StatusCode == http.StatusOK { | ||
ioutil.ReadAll(response.Body) | ||
return nil | ||
} | ||
var errs rest.Errors | ||
bodyContent, _ := ioutil.ReadAll(response.Body) | ||
json.Unmarshal(bodyContent, &errs) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,7 @@ UPDATE_IN_PROGRESS | |
UPDATED | ||
UPDATE_FAILURE | ||
PURGED | ||
PURGE_FAILED | ||
) | ||
*/ | ||
type DeploymentStatus int | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// Copyright 2019 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 operations | ||
|
||
import ( | ||
"context" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
|
||
"github.com/hashicorp/go-multierror" | ||
"github.com/pkg/errors" | ||
|
||
"github.com/ystia/yorc/v4/deployments" | ||
"github.com/ystia/yorc/v4/events" | ||
"github.com/ystia/yorc/v4/helper/collections" | ||
"github.com/ystia/yorc/v4/helper/consulutil" | ||
"github.com/ystia/yorc/v4/tasks" | ||
) | ||
|
||
// PurgeDeployment allows to completely remove references of a deployment within yorc | ||
// | ||
// Forced purge do not stop on errors and try to delete the maximum of elements while normal purge stops on the first error. | ||
// The error returned by this function may be multi-evaluated use the standard errors.Unwrap method to access individual errors. | ||
// | ||
// ignoreTasks allows to prevent removing a given list of tasks this is particularly useful when calling it within a task. | ||
// This option will probably be transitory for Yorc 4.x before switching to a full synchronous purge model | ||
func PurgeDeployment(ctx context.Context, deploymentID, filepathWorkingDirectory string, force bool, ignoreTasks ...string) error { | ||
|
||
var finalError *multierror.Error | ||
|
||
if !force { | ||
status, err := deployments.GetDeploymentStatus(ctx, deploymentID) | ||
if err != nil { | ||
finalError = multierror.Append(finalError, err) | ||
return finalError | ||
} | ||
if status != deployments.UNDEPLOYED { | ||
finalError = multierror.Append(finalError, errors.Errorf("can't purge a deployment not in %q state, actual status is %q", deployments.UNDEPLOYED, status)) | ||
return finalError | ||
} | ||
} | ||
|
||
// Set status to PURGE_IN_PROGRESS | ||
err := deployments.SetDeploymentStatus(ctx, deploymentID, deployments.PURGE_IN_PROGRESS) | ||
if err != nil { | ||
if !force { | ||
finalError = multierror.Append(finalError, err) | ||
return finalError | ||
} | ||
// In force mode this error could be ignored | ||
} | ||
|
||
kv := consulutil.GetKV() | ||
// Remove from KV all tasks from the current target deployment, except this purge task | ||
tasksList, err := deployments.GetDeploymentTaskList(ctx, deploymentID) | ||
if err != nil { | ||
finalError = multierror.Append(finalError, err) | ||
if !force { | ||
deployments.SetDeploymentStatus(ctx, deploymentID, deployments.PURGE_FAILED) | ||
return finalError | ||
} | ||
} | ||
for _, tid := range tasksList { | ||
|
||
if !collections.ContainsString(ignoreTasks, tid) { | ||
err = tasks.DeleteTask(tid) | ||
if err != nil { | ||
finalError = multierror.Append(finalError, err) | ||
if !force { | ||
deployments.SetDeploymentStatus(ctx, deploymentID, deployments.PURGE_FAILED) | ||
return finalError | ||
} | ||
} | ||
} | ||
_, err = kv.DeleteTree(path.Join(consulutil.WorkflowsPrefix, tid)+"/", nil) | ||
if err != nil { | ||
finalError = multierror.Append(finalError, err) | ||
if !force { | ||
deployments.SetDeploymentStatus(ctx, deploymentID, deployments.PURGE_FAILED) | ||
return finalError | ||
} | ||
} | ||
} | ||
// Delete events tree corresponding to the deployment TaskExecution | ||
err = events.PurgeDeploymentEvents(ctx, deploymentID) | ||
if err != nil { | ||
finalError = multierror.Append(finalError, err) | ||
if !force { | ||
deployments.SetDeploymentStatus(ctx, deploymentID, deployments.PURGE_FAILED) | ||
return finalError | ||
} | ||
} | ||
// Delete logs tree corresponding to the deployment | ||
err = events.PurgeDeploymentLogs(ctx, deploymentID) | ||
if err != nil { | ||
finalError = multierror.Append(finalError, err) | ||
if !force { | ||
deployments.SetDeploymentStatus(ctx, deploymentID, deployments.PURGE_FAILED) | ||
return finalError | ||
} | ||
} | ||
// Remove the working directory of the current target deployment | ||
overlayPath := filepath.Join(filepathWorkingDirectory, "deployments", deploymentID) | ||
err = os.RemoveAll(overlayPath) | ||
if err != nil { | ||
err = errors.Wrapf(err, "failed to remove deployments artifacts stored on disk: %q", overlayPath) | ||
finalError = multierror.Append(finalError, err) | ||
if !force { | ||
deployments.SetDeploymentStatus(ctx, deploymentID, deployments.PURGE_FAILED) | ||
return finalError | ||
} | ||
} | ||
|
||
err = deployments.DeleteDeployment(ctx, deploymentID) | ||
if err != nil { | ||
finalError = multierror.Append(finalError, err) | ||
if !force { | ||
deployments.SetDeploymentStatus(ctx, deploymentID, deployments.PURGE_FAILED) | ||
return finalError | ||
} | ||
} | ||
|
||
return finalError.ErrorOrNil() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.