-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1086 from aziontech/rollback-static-files
feat: add rollback command
- Loading branch information
Showing
7 changed files
with
291 additions
and
5 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package rollback | ||
|
||
import "errors" | ||
|
||
var ( | ||
ERRORROLLBACK = errors.New("Failed to roll back to previous static files") | ||
ERRORNEEDSDEPLOY = errors.New("You cannot use the rollback command unless you have already deployed this project. Please check if you are in the correct working directory") | ||
ERRORAZION = errors.New("Failed to open the azion.json file. The file doesn't exist, is corrupted, or has an invalid JSON format") | ||
) |
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,12 @@ | ||
package rollback | ||
|
||
const ( | ||
USAGE = "rollback" | ||
SHORTDESCRIPTION = "Sets static files from a previous deploy" | ||
LONGDESCRIPTION = "Sets static files from a previous deploy within the same bucket" | ||
FLAGHELP = "Displays more information about the rollback command" | ||
FLAGORIGINKEY = "Origin key of the origin used for storage of static files" | ||
CONFFLAG = "Relative path to where your custom azion.json and args.json files are stored" | ||
ASKORIGIN = "Enter the key of the Origin you wish to update:" | ||
SUCCESS = "Static files rolled back successfully" | ||
) |
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,141 @@ | ||
package rollback | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/MakeNowJust/heredoc" | ||
msg "github.com/aziontech/azion-cli/messages/rollback" | ||
apiOrigin "github.com/aziontech/azion-cli/pkg/api/origin" | ||
api "github.com/aziontech/azion-cli/pkg/api/storage" | ||
"github.com/aziontech/azion-cli/pkg/cmdutil" | ||
"github.com/aziontech/azion-cli/pkg/contracts" | ||
"github.com/aziontech/azion-cli/pkg/logger" | ||
"github.com/aziontech/azion-cli/pkg/output" | ||
"github.com/aziontech/azion-cli/utils" | ||
"github.com/spf13/cobra" | ||
"go.uber.org/zap" | ||
) | ||
|
||
var ( | ||
originKey string | ||
projectPath string | ||
) | ||
|
||
type RollbackCmd struct { | ||
AskInput func(string) (string, error) | ||
GetAzionJsonContent func(pathConf string) (*contracts.AzionApplicationOptions, error) | ||
WriteAzionJsonContent func(conf *contracts.AzionApplicationOptions, confPath string) error | ||
} | ||
|
||
func NewDeleteCmd(f *cmdutil.Factory) *RollbackCmd { | ||
return &RollbackCmd{ | ||
GetAzionJsonContent: utils.GetAzionJsonContent, | ||
WriteAzionJsonContent: utils.WriteAzionJsonContent, | ||
AskInput: utils.AskInput, | ||
} | ||
} | ||
|
||
func NewCobraCmd(rollback *RollbackCmd, f *cmdutil.Factory) *cobra.Command { | ||
cobraCmd := &cobra.Command{ | ||
Use: msg.USAGE, | ||
Short: msg.SHORTDESCRIPTION, | ||
Long: msg.LONGDESCRIPTION, | ||
SilenceUsage: true, | ||
SilenceErrors: true, | ||
Example: heredoc.Doc(` | ||
$ azion rollback --origin-key aaaa-bbbb-cccc-dddd | ||
`), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
|
||
if !cmd.Flags().Changed("origin-key") { | ||
answer, err := rollback.AskInput(msg.ASKORIGIN) | ||
if err != nil { | ||
return err | ||
} | ||
originKey = answer | ||
} | ||
|
||
conf, err := rollback.GetAzionJsonContent(projectPath) | ||
if err != nil { | ||
logger.Debug("Error while reading azion.json file", zap.Error(err)) | ||
return msg.ERRORAZION | ||
} | ||
|
||
if conf.Bucket == "" || conf.Prefix == "" { | ||
return msg.ERRORNEEDSDEPLOY | ||
} | ||
|
||
timestamp, err := checkForNewTimestamp(f, conf.Prefix, conf.Bucket) | ||
if err != nil { | ||
return msg.ERRORROLLBACK | ||
} | ||
|
||
clientOrigin := apiOrigin.NewClient(f.HttpClient, f.Config.GetString("api_url"), f.Config.GetString("token")) | ||
request := apiOrigin.UpdateRequest{} | ||
request.SetPrefix(timestamp) | ||
|
||
_, err = clientOrigin.Update(context.Background(), conf.Application.ID, originKey, &request) | ||
if err != nil { | ||
return msg.ERRORROLLBACK | ||
} | ||
|
||
conf.Prefix = timestamp | ||
err = rollback.WriteAzionJsonContent(conf, projectPath) | ||
if err != nil { | ||
return msg.ERRORROLLBACK | ||
} | ||
|
||
rollbackOut := output.GeneralOutput{ | ||
Msg: msg.SUCCESS, | ||
Out: f.IOStreams.Out, | ||
Flags: f.Flags, | ||
} | ||
return output.Print(&rollbackOut) | ||
}, | ||
} | ||
|
||
cobraCmd.Flags().StringVar(&originKey, "origin-key", "", msg.FLAGORIGINKEY) | ||
cobraCmd.Flags().StringVar(&projectPath, "config-dir", "azion", msg.CONFFLAG) | ||
cobraCmd.Flags().BoolP("help", "h", false, msg.FLAGHELP) | ||
|
||
return cobraCmd | ||
} | ||
|
||
func NewCmd(f *cmdutil.Factory) *cobra.Command { | ||
return NewCobraCmd(NewDeleteCmd(f), f) | ||
} | ||
|
||
func checkForNewTimestamp(f *cmdutil.Factory, referenceTimestamp, bucketName string) (string, error) { | ||
logger.Debug("Checking if there are previous static files for the following bucket", zap.Any("Bucket name", bucketName)) | ||
client := api.NewClient(f.HttpClient, f.Config.GetString("storage_url"), f.Config.GetString("token")) | ||
c := context.Background() | ||
options := &contracts.ListOptions{ | ||
// Sort: "desc", | ||
// OrderBy: "last_modified", | ||
PageSize: 100000, | ||
} | ||
|
||
resp, err := client.ListObject(c, bucketName, options) | ||
if err != nil { | ||
fmt.Println(err.Error()) | ||
return "", err | ||
} | ||
|
||
var prevTimestamp string | ||
for _, object := range resp.Results { | ||
parts := strings.Split(object.Key, "/") | ||
if len(parts) > 1 { | ||
timestamp := parts[0] | ||
if timestamp == referenceTimestamp { | ||
return prevTimestamp, nil | ||
} else { | ||
prevTimestamp = timestamp | ||
continue | ||
} | ||
} | ||
} | ||
|
||
return referenceTimestamp, 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package rollback | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/aziontech/azion-cli/pkg/contracts" | ||
"github.com/aziontech/azion-cli/pkg/logger" | ||
"github.com/aziontech/azion-cli/utils" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/zap/zapcore" | ||
|
||
"github.com/aziontech/azion-cli/pkg/httpmock" | ||
"github.com/aziontech/azion-cli/pkg/testutils" | ||
) | ||
|
||
func mockInvalidOriginKey(msg string) (string, error) { | ||
return "invalid", nil | ||
} | ||
|
||
func mockParseError(msg string) (string, error) { | ||
return "invalid", utils.ErrorParseResponse | ||
} | ||
|
||
func TestRollbackWithAskInput(t *testing.T) { | ||
logger.New(zapcore.DebugLevel) | ||
|
||
tests := []struct { | ||
name string | ||
originKey string | ||
method string | ||
endpoint string | ||
statusCode int | ||
responseBody string | ||
expectedOutput string | ||
expectError bool | ||
mockInputs func(string) (string, error) | ||
mockError error | ||
}{ | ||
{ | ||
name: "rollback with invalid origin key", | ||
originKey: "invalid", | ||
method: "UPDATE", | ||
endpoint: "origins/invalid", | ||
statusCode: 400, | ||
responseBody: "Bad Request", | ||
expectedOutput: "", | ||
expectError: true, | ||
mockInputs: mockInvalidOriginKey, | ||
mockError: fmt.Errorf("Failed to parse your response. Check your response and try again. If the error persists, contact Azion support"), | ||
}, | ||
{ | ||
name: "error in input", | ||
originKey: "invalid", | ||
method: "UPDATE", | ||
endpoint: "origins/invalid", | ||
statusCode: 400, | ||
responseBody: "Bad Request", | ||
expectedOutput: "", | ||
expectError: true, | ||
mockInputs: mockInvalidOriginKey, | ||
mockError: fmt.Errorf("invalid argument \"\" for \"--origin-key\" flag: invalid syntax"), | ||
}, | ||
{ | ||
name: "error - parse answer", | ||
originKey: "", | ||
method: "", | ||
endpoint: "", | ||
statusCode: 0, | ||
responseBody: "", | ||
expectedOutput: "", | ||
expectError: true, | ||
mockInputs: mockParseError, | ||
mockError: utils.ErrorParseResponse, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
mock := &httpmock.Registry{} | ||
mock.Register( | ||
httpmock.REST(tt.method, tt.endpoint), | ||
httpmock.StatusStringResponse(tt.statusCode, tt.responseBody), | ||
) | ||
|
||
f, stdout, _ := testutils.NewFactory(mock) | ||
|
||
rollbackCmd := NewDeleteCmd(f) | ||
rollbackCmd.AskInput = tt.mockInputs | ||
rollbackCmd.GetAzionJsonContent = func(pathConf string) (*contracts.AzionApplicationOptions, error) { | ||
return &contracts.AzionApplicationOptions{ | ||
Application: contracts.AzionJsonDataApplication{ | ||
ID: 0001110001, | ||
Name: "namezin", | ||
}, | ||
Bucket: "nomedobucket", | ||
Prefix: "001001001", | ||
}, nil | ||
} | ||
rollbackCmd.WriteAzionJsonContent = func(conf *contracts.AzionApplicationOptions, confPath string) error { | ||
return nil | ||
} | ||
cobraCmd := NewCobraCmd(rollbackCmd, f) | ||
|
||
if tt.originKey != "" { | ||
cobraCmd.SetArgs([]string{"--origin-key", tt.originKey}) | ||
} | ||
|
||
_, err := cobraCmd.ExecuteC() | ||
if tt.expectError { | ||
require.Error(t, err) | ||
} else { | ||
require.NoError(t, err) | ||
assert.Equal(t, tt.expectedOutput, stdout.String()) | ||
} | ||
}) | ||
} | ||
} |
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