diff --git a/Makefile b/Makefile index a69e331f0c..bf510d120e 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ gen-mocks: ## Generate mocks mockgen -source=internal/store/continuous_snapshots.go -destination=internal/mocks/mock_continuous_snapshots.go -package=mocks mockgen -source=internal/store/continuous_jobs.go -destination=internal/mocks/mock_continuous_jobs.go -package=mocks mockgen -source=internal/store/agents.go -destination=internal/mocks/mock_agents.go -package=mocks + mockgen -source=internal/store/checkpoints.go -destination=internal/mocks/mock_checkpoints.go -package=mocks .PHONY: build build: ## Generate a binary in ./bin diff --git a/go.mod b/go.mod index 81b0a02921..9acf684642 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.12 // indirect github.com/mitchellh/go-homedir v1.1.0 - github.com/mongodb/go-client-mongodb-atlas v0.1.4-0.20200318094203-b65adad6f142 - github.com/mongodb/go-client-mongodb-ops-manager v0.0.2-0.20200318095624-136324ab144a + github.com/mongodb/go-client-mongodb-atlas v0.1.4-0.20200318163834-8b6a5ea6eb67 + github.com/mongodb/go-client-mongodb-ops-manager v0.0.2-0.20200319100114-648fc659365b github.com/pelletier/go-toml v1.6.0 // indirect github.com/spf13/afero v1.2.2 github.com/spf13/cast v1.3.1 // indirect diff --git a/go.sum b/go.sum index 3dee713378..9d329346c4 100644 --- a/go.sum +++ b/go.sum @@ -100,11 +100,10 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mongodb/go-client-mongodb-atlas v0.1.4-0.20200318090919-47fd90f26eb1/go.mod h1:LS8O0YLkA+sbtOb3fZLF10yY3tJM+1xATXMJ3oU35LU= -github.com/mongodb/go-client-mongodb-atlas v0.1.4-0.20200318094203-b65adad6f142 h1:A8gFrXWfOPWgDkv4/J7uxVY6ER/NFETb3TZMmX83vdg= -github.com/mongodb/go-client-mongodb-atlas v0.1.4-0.20200318094203-b65adad6f142/go.mod h1:LS8O0YLkA+sbtOb3fZLF10yY3tJM+1xATXMJ3oU35LU= -github.com/mongodb/go-client-mongodb-ops-manager v0.0.2-0.20200318095624-136324ab144a h1:AKMyjBEQgzgI8u9sszRW0qt04ajrhLCC6YRJFstT41s= -github.com/mongodb/go-client-mongodb-ops-manager v0.0.2-0.20200318095624-136324ab144a/go.mod h1:KnlAMq1ZY06LOYW1QUxCWiDLrj71c52K2Z5zGlxeS4k= +github.com/mongodb/go-client-mongodb-atlas v0.1.4-0.20200318163834-8b6a5ea6eb67 h1:YKz/n9K99mUOxs/pX+vpzNvbSFr56vObu2+O33upiFo= +github.com/mongodb/go-client-mongodb-atlas v0.1.4-0.20200318163834-8b6a5ea6eb67/go.mod h1:LS8O0YLkA+sbtOb3fZLF10yY3tJM+1xATXMJ3oU35LU= +github.com/mongodb/go-client-mongodb-ops-manager v0.0.2-0.20200319100114-648fc659365b h1:ROpURq2j0CnObttgt3cm+2veOzoNwUPWWK/hoHZGBQA= +github.com/mongodb/go-client-mongodb-ops-manager v0.0.2-0.20200319100114-648fc659365b/go.mod h1:FhMID1fXaHZpvHPfUhcDL3pfc5JrBB+aD42HLgFoO+8= github.com/mwielbut/pointy v1.1.0 h1:U5/YEfoIkaGCHv0St3CgjduqXID4FNRoyZgLM1kY9vg= github.com/mwielbut/pointy v1.1.0/go.mod h1:MvvO+uMFj9T5DMda33HlvogsFBX7pWWKAkFIn4teYwY= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= diff --git a/internal/cli/atlas_backups.go b/internal/cli/atlas_backups.go index 36da85b7d0..446a74b624 100644 --- a/internal/cli/atlas_backups.go +++ b/internal/cli/atlas_backups.go @@ -27,6 +27,7 @@ func AtlasBackupsBuilder() *cobra.Command { cmd.AddCommand(AtlasBackupsSnapshotsBuilder()) cmd.AddCommand(AtlasBackupsRestoresBuilder()) + cmd.AddCommand(AtlasBackupsCheckpointsBuilder()) return cmd } diff --git a/internal/cli/atlas_backups_checkpoints.go b/internal/cli/atlas_backups_checkpoints.go new file mode 100644 index 0000000000..068e144314 --- /dev/null +++ b/internal/cli/atlas_backups_checkpoints.go @@ -0,0 +1,31 @@ +// Copyright 2020 MongoDB Inc +// +// 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 cli + +import ( + "github.com/spf13/cobra" +) + +func AtlasBackupsCheckpointsBuilder() *cobra.Command { + cmd := &cobra.Command{ + Use: "checkpoints", + Aliases: []string{"checkpoint"}, + Short: "Manage backup checkpoints for your project.", + } + + cmd.AddCommand(AtlasBackupsCheckpointsListBuilder()) + + return cmd +} diff --git a/internal/cli/atlas_backups_checkpoints_list.go b/internal/cli/atlas_backups_checkpoints_list.go new file mode 100644 index 0000000000..62ee560876 --- /dev/null +++ b/internal/cli/atlas_backups_checkpoints_list.go @@ -0,0 +1,88 @@ +// Copyright 2020 MongoDB Inc +// +// 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 cli + +import ( + atlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas" + "github.com/mongodb/mongocli/internal/flags" + "github.com/mongodb/mongocli/internal/json" + "github.com/mongodb/mongocli/internal/store" + "github.com/mongodb/mongocli/internal/usage" + "github.com/spf13/cobra" +) + +type atlasBackupsCheckpointsListOpts struct { + *globalOpts + clusterName string + pageNum int + itemsPerPage int + store store.CheckpointsLister +} + +func (opts *atlasBackupsCheckpointsListOpts) init() error { + if opts.ProjectID() == "" { + return errMissingProjectID + } + + var err error + opts.store, err = store.New() + return err +} + +func (opts *atlasBackupsCheckpointsListOpts) Run() error { + listOpts := opts.newListOptions() + + result, err := opts.store.List(opts.ProjectID(), opts.clusterName, listOpts) + + if err != nil { + return err + } + + return json.PrettyPrint(result) +} + +func (opts *atlasBackupsCheckpointsListOpts) newListOptions() *atlas.ListOptions { + return &atlas.ListOptions{ + PageNum: opts.pageNum, + ItemsPerPage: opts.itemsPerPage, + } +} + +// mongocli atlas backup(s) checkpoint(s) list clusterName [--projectId projectId] +func AtlasBackupsCheckpointsListBuilder() *cobra.Command { + opts := &atlasBackupsCheckpointsListOpts{ + globalOpts: newGlobalOpts(), + } + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List continuous backup checkpoints.", + Args: cobra.ExactArgs(1), + PreRunE: func(cmd *cobra.Command, args []string) error { + return opts.init() + }, + RunE: func(cmd *cobra.Command, args []string) error { + opts.clusterName = args[0] + return opts.Run() + }, + } + + cmd.Flags().IntVar(&opts.pageNum, flags.Page, 0, usage.Page) + cmd.Flags().IntVar(&opts.itemsPerPage, flags.Limit, 0, usage.Limit) + + cmd.Flags().StringVar(&opts.projectID, flags.ProjectID, "", usage.ProjectID) + + return cmd +} diff --git a/internal/cli/atlas_backups_checkpoints_list_test.go b/internal/cli/atlas_backups_checkpoints_list_test.go new file mode 100644 index 0000000000..286cbad46c --- /dev/null +++ b/internal/cli/atlas_backups_checkpoints_list_test.go @@ -0,0 +1,49 @@ +// Copyright 2020 MongoDB Inc +// +// 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 cli + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/mongodb/mongocli/internal/fixtures" + "github.com/mongodb/mongocli/internal/mocks" +) + +func TestAtlasBackupsCheckpointsList_Run(t *testing.T) { + ctrl := gomock.NewController(t) + mockStore := mocks.NewMockCheckpointsLister(ctrl) + + defer ctrl.Finish() + + expected := fixtures.Checkpoints() + + listOpts := &atlasBackupsCheckpointsListOpts{ + globalOpts: newGlobalOpts(), + store: mockStore, + clusterName: "Cluster0", + } + + mockStore. + EXPECT(). + List(listOpts.projectID, "Cluster0", listOpts.newListOptions()). + Return(expected, nil). + Times(1) + + err := listOpts.Run() + if err != nil { + t.Fatalf("Run() unexpected error: %v", err) + } +} diff --git a/internal/fixtures/checkpoints.go b/internal/fixtures/checkpoints.go new file mode 100644 index 0000000000..78d0417880 --- /dev/null +++ b/internal/fixtures/checkpoints.go @@ -0,0 +1,65 @@ +package fixtures + +import atlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas" + +func Checkpoint() *atlas.Checkpoint { + return &atlas.Checkpoint{ + ClusterID: "6b8cd61180eef547110159d9", + Completed: "2018-02-08T23:20:25Z", + GroupID: "6b8cd3c380eef5349ef77gf7", + ID: "5a7cdb3980eef53de5bffdcf", + Links: []*atlas.Link{ + { + Rel: "self", + Href: "https://cloud.mongodb.com/api/public/v1.0/groups/6b8cd3c380eef5349ef77gf7/clusters/Cluster0/checkpoints", + }, + }, + Parts: []*atlas.Part{ + + { + ReplicaSetName: "Cluster0-shard-1", + TypeName: "REPLICA_SET", + CheckpointPart: atlas.CheckpointPart{ + ShardName: "Cluster0-shard-1", + TokenDiscovered: true, + TokenTimestamp: atlas.SnapshotTimestamp{ + Date: "2018-02-08T23:20:25Z", + Increment: 1, + }, + }, + }, + { + ReplicaSetName: "Cluster0-shard-0", + TypeName: "REPLICA_SET", + CheckpointPart: atlas.CheckpointPart{ + ShardName: "Cluster0-shard-0", + TokenDiscovered: true, + TokenTimestamp: atlas.SnapshotTimestamp{ + Date: "2018-02-08T23:20:25Z", + Increment: 1, + }}, + }, + { + ReplicaSetName: "Cluster0-config-0", + TypeName: "CONFIG_SERVER_REPLICA_SET", + CheckpointPart: atlas.CheckpointPart{ + TokenDiscovered: true, + TokenTimestamp: atlas.SnapshotTimestamp{ + Date: "2018-02-08T23:20:25Z", + Increment: 2, + }}, + }, + }, + Restorable: true, + Started: "2018-02-08T23:20:25Z", + Timestamp: "2018-02-08T23:19:37Z", + } +} + +func Checkpoints() *atlas.Checkpoints { + return &atlas.Checkpoints{ + Results: []*atlas.Checkpoint{Checkpoint()}, + Links: nil, + TotalCount: 1, + } +} diff --git a/internal/mocks/mock_checkpoints.go b/internal/mocks/mock_checkpoints.go new file mode 100644 index 0000000000..480f47dea9 --- /dev/null +++ b/internal/mocks/mock_checkpoints.go @@ -0,0 +1,49 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: internal/store/checkpoints.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + gomock "github.com/golang/mock/gomock" + mongodbatlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas" + reflect "reflect" +) + +// MockCheckpointsLister is a mock of CheckpointsLister interface +type MockCheckpointsLister struct { + ctrl *gomock.Controller + recorder *MockCheckpointsListerMockRecorder +} + +// MockCheckpointsListerMockRecorder is the mock recorder for MockCheckpointsLister +type MockCheckpointsListerMockRecorder struct { + mock *MockCheckpointsLister +} + +// NewMockCheckpointsLister creates a new mock instance +func NewMockCheckpointsLister(ctrl *gomock.Controller) *MockCheckpointsLister { + mock := &MockCheckpointsLister{ctrl: ctrl} + mock.recorder = &MockCheckpointsListerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockCheckpointsLister) EXPECT() *MockCheckpointsListerMockRecorder { + return m.recorder +} + +// List mocks base method +func (m *MockCheckpointsLister) List(arg0, arg1 string, arg2 *mongodbatlas.ListOptions) (*mongodbatlas.Checkpoints, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0, arg1, arg2) + ret0, _ := ret[0].(*mongodbatlas.Checkpoints) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List +func (mr *MockCheckpointsListerMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockCheckpointsLister)(nil).List), arg0, arg1, arg2) +} diff --git a/internal/store/checkpoints.go b/internal/store/checkpoints.go new file mode 100644 index 0000000000..941151e69b --- /dev/null +++ b/internal/store/checkpoints.go @@ -0,0 +1,42 @@ +// Copyright 2020 MongoDB Inc +// +// 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 store + +import ( + "context" + "fmt" + + atlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas" + om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" + "github.com/mongodb/mongocli/internal/config" +) + +type CheckpointsLister interface { + List(string, string, *atlas.ListOptions) (*atlas.Checkpoints, error) +} + +// List encapsulate the logic to manage different cloud providers +func (s *Store) List(projectID, clusterID string, opts *atlas.ListOptions) (*atlas.Checkpoints, error) { + switch s.service { + case config.CloudService: + result, _, err := s.client.(*atlas.Client).Checkpoints.List(context.Background(), projectID, clusterID, opts) + return result, err + case config.CloudManagerService, config.OpsManagerService: + result, _, err := s.client.(*om.Client).Checkpoints.List(context.Background(), projectID, clusterID, opts) + return result, err + default: + return nil, fmt.Errorf("unsupported service: %s", s.service) + } +}