Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion docs/command/atlas-backups-restores-start.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Start a restore job for your project and cluster.
If you create an automated or pointInTime restore job, Atlas removes all existing data on the target cluster prior to the restore.

To use this command, you must authenticate with a user account or an API key with the Project Owner role.
Atlas supports this command only for M10+ clusters.
Atlas supports this command only for Flex and M10+ clusters.
Flex clusters support only automated restore jobs.

Syntax
------
Expand Down Expand Up @@ -137,6 +138,17 @@ Examples
--targetProjectId 1a2345b67c8e9a12f3456de7


.. code-block::
:copyable: false

# Create an automated restore for a Flex Cluster:
atlas backup restore start automated \
--clusterName myFlexSource \
--snapshotId 5e7e00128f8ce03996a47179 \
--targetClusterName myFlexCluster \
--targetProjectId 1a2345b67c8e9a12f3456de7


.. code-block::
:copyable: false

Expand Down
38 changes: 35 additions & 3 deletions internal/cli/backup/restores/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,37 @@ func (opts *StartOpts) initStore(ctx context.Context) func() error {
var startTemplate = "Restore job '{{.Id}}' successfully started\n"

func (opts *StartOpts) Run() error {
r, err := opts.store.CreateRestoreFlexClusterJobs(opts.ConfigProjectID(), opts.clusterName, opts.newFlexBackupRestoreJobCreate())
if err == nil {
return opts.Print(r)
}

apiError, ok := admin.AsError(err)
if !ok {
return commonerrors.Check(err)
}

if apiError.ErrorCode != cannotUseNotFlexWithFlexApisErrorCode {
return commonerrors.Check(err)
}

request := opts.newCloudProviderSnapshotRestoreJob()
r, err := opts.store.CreateRestoreJobs(opts.ConfigProjectID(), opts.clusterName, request)
restoreJob, err := opts.store.CreateRestoreJobs(opts.ConfigProjectID(), opts.clusterName, request)

if err != nil {
return commonerrors.Check(err)
}

return opts.Print(r)
return opts.Print(restoreJob)
}

func (opts *StartOpts) newFlexBackupRestoreJobCreate() *admin.FlexBackupRestoreJobCreate20241113 {
return &admin.FlexBackupRestoreJobCreate20241113{
SnapshotId: opts.snapshotID,
TargetDeploymentItemName: opts.targetClusterName,
TargetProjectId: &opts.targetProjectID,
InstanceName: &opts.clusterName,
}
}

func (opts *StartOpts) newCloudProviderSnapshotRestoreJob() *admin.DiskBackupSnapshotRestoreJob {
Expand Down Expand Up @@ -134,6 +157,7 @@ func markRequiredPointInTimeRestoreFlags(cmd *cobra.Command) error {
return cmd.MarkFlagRequired(flag.TargetClusterName)
}

// StartBuilder builds a cobra.Command that can run as:
// atlas backup(s) restore(s) job(s) start <automated|download|pointInTime>.
func StartBuilder() *cobra.Command {
opts := new(StartOpts)
Expand All @@ -142,7 +166,8 @@ func StartBuilder() *cobra.Command {
Short: "Start a restore job for your project and cluster.",
Long: `If you create an automated or pointInTime restore job, Atlas removes all existing data on the target cluster prior to the restore.

` + fmt.Sprintf("%s\n%s", fmt.Sprintf(usage.RequiredRole, "Project Owner"), "Atlas supports this command only for M10+ clusters."),
` + fmt.Sprintf("%s\n%s\n%s", fmt.Sprintf(usage.RequiredRole, "Project Owner"), "Atlas supports this command only for Flex and M10+ clusters.",
"Flex clusters support only automated restore jobs."),
Args: require.ExactValidArgs(1),
ValidArgs: []string{automatedRestore, downloadRestore, pointInTimeRestore},
Annotations: map[string]string{
Expand All @@ -156,6 +181,13 @@ func StartBuilder() *cobra.Command {
--targetClusterName myDemo2 \
--targetProjectId 1a2345b67c8e9a12f3456de7

# Create an automated restore for a Flex Cluster:
atlas backup restore start automated \
--clusterName myFlexSource \
--snapshotId 5e7e00128f8ce03996a47179 \
--targetClusterName myFlexCluster \
--targetProjectId 1a2345b67c8e9a12f3456de7

# Create a point-in-time restore:
atlas backup restore start pointInTime \
--clusterName myDemo \
Expand Down
60 changes: 52 additions & 8 deletions internal/cli/backup/restores/start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/golang/mock/gomock"
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/mocks"
"github.com/stretchr/testify/require"
atlasv2 "go.mongodb.org/atlas-sdk/v20241113004/admin"
)

Expand All @@ -39,15 +40,44 @@ func TestStart_Run(t *testing.T) {
targetProjectID: "1",
}

expectedError := &atlasv2.GenericOpenAPIError{}
expectedError.SetModel(atlasv2.ApiError{ErrorCode: cannotUseNotFlexWithFlexApisErrorCode})

mockStore.
EXPECT().
CreateRestoreFlexClusterJobs(listOpts.ProjectID, "Cluster0", listOpts.newFlexBackupRestoreJobCreate()).
Return(nil, expectedError).
Times(1)

mockStore.
EXPECT().
CreateRestoreJobs(listOpts.ProjectID, "Cluster0", listOpts.newCloudProviderSnapshotRestoreJob()).
Return(expected, nil).
Times(1)

if err := listOpts.Run(); err != nil {
t.Fatalf("Run() unexpected error: %v", err)
require.NoError(t, listOpts.Run())
})

t.Run("Flex Cluster automated restore job", func(t *testing.T) {
listOpts := &StartOpts{
store: mockStore,
method: automatedRestore,
clusterName: "Cluster0",
targetClusterName: "Cluster1",
targetProjectID: "1",
}

expectedFlex := &atlasv2.FlexBackupRestoreJob20241113{}
expectedError := &atlasv2.GenericOpenAPIError{}
expectedError.SetModel(atlasv2.ApiError{ErrorCode: cannotUseNotFlexWithFlexApisErrorCode})

mockStore.
EXPECT().
CreateRestoreFlexClusterJobs(listOpts.ProjectID, "Cluster0", listOpts.newFlexBackupRestoreJobCreate()).
Return(expectedFlex, nil).
Times(1)

require.NoError(t, listOpts.Run())
})

t.Run(pointInTimeRestore, func(t *testing.T) {
Expand All @@ -59,15 +89,22 @@ func TestStart_Run(t *testing.T) {
targetProjectID: "1",
}

expectedError := &atlasv2.GenericOpenAPIError{}
expectedError.SetModel(atlasv2.ApiError{ErrorCode: cannotUseNotFlexWithFlexApisErrorCode})

mockStore.
EXPECT().
CreateRestoreFlexClusterJobs(listOpts.ProjectID, "Cluster0", listOpts.newFlexBackupRestoreJobCreate()).
Return(nil, expectedError).
Times(1)

mockStore.
EXPECT().
CreateRestoreJobs(listOpts.ProjectID, "Cluster0", listOpts.newCloudProviderSnapshotRestoreJob()).
Return(expected, nil).
Times(1)

if err := listOpts.Run(); err != nil {
t.Fatalf("Run() unexpected error: %v", err)
}
require.NoError(t, listOpts.Run())
})

t.Run(downloadRestore, func(t *testing.T) {
Expand All @@ -77,14 +114,21 @@ func TestStart_Run(t *testing.T) {
clusterName: "Cluster0",
}

expectedError := &atlasv2.GenericOpenAPIError{}
expectedError.SetModel(atlasv2.ApiError{ErrorCode: cannotUseNotFlexWithFlexApisErrorCode})

mockStore.
EXPECT().
CreateRestoreFlexClusterJobs(listOpts.ProjectID, "Cluster0", listOpts.newFlexBackupRestoreJobCreate()).
Return(nil, expectedError).
Times(1)

mockStore.
EXPECT().
CreateRestoreJobs(listOpts.ProjectID, "Cluster0", listOpts.newCloudProviderSnapshotRestoreJob()).
Return(expected, nil).
Times(1)

if err := listOpts.Run(); err != nil {
t.Fatalf("Run() unexpected error: %v", err)
}
require.NoError(t, listOpts.Run())
})
}
Loading