From b1d8819052c7e29e5e02f83e0abef67a5d4cad68 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 10:53:18 +0100 Subject: [PATCH 01/15] CLOUDP-60954 mongocli om logs collect --- internal/cli/cloud_manager.go | 1 + internal/cli/ops_manager.go | 1 + internal/cli/ops_manager_logs.go | 31 +++++++ internal/cli/ops_manager_logs_collect.go | 84 +++++++++++++++++++ internal/cli/ops_manager_logs_collect_test.go | 49 +++++++++++ internal/description/description.go | 2 + internal/flags/flags.go | 5 ++ internal/mocks/mock_logs.go | 39 +++++++++ internal/store/logs.go | 15 ++++ internal/usage/usage.go | 5 ++ 10 files changed, 232 insertions(+) create mode 100644 internal/cli/ops_manager_logs.go create mode 100644 internal/cli/ops_manager_logs_collect.go create mode 100644 internal/cli/ops_manager_logs_collect_test.go diff --git a/internal/cli/cloud_manager.go b/internal/cli/cloud_manager.go index fa9a531057..6a8eadc457 100644 --- a/internal/cli/cloud_manager.go +++ b/internal/cli/cloud_manager.go @@ -36,6 +36,7 @@ func CloudManagerBuilder() *cobra.Command { cmd.AddCommand(AtlasEventsBuilder()) cmd.AddCommand(OpsManagerProcessesBuilder()) cmd.AddCommand(OpsManagerMeasurementsBuilder()) + cmd.AddCommand(OpsManagerLogsBuilder()) return cmd } diff --git a/internal/cli/ops_manager.go b/internal/cli/ops_manager.go index 505d77352b..e477f23da1 100644 --- a/internal/cli/ops_manager.go +++ b/internal/cli/ops_manager.go @@ -37,6 +37,7 @@ func OpsManagerBuilder() *cobra.Command { cmd.AddCommand(AtlasEventsBuilder()) cmd.AddCommand(OpsManagerProcessesBuilder()) cmd.AddCommand(OpsManagerMeasurementsBuilder()) + cmd.AddCommand(OpsManagerLogsBuilder()) return cmd } diff --git a/internal/cli/ops_manager_logs.go b/internal/cli/ops_manager_logs.go new file mode 100644 index 0000000000..fecc772631 --- /dev/null +++ b/internal/cli/ops_manager_logs.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/mongodb/mongocli/internal/description" + "github.com/spf13/cobra" +) + +func OpsManagerLogsBuilder() *cobra.Command { + cmd := &cobra.Command{ + Use: "logs", + Aliases: []string{"log"}, + Short: description.OMLogs, + } + cmd.AddCommand(opsManagerLogsCollectOptsBuilder()) + + return cmd +} diff --git a/internal/cli/ops_manager_logs_collect.go b/internal/cli/ops_manager_logs_collect.go new file mode 100644 index 0000000000..052bc51aa3 --- /dev/null +++ b/internal/cli/ops_manager_logs_collect.go @@ -0,0 +1,84 @@ +// 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 ( + om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" + "github.com/mongodb/mongocli/internal/description" + "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 opsManagerLogsCollectOpts struct { + globalOpts + resourceType string + resourceName string + logTypes []string + sizeRequestedPerFileBytes int64 + redacted bool + store store.Logs +} + +func (opts *opsManagerLogsCollectOpts) initStore() error { + var err error + opts.store, err = store.New() + return err +} + +func (opts *opsManagerLogsCollectOpts) Run() error { + result, err := opts.store.Collect(opts.ProjectID(), opts.newLog()) + if err != nil { + return err + } + return json.PrettyPrint(result) +} + +func (opts *opsManagerLogsCollectOpts) newLog() *om.LogCollectionJob { + return &om.LogCollectionJob{ + ResourceType: opts.resourceType, + ResourceName: opts.resourceName, + Redacted: &opts.redacted, + SizeRequestedPerFileBytes: opts.sizeRequestedPerFileBytes, + LogTypes: opts.logTypes, + } +} + +// mongocli om logs collect --resourceType type --resourceName resource --sizeRequestedPerFileBytes size --logTypes type --redacted redacted [--projectId projectId] +func opsManagerLogsCollectOptsBuilder() *cobra.Command { + opts := &opsManagerLogsCollectOpts{} + cmd := &cobra.Command{ + Use: "collect", + Short: description.CollectOMLogs, + PreRunE: func(cmd *cobra.Command, args []string) error { + return opts.PreRunE(opts.initStore) + }, + RunE: func(cmd *cobra.Command, args []string) error { + return opts.Run() + }, + } + + cmd.Flags().StringVar(&opts.resourceName, flags.ResourceName, "", usage.ResourceName) + cmd.Flags().StringVar(&opts.resourceType, flags.ResourceType, "", usage.ResourceType) + cmd.Flags().StringArrayVar(&opts.logTypes, flags.LogTypes, nil, usage.LogTypes) + cmd.Flags().Int64Var(&opts.sizeRequestedPerFileBytes, flags.SizeRequestedPerFileBytes, 0, usage.SizeRequestedPerFileBytes) + cmd.Flags().BoolVar(&opts.redacted, flags.Redacted, false, usage.Redacted) + + cmd.Flags().StringVar(&opts.projectID, flags.ProjectID, "", usage.ProjectID) + + return cmd +} diff --git a/internal/cli/ops_manager_logs_collect_test.go b/internal/cli/ops_manager_logs_collect_test.go new file mode 100644 index 0000000000..46aab2dc42 --- /dev/null +++ b/internal/cli/ops_manager_logs_collect_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" + om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" + "github.com/mongodb/mongocli/internal/mocks" +) + +func TestOpsManagerLogsCollectOpts_Run(t *testing.T) { + ctrl := gomock.NewController(t) + mockStore := mocks.NewMockLogs(ctrl) + + defer ctrl.Finish() + + expected := &om.LogCollectionJob{ID: "1"} + + listOpts := &opsManagerLogsCollectOpts{ + redacted: false, + sizeRequestedPerFileBytes: 64, + resourceType: "CLUSTER", + resourceName: "", + logTypes: []string{"AUTOMATION_AGENT"}, + store: mockStore, + } + + mockStore. + EXPECT().Collect(listOpts.projectID, listOpts.newLog()). + Return(expected, nil). + Times(1) + + if err := listOpts.Run(); err != nil { + t.Fatalf("Run() unexpected error: %v", err) + } +} diff --git a/internal/description/description.go b/internal/description/description.go index abeed0b3ca..ff8be83756 100644 --- a/internal/description/description.go +++ b/internal/description/description.go @@ -91,6 +91,8 @@ A user’s roles apply to all the clusters in the project.` Automation = "Manage Ops Manager automation config." ShowAutomationStatus = "Show the current status of the automation config." Global = "Manage Ops Manager global properties." + OMLogs = "Manage Ops Manager log collection job." + CollectOMLogs = "Start a job to collect logs" Owner = "Manage Ops Manager owners." CreateOwner = "Create the first user for Ops Manager." Servers = "Manage Ops Manager servers." diff --git a/internal/flags/flags.go b/internal/flags/flags.go index fb0cdc1d88..985baf666b 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -115,4 +115,9 @@ const ( Normalization = "normalization" // Normalization flag Backwards = "backwards" // Backwards flag Strength = "strength" // Strength flag + ResourceName = "resourceName" // ResourceName flag + ResourceType = "resourceType" // ResourceType flag + LogTypes = "logTypes" // LogTypes flag + SizeRequestedPerFileBytes = "sizeRequestedPerFileBytes" //SizeRequestedPerFileBytes flag + Redacted = "redacted" // Redacted flag ) diff --git a/internal/mocks/mock_logs.go b/internal/mocks/mock_logs.go index b5b665fefa..c61776840d 100644 --- a/internal/mocks/mock_logs.go +++ b/internal/mocks/mock_logs.go @@ -7,6 +7,7 @@ package mocks import ( gomock "github.com/golang/mock/gomock" mongodbatlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas" + opsmngr "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" io "io" reflect "reflect" ) @@ -47,3 +48,41 @@ func (mr *MockLogsDownloaderMockRecorder) DownloadLog(arg0, arg1, arg2, arg3, ar mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DownloadLog", reflect.TypeOf((*MockLogsDownloader)(nil).DownloadLog), arg0, arg1, arg2, arg3, arg4) } + +// MockLogs is a mock of Logs interface +type MockLogs struct { + ctrl *gomock.Controller + recorder *MockLogsMockRecorder +} + +// MockLogsMockRecorder is the mock recorder for MockLogs +type MockLogsMockRecorder struct { + mock *MockLogs +} + +// NewMockLogs creates a new mock instance +func NewMockLogs(ctrl *gomock.Controller) *MockLogs { + mock := &MockLogs{ctrl: ctrl} + mock.recorder = &MockLogsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockLogs) EXPECT() *MockLogsMockRecorder { + return m.recorder +} + +// Collect mocks base method +func (m *MockLogs) Collect(arg0 string, arg1 *opsmngr.LogCollectionJob) (*opsmngr.LogCollectionJob, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Collect", arg0, arg1) + ret0, _ := ret[0].(*opsmngr.LogCollectionJob) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Collect indicates an expected call of Collect +func (mr *MockLogsMockRecorder) Collect(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collect", reflect.TypeOf((*MockLogs)(nil).Collect), arg0, arg1) +} diff --git a/internal/store/logs.go b/internal/store/logs.go index 6a52c33bd9..1f8e8df444 100644 --- a/internal/store/logs.go +++ b/internal/store/logs.go @@ -20,6 +20,7 @@ import ( "io" 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" ) @@ -27,6 +28,20 @@ type LogsDownloader interface { DownloadLog(string, string, string, io.Writer, *atlas.DateRangetOptions) error } +type Logs interface { + Collect(string, *om.LogCollectionJob) (*om.LogCollectionJob, error) +} + +func (s *Store) Collect(groupID string, newLog *om.LogCollectionJob) (*om.LogCollectionJob, error) { + switch s.service { + case config.OpsManagerService, config.CloudManagerService: + log, _, err := s.client.(*om.Client).LogCollections.Create(context.Background(), groupID, newLog) + return log, err + default: + return nil, fmt.Errorf("unsupported service: %s", s.service) + } +} + // ProcessDisks encapsulate the logic to manage different cloud providers func (s *Store) DownloadLog(groupID, host, name string, out io.Writer, opts *atlas.DateRangetOptions) error { switch s.service { diff --git a/internal/usage/usage.go b/internal/usage/usage.go index 1a3d4249b2..bd615ec723 100644 --- a/internal/usage/usage.go +++ b/internal/usage/usage.go @@ -81,6 +81,11 @@ const ( RSName = "The replica set that the index is built on." Key = "Index keys. Should be formatted as field:type." Unique = "Create a unique key index." + ResourceName = "Name of the resource from which to collect logs." + ResourceType = "Type of resource from which to collect logs." + LogTypes = "Array of strings specifying the types of logs to collect." + SizeRequestedPerFileBytes = "Size for each log file in bytes." + Redacted = "If set to true, emails, hostnames, IP addresses, and namespaces in API responses involving this job are replaced with random string values." Sparse = "Create a sparse index." Locale = "Locale that the ICU defines." CaseLevel = "If set to true, the index uses case comparison. This field applies only if the strength level is set to 1 or 2." From 9c6b9504011de2dc9a1fef87687951589eeef894 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 11:26:47 +0100 Subject: [PATCH 02/15] CLOUDP-60955 mongocli om logs list --- internal/cli/ops_manager_logs.go | 1 + internal/cli/ops_manager_logs_list.go | 73 ++++++++++++++++++++++ internal/cli/ops_manager_logs_list_test.go | 1 + internal/flags/flags.go | 1 + internal/store/logs.go | 11 ++++ internal/usage/usage.go | 1 + 6 files changed, 88 insertions(+) create mode 100644 internal/cli/ops_manager_logs_list.go create mode 100644 internal/cli/ops_manager_logs_list_test.go diff --git a/internal/cli/ops_manager_logs.go b/internal/cli/ops_manager_logs.go index fecc772631..74c62635e9 100644 --- a/internal/cli/ops_manager_logs.go +++ b/internal/cli/ops_manager_logs.go @@ -26,6 +26,7 @@ func OpsManagerLogsBuilder() *cobra.Command { Short: description.OMLogs, } cmd.AddCommand(opsManagerLogsCollectOptsBuilder()) + cmd.AddCommand(opsManagerLogsListOptsBuilder()) return cmd } diff --git a/internal/cli/ops_manager_logs_list.go b/internal/cli/ops_manager_logs_list.go new file mode 100644 index 0000000000..f39a66ec53 --- /dev/null +++ b/internal/cli/ops_manager_logs_list.go @@ -0,0 +1,73 @@ +// 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 ( + om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" + "github.com/mongodb/mongocli/internal/description" + "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 opsManagerLogsListOpts struct { + globalOpts + om.LogListOptions + store store.Logs +} + +func (opts *opsManagerLogsListOpts) initStore() error { + var err error + opts.store, err = store.New() + return err +} + +func (opts *opsManagerLogsListOpts) Run() error { + result, err := opts.store.ListLogJobs(opts.ProjectID(), opts.newLogListOptions()) + if err != nil { + return err + } + return json.PrettyPrint(result) +} + +func (opts *opsManagerLogsListOpts) newLogListOptions() *om.LogListOptions { + return &om.LogListOptions{Verbose:opts.Verbose} +} + +// mongocli om logs collect --verbose verbose [--projectId projectId] +func opsManagerLogsListOptsBuilder() *cobra.Command { + opts := &opsManagerLogsListOpts{ + + } + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: description.CollectOMLogs, + PreRunE: func(cmd *cobra.Command, args []string) error { + return opts.PreRunE(opts.initStore) + }, + RunE: func(cmd *cobra.Command, args []string) error { + return opts.Run() + }, + } + + cmd.Flags().BoolVar(&opts.Verbose, flags.Verbose, false, usage.Verbose) + + cmd.Flags().StringVar(&opts.projectID, flags.ProjectID, "", usage.ProjectID) + + return cmd +} diff --git a/internal/cli/ops_manager_logs_list_test.go b/internal/cli/ops_manager_logs_list_test.go new file mode 100644 index 0000000000..7f1e458cd3 --- /dev/null +++ b/internal/cli/ops_manager_logs_list_test.go @@ -0,0 +1 @@ +package cli diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 985baf666b..64157e41b2 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -120,4 +120,5 @@ const ( LogTypes = "logTypes" // LogTypes flag SizeRequestedPerFileBytes = "sizeRequestedPerFileBytes" //SizeRequestedPerFileBytes flag Redacted = "redacted" // Redacted flag + Verbose = "verbose" // Verbose flag ) diff --git a/internal/store/logs.go b/internal/store/logs.go index 1f8e8df444..412b5dee02 100644 --- a/internal/store/logs.go +++ b/internal/store/logs.go @@ -30,6 +30,17 @@ type LogsDownloader interface { type Logs interface { Collect(string, *om.LogCollectionJob) (*om.LogCollectionJob, error) + ListLogJobs(string, *om.LogListOptions)(*om.LogCollectionJobs, error) +} + +func (s *Store) ListLogJobs(groupID string, opts *om.LogListOptions) (*om.LogCollectionJobs, error) { + switch s.service { + case config.OpsManagerService, config.CloudManagerService: + log, _, err := s.client.(*om.Client).LogCollections.List(context.Background(), groupID, opts) + return log, err + default: + return nil, fmt.Errorf("unsupported service: %s", s.service) + } } func (s *Store) Collect(groupID string, newLog *om.LogCollectionJob) (*om.LogCollectionJob, error) { diff --git a/internal/usage/usage.go b/internal/usage/usage.go index bd615ec723..abcb14aab8 100644 --- a/internal/usage/usage.go +++ b/internal/usage/usage.go @@ -97,6 +97,7 @@ const ( Normalization = "If true, collation checks if text requires normalization and performs normalization to compare text." Backwards = "If true, strings with diacritics sort from the back to the front of the string." ClusterName = "Name of the cluster." + Verbose = "If true, returns all child jobs in the response." ClusterID = "Unique identifier of the cluster." Background = "Create the index in the background." TargetProjectID = "Unique identifier of the project that contains the destination cluster for the restore job." From eba677997a9c8e22c448bbbf5d30cf2b61202a57 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 11:29:17 +0100 Subject: [PATCH 03/15] Updated OM client to v0.2.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2907a24d5c..8775a6d844 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mattn/go-colorable v0.1.6 // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/mongodb/go-client-mongodb-atlas v0.2.1-0.20200423095452-c3661e38ae5c - github.com/mongodb/go-client-mongodb-ops-manager v0.2.0 + github.com/mongodb/go-client-mongodb-ops-manager v0.2.1 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 ee9dc65c43..674571bd9b 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,8 @@ github.com/mongodb/go-client-mongodb-atlas v0.2.0 h1:UH02byrzAlboirAMgV0sMIJqXbE github.com/mongodb/go-client-mongodb-atlas v0.2.0/go.mod h1:LS8O0YLkA+sbtOb3fZLF10yY3tJM+1xATXMJ3oU35LU= github.com/mongodb/go-client-mongodb-atlas v0.2.1-0.20200423095452-c3661e38ae5c h1:k1l2qLwwy4eCOli7f3oRWDbtIvXQF5SpO7JAXXcW3qI= github.com/mongodb/go-client-mongodb-atlas v0.2.1-0.20200423095452-c3661e38ae5c/go.mod h1:LS8O0YLkA+sbtOb3fZLF10yY3tJM+1xATXMJ3oU35LU= -github.com/mongodb/go-client-mongodb-ops-manager v0.2.0 h1:OfC6UXGJqivsMTNnxnZBu+e4Oz4s/9RdSwpv23eWBus= -github.com/mongodb/go-client-mongodb-ops-manager v0.2.0/go.mod h1:IWna/sWCgLSOLUeOYLg2nYf6HJcQnnqNI73K5NEMq9Q= +github.com/mongodb/go-client-mongodb-ops-manager v0.2.1 h1:60nEpqO5P74K5Y81OP3HHNt3e8n0NKKDX5wWqZDLuLY= +github.com/mongodb/go-client-mongodb-ops-manager v0.2.1/go.mod h1:IWna/sWCgLSOLUeOYLg2nYf6HJcQnnqNI73K5NEMq9Q= 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= From 603f09fe6a42a58858a43c72ea97402c4239efde Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 12:23:18 +0100 Subject: [PATCH 04/15] Addressed PR comments --- internal/cli/ops_manager_logs.go | 4 +- internal/cli/ops_manager_logs_collect.go | 33 +++++-- internal/cli/ops_manager_logs_collect_test.go | 2 +- internal/description/description.go | 96 +++++++++---------- internal/flags/flags.go | 4 +- internal/mocks/mock_logs.go | 28 +++--- internal/store/logs.go | 2 +- internal/usage/usage.go | 4 +- 8 files changed, 94 insertions(+), 79 deletions(-) diff --git a/internal/cli/ops_manager_logs.go b/internal/cli/ops_manager_logs.go index fecc772631..239b6298f0 100644 --- a/internal/cli/ops_manager_logs.go +++ b/internal/cli/ops_manager_logs.go @@ -23,9 +23,9 @@ func OpsManagerLogsBuilder() *cobra.Command { cmd := &cobra.Command{ Use: "logs", Aliases: []string{"log"}, - Short: description.OMLogs, + Short: description.LogCollection, } - cmd.AddCommand(opsManagerLogsCollectOptsBuilder()) + cmd.AddCommand(OpsManagerLogsCollectOptsBuilder()) return cmd } diff --git a/internal/cli/ops_manager_logs_collect.go b/internal/cli/ops_manager_logs_collect.go index 052bc51aa3..726d931a1d 100644 --- a/internal/cli/ops_manager_logs_collect.go +++ b/internal/cli/ops_manager_logs_collect.go @@ -15,10 +15,14 @@ package cli import ( + "fmt" + "strings" + om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" "github.com/mongodb/mongocli/internal/description" "github.com/mongodb/mongocli/internal/flags" "github.com/mongodb/mongocli/internal/json" + "github.com/mongodb/mongocli/internal/search" "github.com/mongodb/mongocli/internal/store" "github.com/mongodb/mongocli/internal/usage" "github.com/spf13/cobra" @@ -31,7 +35,7 @@ type opsManagerLogsCollectOpts struct { logTypes []string sizeRequestedPerFileBytes int64 redacted bool - store store.Logs + store store.LogCollector } func (opts *opsManagerLogsCollectOpts) initStore() error { @@ -58,25 +62,40 @@ func (opts *opsManagerLogsCollectOpts) newLog() *om.LogCollectionJob { } } -// mongocli om logs collect --resourceType type --resourceName resource --sizeRequestedPerFileBytes size --logTypes type --redacted redacted [--projectId projectId] -func opsManagerLogsCollectOptsBuilder() *cobra.Command { +// mongocli om logs collect resourceType resourceName --sizeRequestedPerFileBytes size --logTypes type --redacted redacted [--projectId projectId] +func OpsManagerLogsCollectOptsBuilder() *cobra.Command { opts := &opsManagerLogsCollectOpts{} cmd := &cobra.Command{ Use: "collect", - Short: description.CollectOMLogs, + Short: description.StartLogCollectionJob, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 2 { + return fmt.Errorf("accepts %d arg(s), received %d", 2, len(args)) + } + + args[0] = strings.ToLower(args[0]) + if !search.StringInSlice(cmd.ValidArgs, args[0]) { + return fmt.Errorf("invalid Resource Type. The Resource Type must be cluster, process or replicaset but was %q", args[0]) + } + return nil + }, + ValidArgs: []string{"cluster", "process", "replicaset"}, PreRunE: func(cmd *cobra.Command, args []string) error { return opts.PreRunE(opts.initStore) }, RunE: func(cmd *cobra.Command, args []string) error { + opts.resourceType = args[0] + opts.resourceName = args[1] return opts.Run() }, } - cmd.Flags().StringVar(&opts.resourceName, flags.ResourceName, "", usage.ResourceName) - cmd.Flags().StringVar(&opts.resourceType, flags.ResourceType, "", usage.ResourceType) cmd.Flags().StringArrayVar(&opts.logTypes, flags.LogTypes, nil, usage.LogTypes) cmd.Flags().Int64Var(&opts.sizeRequestedPerFileBytes, flags.SizeRequestedPerFileBytes, 0, usage.SizeRequestedPerFileBytes) - cmd.Flags().BoolVar(&opts.redacted, flags.Redacted, false, usage.Redacted) + cmd.Flags().BoolVar(&opts.redacted, flags.LogRedacted, false, usage.LogRedacted) + + _ = cmd.MarkFlagRequired(flags.SizeRequestedPerFileBytes) + _ = cmd.MarkFlagRequired(flags.LogTypes) cmd.Flags().StringVar(&opts.projectID, flags.ProjectID, "", usage.ProjectID) diff --git a/internal/cli/ops_manager_logs_collect_test.go b/internal/cli/ops_manager_logs_collect_test.go index 46aab2dc42..a9e39c7873 100644 --- a/internal/cli/ops_manager_logs_collect_test.go +++ b/internal/cli/ops_manager_logs_collect_test.go @@ -23,7 +23,7 @@ import ( func TestOpsManagerLogsCollectOpts_Run(t *testing.T) { ctrl := gomock.NewController(t) - mockStore := mocks.NewMockLogs(ctrl) + mockStore := mocks.NewMockLogCollector(ctrl) defer ctrl.Finish() diff --git a/internal/description/description.go b/internal/description/description.go index ff8be83756..0d1dbac364 100644 --- a/internal/description/description.go +++ b/internal/description/description.go @@ -53,52 +53,52 @@ const ( The dbusers command retrieves, creates and modifies the MongoDB database users in your cluster. Each user has a set of roles that provide access to the project’s databases. A user’s roles apply to all the clusters in the project.` - CreateDBUser = "Create a database user for a project." - DeleteDBUser = "Delete a database user for a project." - ListDBUsers = "List Atlas database users for a project." - ListEvents = "List events for an organization or project" - UpdateDBUser = "Update a MongoDB dbuser in Atlas." - ProcessMeasurements = "Get measurements for a given host." - Disks = "List available disks or disks measurements for a given host." - ListDisks = "List available disks for a given host." - DescribeDisks = "Describe disks measurements for a given host partition." - Databases = "List available databases or databases measurements for a given host." - ListDatabases = "List available databases for a given host." - Whitelist = "Manage the IP whitelist for a project." - CreateWhitelist = "Create an IP whitelist for a project." - DeleteWhitelist = "Delete a database user for a project." - DescribeWhitelist = "Describe an Atlas whitelist." - ListWhitelist = "List Atlas whitelist for a project." - CloudManager = "Cloud Manager operations." - ShutdownOMCluster = "Shutdown a Cloud Manager cluster." - StartUpOMCluster = "Startup a Cloud Manager cluster." - UpdateOMCluster = "Update a Cloud Manager cluster." - ConfigDescription = "Configure the tool." - SetConfig = "Configure the tool." - IAM = "Authentication operations." - Organization = "Organization operations." - OrganizationLong = "Create, list and manage your MongoDB organizations." - CreateOrganization = "Create an organization." - DeleteOrganization = "Delete an organization." - ListOrganizations = "List organizations." - Projects = "Project operations." - ProjectsLong = "Create, list and manage your MongoDB projects." - CreateProject = "Create a project." - DeleteProject = "Delete a project." - ListProjects = "List projects." - OpsManager = "Ops Manager operations." - ListGlobalAlerts = "List global alerts." - Automation = "Manage Ops Manager automation config." - ShowAutomationStatus = "Show the current status of the automation config." - Global = "Manage Ops Manager global properties." - OMLogs = "Manage Ops Manager log collection job." - CollectOMLogs = "Start a job to collect logs" - Owner = "Manage Ops Manager owners." - CreateOwner = "Create the first user for Ops Manager." - Servers = "Manage Ops Manager servers." - ListServer = "List all available servers running an automation agent for the given project." - Security = "Manage clusters security configuration." - EnableSecurity = "Enable authentication mechanisms for the project." - Events = "Manage events for your project." - Measurements = "Get measurements on the state of the MongoDB process." + CreateDBUser = "Create a database user for a project." + DeleteDBUser = "Delete a database user for a project." + ListDBUsers = "List Atlas database users for a project." + ListEvents = "List events for an organization or project" + UpdateDBUser = "Update a MongoDB dbuser in Atlas." + ProcessMeasurements = "Get measurements for a given host." + Disks = "List available disks or disks measurements for a given host." + ListDisks = "List available disks for a given host." + DescribeDisks = "Describe disks measurements for a given host partition." + Databases = "List available databases or databases measurements for a given host." + ListDatabases = "List available databases for a given host." + Whitelist = "Manage the IP whitelist for a project." + CreateWhitelist = "Create an IP whitelist for a project." + DeleteWhitelist = "Delete a database user for a project." + DescribeWhitelist = "Describe an Atlas whitelist." + ListWhitelist = "List Atlas whitelist for a project." + CloudManager = "Cloud Manager operations." + ShutdownOMCluster = "Shutdown a Cloud Manager cluster." + StartUpOMCluster = "Startup a Cloud Manager cluster." + UpdateOMCluster = "Update a Cloud Manager cluster." + ConfigDescription = "Configure the tool." + SetConfig = "Configure the tool." + IAM = "Authentication operations." + Organization = "Organization operations." + OrganizationLong = "Create, list and manage your MongoDB organizations." + CreateOrganization = "Create an organization." + DeleteOrganization = "Delete an organization." + ListOrganizations = "List organizations." + Projects = "Project operations." + ProjectsLong = "Create, list and manage your MongoDB projects." + CreateProject = "Create a project." + DeleteProject = "Delete a project." + ListProjects = "List projects." + OpsManager = "Ops Manager operations." + ListGlobalAlerts = "List global alerts." + Automation = "Manage Ops Manager automation config." + ShowAutomationStatus = "Show the current status of the automation config." + Global = "Manage Ops Manager global properties." + LogCollection = "Manage log collection jobs." + StartLogCollectionJob = "Start a job to collect logs." + Owner = "Manage Ops Manager owners." + CreateOwner = "Create the first user for Ops Manager." + Servers = "Manage Ops Manager servers." + ListServer = "List all available servers running an automation agent for the given project." + Security = "Manage clusters security configuration." + EnableSecurity = "Enable authentication mechanisms for the project." + Events = "Manage events for your project." + Measurements = "Get measurements on the state of the MongoDB process." ) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 985baf666b..74ccd95887 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -115,9 +115,7 @@ const ( Normalization = "normalization" // Normalization flag Backwards = "backwards" // Backwards flag Strength = "strength" // Strength flag - ResourceName = "resourceName" // ResourceName flag - ResourceType = "resourceType" // ResourceType flag LogTypes = "logTypes" // LogTypes flag SizeRequestedPerFileBytes = "sizeRequestedPerFileBytes" //SizeRequestedPerFileBytes flag - Redacted = "redacted" // Redacted flag + LogRedacted = "redacted" // LogRedacted flag ) diff --git a/internal/mocks/mock_logs.go b/internal/mocks/mock_logs.go index c61776840d..7281e9d9e3 100644 --- a/internal/mocks/mock_logs.go +++ b/internal/mocks/mock_logs.go @@ -49,31 +49,31 @@ func (mr *MockLogsDownloaderMockRecorder) DownloadLog(arg0, arg1, arg2, arg3, ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DownloadLog", reflect.TypeOf((*MockLogsDownloader)(nil).DownloadLog), arg0, arg1, arg2, arg3, arg4) } -// MockLogs is a mock of Logs interface -type MockLogs struct { +// MockLogCollector is a mock of LogCollector interface +type MockLogCollector struct { ctrl *gomock.Controller - recorder *MockLogsMockRecorder + recorder *MockLogCollectorMockRecorder } -// MockLogsMockRecorder is the mock recorder for MockLogs -type MockLogsMockRecorder struct { - mock *MockLogs +// MockLogCollectorMockRecorder is the mock recorder for MockLogCollector +type MockLogCollectorMockRecorder struct { + mock *MockLogCollector } -// NewMockLogs creates a new mock instance -func NewMockLogs(ctrl *gomock.Controller) *MockLogs { - mock := &MockLogs{ctrl: ctrl} - mock.recorder = &MockLogsMockRecorder{mock} +// NewMockLogCollector creates a new mock instance +func NewMockLogCollector(ctrl *gomock.Controller) *MockLogCollector { + mock := &MockLogCollector{ctrl: ctrl} + mock.recorder = &MockLogCollectorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use -func (m *MockLogs) EXPECT() *MockLogsMockRecorder { +func (m *MockLogCollector) EXPECT() *MockLogCollectorMockRecorder { return m.recorder } // Collect mocks base method -func (m *MockLogs) Collect(arg0 string, arg1 *opsmngr.LogCollectionJob) (*opsmngr.LogCollectionJob, error) { +func (m *MockLogCollector) Collect(arg0 string, arg1 *opsmngr.LogCollectionJob) (*opsmngr.LogCollectionJob, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Collect", arg0, arg1) ret0, _ := ret[0].(*opsmngr.LogCollectionJob) @@ -82,7 +82,7 @@ func (m *MockLogs) Collect(arg0 string, arg1 *opsmngr.LogCollectionJob) (*opsmng } // Collect indicates an expected call of Collect -func (mr *MockLogsMockRecorder) Collect(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockLogCollectorMockRecorder) Collect(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collect", reflect.TypeOf((*MockLogs)(nil).Collect), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collect", reflect.TypeOf((*MockLogCollector)(nil).Collect), arg0, arg1) } diff --git a/internal/store/logs.go b/internal/store/logs.go index 1f8e8df444..219ce0f035 100644 --- a/internal/store/logs.go +++ b/internal/store/logs.go @@ -28,7 +28,7 @@ type LogsDownloader interface { DownloadLog(string, string, string, io.Writer, *atlas.DateRangetOptions) error } -type Logs interface { +type LogCollector interface { Collect(string, *om.LogCollectionJob) (*om.LogCollectionJob, error) } diff --git a/internal/usage/usage.go b/internal/usage/usage.go index bd615ec723..8ba2e8cfda 100644 --- a/internal/usage/usage.go +++ b/internal/usage/usage.go @@ -81,11 +81,9 @@ const ( RSName = "The replica set that the index is built on." Key = "Index keys. Should be formatted as field:type." Unique = "Create a unique key index." - ResourceName = "Name of the resource from which to collect logs." - ResourceType = "Type of resource from which to collect logs." LogTypes = "Array of strings specifying the types of logs to collect." SizeRequestedPerFileBytes = "Size for each log file in bytes." - Redacted = "If set to true, emails, hostnames, IP addresses, and namespaces in API responses involving this job are replaced with random string values." + LogRedacted = "If set to true, emails, hostnames, IP addresses, and namespaces in API responses involving this job are replaced with random string values." Sparse = "Create a sparse index." Locale = "Locale that the ICU defines." CaseLevel = "If set to true, the index uses case comparison. This field applies only if the strength level is set to 1 or 2." From db86d824374b5b26131fac229e807af3dc2c3ca4 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 13:11:31 +0100 Subject: [PATCH 05/15] Added unit test --- internal/cli/ops_manager_logs_list.go | 12 +++--- internal/cli/ops_manager_logs_list_test.go | 44 ++++++++++++++++++++++ internal/description/description.go | 1 + internal/flags/flags.go | 2 +- internal/mocks/mock_logs.go | 38 +++++++++++++++++++ internal/store/logs.go | 5 ++- internal/usage/usage.go | 2 +- 7 files changed, 94 insertions(+), 10 deletions(-) diff --git a/internal/cli/ops_manager_logs_list.go b/internal/cli/ops_manager_logs_list.go index f39a66ec53..3ab464e62f 100644 --- a/internal/cli/ops_manager_logs_list.go +++ b/internal/cli/ops_manager_logs_list.go @@ -27,7 +27,7 @@ import ( type opsManagerLogsListOpts struct { globalOpts om.LogListOptions - store store.Logs + store store.LogsLister } func (opts *opsManagerLogsListOpts) initStore() error { @@ -45,18 +45,16 @@ func (opts *opsManagerLogsListOpts) Run() error { } func (opts *opsManagerLogsListOpts) newLogListOptions() *om.LogListOptions { - return &om.LogListOptions{Verbose:opts.Verbose} + return &om.LogListOptions{Verbose: opts.Verbose} } // mongocli om logs collect --verbose verbose [--projectId projectId] func opsManagerLogsListOptsBuilder() *cobra.Command { - opts := &opsManagerLogsListOpts{ - - } + opts := &opsManagerLogsListOpts{} cmd := &cobra.Command{ - Use: "list", + Use: "list", Aliases: []string{"ls"}, - Short: description.CollectOMLogs, + Short: description.ListLogCollectionJobs, PreRunE: func(cmd *cobra.Command, args []string) error { return opts.PreRunE(opts.initStore) }, diff --git a/internal/cli/ops_manager_logs_list_test.go b/internal/cli/ops_manager_logs_list_test.go index 7f1e458cd3..72c473d87a 100644 --- a/internal/cli/ops_manager_logs_list_test.go +++ b/internal/cli/ops_manager_logs_list_test.go @@ -1 +1,45 @@ +// 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" + om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" + "github.com/mongodb/mongocli/internal/mocks" +) + +func TestOpsManagerLogsListOpts_Run(t *testing.T) { + ctrl := gomock.NewController(t) + mockStore := mocks.NewMockLogsLister(ctrl) + + defer ctrl.Finish() + + expected := &om.LogCollectionJobs{} + + listOpts := &opsManagerLogsListOpts{ + store: mockStore, + LogListOptions: om.LogListOptions{Verbose: true}, + } + + mockStore. + EXPECT().ListLogJobs(listOpts.projectID, &listOpts.LogListOptions). + Return(expected, nil). + Times(1) + + if err := listOpts.Run(); err != nil { + t.Fatalf("Run() unexpected error: %v", err) + } +} diff --git a/internal/description/description.go b/internal/description/description.go index 0d1dbac364..698d0d0c9a 100644 --- a/internal/description/description.go +++ b/internal/description/description.go @@ -93,6 +93,7 @@ A user’s roles apply to all the clusters in the project.` Global = "Manage Ops Manager global properties." LogCollection = "Manage log collection jobs." StartLogCollectionJob = "Start a job to collect logs." + ListLogCollectionJobs = "List log collect jobs." Owner = "Manage Ops Manager owners." CreateOwner = "Create the first user for Ops Manager." Servers = "Manage Ops Manager servers." diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 96dba612a4..7cea54e375 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -118,5 +118,5 @@ const ( LogTypes = "logTypes" // LogTypes flag SizeRequestedPerFileBytes = "sizeRequestedPerFileBytes" //SizeRequestedPerFileBytes flag LogRedacted = "redacted" // LogRedacted flag - Verbose = "verbose" // Verbose flag + Verbose = "verbose" // Verbose flag ) diff --git a/internal/mocks/mock_logs.go b/internal/mocks/mock_logs.go index 7281e9d9e3..a85d2cf5ba 100644 --- a/internal/mocks/mock_logs.go +++ b/internal/mocks/mock_logs.go @@ -86,3 +86,41 @@ func (mr *MockLogCollectorMockRecorder) Collect(arg0, arg1 interface{}) *gomock. mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collect", reflect.TypeOf((*MockLogCollector)(nil).Collect), arg0, arg1) } + +// MockLogsLister is a mock of LogsLister interface +type MockLogsLister struct { + ctrl *gomock.Controller + recorder *MockLogsListerMockRecorder +} + +// MockLogsListerMockRecorder is the mock recorder for MockLogsLister +type MockLogsListerMockRecorder struct { + mock *MockLogsLister +} + +// NewMockLogsLister creates a new mock instance +func NewMockLogsLister(ctrl *gomock.Controller) *MockLogsLister { + mock := &MockLogsLister{ctrl: ctrl} + mock.recorder = &MockLogsListerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockLogsLister) EXPECT() *MockLogsListerMockRecorder { + return m.recorder +} + +// ListLogJobs mocks base method +func (m *MockLogsLister) ListLogJobs(arg0 string, arg1 *opsmngr.LogListOptions) (*opsmngr.LogCollectionJobs, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListLogJobs", arg0, arg1) + ret0, _ := ret[0].(*opsmngr.LogCollectionJobs) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListLogJobs indicates an expected call of ListLogJobs +func (mr *MockLogsListerMockRecorder) ListLogJobs(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListLogJobs", reflect.TypeOf((*MockLogsLister)(nil).ListLogJobs), arg0, arg1) +} diff --git a/internal/store/logs.go b/internal/store/logs.go index 1bce1ba008..51f1f1c347 100644 --- a/internal/store/logs.go +++ b/internal/store/logs.go @@ -30,7 +30,10 @@ type LogsDownloader interface { type LogCollector interface { Collect(string, *om.LogCollectionJob) (*om.LogCollectionJob, error) - ListLogJobs(string, *om.LogListOptions)(*om.LogCollectionJobs, error) +} + +type LogsLister interface { + ListLogJobs(string, *om.LogListOptions) (*om.LogCollectionJobs, error) } func (s *Store) ListLogJobs(groupID string, opts *om.LogListOptions) (*om.LogCollectionJobs, error) { diff --git a/internal/usage/usage.go b/internal/usage/usage.go index 0c448191ce..aa3e240ff1 100644 --- a/internal/usage/usage.go +++ b/internal/usage/usage.go @@ -95,7 +95,7 @@ const ( Normalization = "If true, collation checks if text requires normalization and performs normalization to compare text." Backwards = "If true, strings with diacritics sort from the back to the front of the string." ClusterName = "Name of the cluster." - Verbose = "If true, returns all child jobs in the response." + Verbose = "If true, returns all child jobs in the response." ClusterID = "Unique identifier of the cluster." Background = "Create the index in the background." TargetProjectID = "Unique identifier of the project that contains the destination cluster for the restore job." From dd052bb9e524ca292d56e781400030559b9b2811 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 13:39:08 +0100 Subject: [PATCH 06/15] Refactoring --- internal/store/logs.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/internal/store/logs.go b/internal/store/logs.go index 4e44d5b79d..ecf8925b6d 100644 --- a/internal/store/logs.go +++ b/internal/store/logs.go @@ -17,6 +17,7 @@ package store import ( "context" "fmt" + om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" "io" atlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas" @@ -35,16 +36,7 @@ type LogsLister interface { ListLogJobs(string, *om.LogListOptions) (*om.LogCollectionJobs, error) } -func (s *Store) Collect(groupID string, newLog *om.LogCollectionJob) (*om.LogCollectionJob, error) { - switch s.service { - case config.OpsManagerService, config.CloudManagerService: - log, _, err := s.client.(*om.Client).LogCollections.Create(context.Background(), groupID, newLog) - return log, err - default: - return nil, fmt.Errorf("unsupported service: %s", s.service) - } -} - +// ListLogJobs encapsulate the logic to manage different cloud providers func (s *Store) ListLogJobs(groupID string, opts *om.LogListOptions) (*om.LogCollectionJobs, error) { switch s.service { case config.OpsManagerService, config.CloudManagerService: @@ -55,6 +47,7 @@ func (s *Store) ListLogJobs(groupID string, opts *om.LogListOptions) (*om.LogCol } } +// Collect encapsulate the logic to manage different cloud providers func (s *Store) Collect(groupID string, newLog *om.LogCollectionJob) (*om.LogCollectionJob, error) { switch s.service { case config.OpsManagerService, config.CloudManagerService: From 9468ba39354dc32a6a8c2e0263a08b7f3fd099ce Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 13:45:38 +0100 Subject: [PATCH 07/15] Generated mock --- internal/flags/flags.go | 2 +- internal/mocks/mock_logs.go | 38 +++++++++++++++++++++++++++++++++++++ internal/store/logs.go | 2 +- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 1b8c92eb39..a7a82cc6ac 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -116,6 +116,6 @@ const ( Backwards = "backwards" // Backwards flag Strength = "strength" // Strength flag SizeRequestedPerFileBytes = "sizeRequestedPerFileBytes" //SizeRequestedPerFileBytes flag - Redacted = "redacted" // Redacted flag + Redacted = "redacted" // Redacted flag Verbose = "verbose" ) diff --git a/internal/mocks/mock_logs.go b/internal/mocks/mock_logs.go index 7281e9d9e3..a85d2cf5ba 100644 --- a/internal/mocks/mock_logs.go +++ b/internal/mocks/mock_logs.go @@ -86,3 +86,41 @@ func (mr *MockLogCollectorMockRecorder) Collect(arg0, arg1 interface{}) *gomock. mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collect", reflect.TypeOf((*MockLogCollector)(nil).Collect), arg0, arg1) } + +// MockLogsLister is a mock of LogsLister interface +type MockLogsLister struct { + ctrl *gomock.Controller + recorder *MockLogsListerMockRecorder +} + +// MockLogsListerMockRecorder is the mock recorder for MockLogsLister +type MockLogsListerMockRecorder struct { + mock *MockLogsLister +} + +// NewMockLogsLister creates a new mock instance +func NewMockLogsLister(ctrl *gomock.Controller) *MockLogsLister { + mock := &MockLogsLister{ctrl: ctrl} + mock.recorder = &MockLogsListerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockLogsLister) EXPECT() *MockLogsListerMockRecorder { + return m.recorder +} + +// ListLogJobs mocks base method +func (m *MockLogsLister) ListLogJobs(arg0 string, arg1 *opsmngr.LogListOptions) (*opsmngr.LogCollectionJobs, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListLogJobs", arg0, arg1) + ret0, _ := ret[0].(*opsmngr.LogCollectionJobs) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListLogJobs indicates an expected call of ListLogJobs +func (mr *MockLogsListerMockRecorder) ListLogJobs(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListLogJobs", reflect.TypeOf((*MockLogsLister)(nil).ListLogJobs), arg0, arg1) +} diff --git a/internal/store/logs.go b/internal/store/logs.go index ecf8925b6d..d60ba54909 100644 --- a/internal/store/logs.go +++ b/internal/store/logs.go @@ -17,10 +17,10 @@ package store import ( "context" "fmt" - om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" "io" 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" ) From c0143353b2a6538f1c1637b91aebeb8dc105e2f8 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 13:51:26 +0100 Subject: [PATCH 08/15] Making opsManagerLogsListOptsBuilder public --- internal/cli/ops_manager_logs.go | 2 +- internal/cli/ops_manager_logs_list.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cli/ops_manager_logs.go b/internal/cli/ops_manager_logs.go index 0d123e0474..334667af7f 100644 --- a/internal/cli/ops_manager_logs.go +++ b/internal/cli/ops_manager_logs.go @@ -27,7 +27,7 @@ func OpsManagerLogsBuilder() *cobra.Command { } cmd.AddCommand(OpsManagerLogsCollectOptsBuilder()) - cmd.AddCommand(opsManagerLogsListOptsBuilder()) + cmd.AddCommand(OpsManagerLogsListOptsBuilder()) return cmd } diff --git a/internal/cli/ops_manager_logs_list.go b/internal/cli/ops_manager_logs_list.go index 3ab464e62f..13f7546e8a 100644 --- a/internal/cli/ops_manager_logs_list.go +++ b/internal/cli/ops_manager_logs_list.go @@ -49,7 +49,7 @@ func (opts *opsManagerLogsListOpts) newLogListOptions() *om.LogListOptions { } // mongocli om logs collect --verbose verbose [--projectId projectId] -func opsManagerLogsListOptsBuilder() *cobra.Command { +func OpsManagerLogsListOptsBuilder() *cobra.Command { opts := &opsManagerLogsListOpts{} cmd := &cobra.Command{ Use: "list", From e787ab19b6c45d2b4d1165caf432ecb64faeb833 Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Fri, 24 Apr 2020 15:50:49 +0100 Subject: [PATCH 09/15] Update internal/cli/ops_manager_logs_list.go --- internal/cli/ops_manager_logs_list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cli/ops_manager_logs_list.go b/internal/cli/ops_manager_logs_list.go index 13f7546e8a..b698e17e2c 100644 --- a/internal/cli/ops_manager_logs_list.go +++ b/internal/cli/ops_manager_logs_list.go @@ -48,7 +48,7 @@ func (opts *opsManagerLogsListOpts) newLogListOptions() *om.LogListOptions { return &om.LogListOptions{Verbose: opts.Verbose} } -// mongocli om logs collect --verbose verbose [--projectId projectId] +// mongocli om logs list --verbose verbose [--projectId projectId] func OpsManagerLogsListOptsBuilder() *cobra.Command { opts := &opsManagerLogsListOpts{} cmd := &cobra.Command{ From 2d83712681bd3e380158090d1a5451f87c91d719 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 16:07:55 +0100 Subject: [PATCH 10/15] Starting implementing the download command --- internal/cli/ops_manager_logs_download.go | 73 +++++++++++++++++++ .../cli/ops_manager_logs_download_test.go | 1 + internal/description/description.go | 1 + internal/flags/flags.go | 4 +- internal/usage/usage.go | 1 + 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 internal/cli/ops_manager_logs_download.go create mode 100644 internal/cli/ops_manager_logs_download_test.go diff --git a/internal/cli/ops_manager_logs_download.go b/internal/cli/ops_manager_logs_download.go new file mode 100644 index 0000000000..64947f601f --- /dev/null +++ b/internal/cli/ops_manager_logs_download.go @@ -0,0 +1,73 @@ +// 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 ( + om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" + "github.com/mongodb/mongocli/internal/description" + "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 opsManagerLogsDownloadOpts struct { + globalOpts + id string + filePath string + store store.LogsLister +} + +func (opts *opsManagerLogsDownloadOpts) initStore() error { + var err error + opts.store, err = store.New() + return err +} + +func (opts *opsManagerLogsDownloadOpts) Run() error { + result, err := opts.store.ListLogJobs(opts.ProjectID(), opts.newLogListOptions()) + if err != nil { + return err + } + return json.PrettyPrint(result) +} + +func (opts *opsManagerLogsDownloadOpts) newLogListOptions() *om.LogListOptions { + return &om.LogListOptions{Verbose: opts.Verbose} +} + +// mongocli om logs download id [--filePath filePath] [--projectId projectId] +func OpsManagerLogsDownloadOptsBuilder() *cobra.Command { + opts := &opsManagerLogsDownloadOpts{} + cmd := &cobra.Command{ + Use: "download", + Short: description.DownloadLogs, + PreRunE: func(cmd *cobra.Command, args []string) error { + return opts.PreRunE(opts.initStore) + }, + RunE: func(cmd *cobra.Command, args []string) error { + opts.id = args[0] + return opts.Run() + }, + } + + cmd.Flags().StringVar(&opts.id, flags.ID, "", usage.Verbose) + cmd.Flags().StringVar(&opts.filePath, flags.FilePath, "", usage.Verbose) + + cmd.Flags().StringVar(&opts.projectID, flags.ProjectID, "", usage.ProjectID) + + return cmd +} diff --git a/internal/cli/ops_manager_logs_download_test.go b/internal/cli/ops_manager_logs_download_test.go new file mode 100644 index 0000000000..7f1e458cd3 --- /dev/null +++ b/internal/cli/ops_manager_logs_download_test.go @@ -0,0 +1 @@ +package cli diff --git a/internal/description/description.go b/internal/description/description.go index 698d0d0c9a..4b85f1a3b8 100644 --- a/internal/description/description.go +++ b/internal/description/description.go @@ -94,6 +94,7 @@ A user’s roles apply to all the clusters in the project.` LogCollection = "Manage log collection jobs." StartLogCollectionJob = "Start a job to collect logs." ListLogCollectionJobs = "List log collect jobs." + DownloadLogs = "Download logs from a log collection job." Owner = "Manage Ops Manager owners." CreateOwner = "Create the first user for Ops Manager." Servers = "Manage Ops Manager servers." diff --git a/internal/flags/flags.go b/internal/flags/flags.go index a7a82cc6ac..776b609114 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -115,7 +115,9 @@ const ( Normalization = "normalization" // Normalization flag Backwards = "backwards" // Backwards flag Strength = "strength" // Strength flag - SizeRequestedPerFileBytes = "sizeRequestedPerFileBytes" //SizeRequestedPerFileBytes flag + SizeRequestedPerFileBytes = "sizeRequestedPerFileBytes" // SizeRequestedPerFileBytes flag Redacted = "redacted" // Redacted flag Verbose = "verbose" + ID = "id" //ID flag + FilePath ="filePath" // FilePath flag ) diff --git a/internal/usage/usage.go b/internal/usage/usage.go index aa3e240ff1..589e0e4068 100644 --- a/internal/usage/usage.go +++ b/internal/usage/usage.go @@ -96,6 +96,7 @@ const ( Backwards = "If true, strings with diacritics sort from the back to the front of the string." ClusterName = "Name of the cluster." Verbose = "If true, returns all child jobs in the response." + ClusterID = "Unique identifier of the cluster." Background = "Create the index in the background." TargetProjectID = "Unique identifier of the project that contains the destination cluster for the restore job." From ec77d7e243bde2339ac8b8987bf64e8e6c2656cc Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 16:39:47 +0100 Subject: [PATCH 11/15] Addressed PR comments --- internal/cli/ops_manager_logs_list.go | 10 +++---- internal/cli/ops_manager_logs_list_test.go | 8 ++--- internal/description/description.go | 2 +- internal/mocks/mock_logs.go | 34 +++++++++++----------- internal/store/logs.go | 8 ++--- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/internal/cli/ops_manager_logs_list.go b/internal/cli/ops_manager_logs_list.go index b698e17e2c..bc94efa9b8 100644 --- a/internal/cli/ops_manager_logs_list.go +++ b/internal/cli/ops_manager_logs_list.go @@ -26,8 +26,8 @@ import ( type opsManagerLogsListOpts struct { globalOpts - om.LogListOptions - store store.LogsLister + verbose bool + store store.LogJobLister } func (opts *opsManagerLogsListOpts) initStore() error { @@ -37,7 +37,7 @@ func (opts *opsManagerLogsListOpts) initStore() error { } func (opts *opsManagerLogsListOpts) Run() error { - result, err := opts.store.ListLogJobs(opts.ProjectID(), opts.newLogListOptions()) + result, err := opts.store.LogCollectionJobs(opts.ProjectID(), opts.newLogListOptions()) if err != nil { return err } @@ -45,7 +45,7 @@ func (opts *opsManagerLogsListOpts) Run() error { } func (opts *opsManagerLogsListOpts) newLogListOptions() *om.LogListOptions { - return &om.LogListOptions{Verbose: opts.Verbose} + return &om.LogListOptions{Verbose: opts.verbose} } // mongocli om logs list --verbose verbose [--projectId projectId] @@ -63,7 +63,7 @@ func OpsManagerLogsListOptsBuilder() *cobra.Command { }, } - cmd.Flags().BoolVar(&opts.Verbose, flags.Verbose, false, usage.Verbose) + cmd.Flags().BoolVar(&opts.verbose, flags.Verbose, false, usage.Verbose) cmd.Flags().StringVar(&opts.projectID, flags.ProjectID, "", usage.ProjectID) diff --git a/internal/cli/ops_manager_logs_list_test.go b/internal/cli/ops_manager_logs_list_test.go index 72c473d87a..a17d96a26d 100644 --- a/internal/cli/ops_manager_logs_list_test.go +++ b/internal/cli/ops_manager_logs_list_test.go @@ -23,19 +23,19 @@ import ( func TestOpsManagerLogsListOpts_Run(t *testing.T) { ctrl := gomock.NewController(t) - mockStore := mocks.NewMockLogsLister(ctrl) + mockStore := mocks.NewMockLogJobLister(ctrl) defer ctrl.Finish() expected := &om.LogCollectionJobs{} listOpts := &opsManagerLogsListOpts{ - store: mockStore, - LogListOptions: om.LogListOptions{Verbose: true}, + store: mockStore, + verbose: true, } mockStore. - EXPECT().ListLogJobs(listOpts.projectID, &listOpts.LogListOptions). + EXPECT().LogCollectionJobs(listOpts.projectID, listOpts.newLogListOptions()). Return(expected, nil). Times(1) diff --git a/internal/description/description.go b/internal/description/description.go index 698d0d0c9a..5874428ddb 100644 --- a/internal/description/description.go +++ b/internal/description/description.go @@ -93,7 +93,7 @@ A user’s roles apply to all the clusters in the project.` Global = "Manage Ops Manager global properties." LogCollection = "Manage log collection jobs." StartLogCollectionJob = "Start a job to collect logs." - ListLogCollectionJobs = "List log collect jobs." + ListLogCollectionJobs = "List log collection jobs." Owner = "Manage Ops Manager owners." CreateOwner = "Create the first user for Ops Manager." Servers = "Manage Ops Manager servers." diff --git a/internal/mocks/mock_logs.go b/internal/mocks/mock_logs.go index a85d2cf5ba..62aef1d7a7 100644 --- a/internal/mocks/mock_logs.go +++ b/internal/mocks/mock_logs.go @@ -87,40 +87,40 @@ func (mr *MockLogCollectorMockRecorder) Collect(arg0, arg1 interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collect", reflect.TypeOf((*MockLogCollector)(nil).Collect), arg0, arg1) } -// MockLogsLister is a mock of LogsLister interface -type MockLogsLister struct { +// MockLogJobLister is a mock of LogJobLister interface +type MockLogJobLister struct { ctrl *gomock.Controller - recorder *MockLogsListerMockRecorder + recorder *MockLogJobListerMockRecorder } -// MockLogsListerMockRecorder is the mock recorder for MockLogsLister -type MockLogsListerMockRecorder struct { - mock *MockLogsLister +// MockLogJobListerMockRecorder is the mock recorder for MockLogJobLister +type MockLogJobListerMockRecorder struct { + mock *MockLogJobLister } -// NewMockLogsLister creates a new mock instance -func NewMockLogsLister(ctrl *gomock.Controller) *MockLogsLister { - mock := &MockLogsLister{ctrl: ctrl} - mock.recorder = &MockLogsListerMockRecorder{mock} +// NewMockLogJobLister creates a new mock instance +func NewMockLogJobLister(ctrl *gomock.Controller) *MockLogJobLister { + mock := &MockLogJobLister{ctrl: ctrl} + mock.recorder = &MockLogJobListerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use -func (m *MockLogsLister) EXPECT() *MockLogsListerMockRecorder { +func (m *MockLogJobLister) EXPECT() *MockLogJobListerMockRecorder { return m.recorder } -// ListLogJobs mocks base method -func (m *MockLogsLister) ListLogJobs(arg0 string, arg1 *opsmngr.LogListOptions) (*opsmngr.LogCollectionJobs, error) { +// LogCollectionJobs mocks base method +func (m *MockLogJobLister) LogCollectionJobs(arg0 string, arg1 *opsmngr.LogListOptions) (*opsmngr.LogCollectionJobs, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListLogJobs", arg0, arg1) + ret := m.ctrl.Call(m, "LogCollectionJobs", arg0, arg1) ret0, _ := ret[0].(*opsmngr.LogCollectionJobs) ret1, _ := ret[1].(error) return ret0, ret1 } -// ListLogJobs indicates an expected call of ListLogJobs -func (mr *MockLogsListerMockRecorder) ListLogJobs(arg0, arg1 interface{}) *gomock.Call { +// LogCollectionJobs indicates an expected call of LogCollectionJobs +func (mr *MockLogJobListerMockRecorder) LogCollectionJobs(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListLogJobs", reflect.TypeOf((*MockLogsLister)(nil).ListLogJobs), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogCollectionJobs", reflect.TypeOf((*MockLogJobLister)(nil).LogCollectionJobs), arg0, arg1) } diff --git a/internal/store/logs.go b/internal/store/logs.go index d60ba54909..0568bc0e35 100644 --- a/internal/store/logs.go +++ b/internal/store/logs.go @@ -32,12 +32,12 @@ type LogCollector interface { Collect(string, *om.LogCollectionJob) (*om.LogCollectionJob, error) } -type LogsLister interface { - ListLogJobs(string, *om.LogListOptions) (*om.LogCollectionJobs, error) +type LogJobLister interface { + LogCollectionJobs(string, *om.LogListOptions) (*om.LogCollectionJobs, error) } -// ListLogJobs encapsulate the logic to manage different cloud providers -func (s *Store) ListLogJobs(groupID string, opts *om.LogListOptions) (*om.LogCollectionJobs, error) { +// LogCollectionJobs encapsulate the logic to manage different cloud providers +func (s *Store) LogCollectionJobs(groupID string, opts *om.LogListOptions) (*om.LogCollectionJobs, error) { switch s.service { case config.OpsManagerService, config.CloudManagerService: log, _, err := s.client.(*om.Client).LogCollections.List(context.Background(), groupID, opts) From d15a47b03881babc78534459fbbb52b0258123e0 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 18:08:31 +0100 Subject: [PATCH 12/15] CLOUDP-60956: mongocli om logs download --- internal/cli/ops_manager_logs.go | 1 + internal/cli/ops_manager_logs_download.go | 40 +++++++++------ .../cli/ops_manager_logs_download_test.go | 50 +++++++++++++++++++ internal/description/description.go | 2 +- internal/flags/flags.go | 4 +- internal/mocks/mock_logs.go | 37 ++++++++++++++ internal/store/logs.go | 15 ++++++ 7 files changed, 131 insertions(+), 18 deletions(-) diff --git a/internal/cli/ops_manager_logs.go b/internal/cli/ops_manager_logs.go index 334667af7f..8a0a3e3018 100644 --- a/internal/cli/ops_manager_logs.go +++ b/internal/cli/ops_manager_logs.go @@ -28,6 +28,7 @@ func OpsManagerLogsBuilder() *cobra.Command { cmd.AddCommand(OpsManagerLogsCollectOptsBuilder()) cmd.AddCommand(OpsManagerLogsListOptsBuilder()) + cmd.AddCommand(OpsManagerLogsDownloadOptsBuilder()) return cmd } diff --git a/internal/cli/ops_manager_logs_download.go b/internal/cli/ops_manager_logs_download.go index 64947f601f..083e4d4637 100644 --- a/internal/cli/ops_manager_logs_download.go +++ b/internal/cli/ops_manager_logs_download.go @@ -15,20 +15,23 @@ package cli import ( - om "github.com/mongodb/go-client-mongodb-ops-manager/opsmngr" + "io" + "os" + "github.com/mongodb/mongocli/internal/description" "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/afero" "github.com/spf13/cobra" ) type opsManagerLogsDownloadOpts struct { globalOpts - id string - filePath string - store store.LogsLister + id string + out string + fs afero.Fs + store store.LogJobsDownloader } func (opts *opsManagerLogsDownloadOpts) initStore() error { @@ -38,23 +41,31 @@ func (opts *opsManagerLogsDownloadOpts) initStore() error { } func (opts *opsManagerLogsDownloadOpts) Run() error { - result, err := opts.store.ListLogJobs(opts.ProjectID(), opts.newLogListOptions()) + out, err := opts.newWriteCloser() if err != nil { return err } - return json.PrettyPrint(result) + + if err := opts.store.DownloadLogJob(opts.ProjectID(), opts.id, out); err != nil { + return err + } + + return nil } -func (opts *opsManagerLogsDownloadOpts) newLogListOptions() *om.LogListOptions { - return &om.LogListOptions{Verbose: opts.Verbose} +func (opts *opsManagerLogsDownloadOpts) newWriteCloser() (io.WriteCloser, error) { + // Create file only if is not there already (don't overwrite) + ff := os.O_CREATE | os.O_TRUNC | os.O_WRONLY | os.O_EXCL + f, err := opts.fs.OpenFile(opts.out, ff, 0777) + return f, err } -// mongocli om logs download id [--filePath filePath] [--projectId projectId] +// mongocli om logs download id [--out out] [--projectId projectId] func OpsManagerLogsDownloadOptsBuilder() *cobra.Command { opts := &opsManagerLogsDownloadOpts{} cmd := &cobra.Command{ - Use: "download", - Short: description.DownloadLogs, + Use: "download [id]", + Short: description.DownloadLogs, PreRunE: func(cmd *cobra.Command, args []string) error { return opts.PreRunE(opts.initStore) }, @@ -64,10 +75,11 @@ func OpsManagerLogsDownloadOptsBuilder() *cobra.Command { }, } - cmd.Flags().StringVar(&opts.id, flags.ID, "", usage.Verbose) - cmd.Flags().StringVar(&opts.filePath, flags.FilePath, "", usage.Verbose) + cmd.Flags().StringVarP(&opts.out, flags.Out, flags.OutShort, "", usage.LogOut) cmd.Flags().StringVar(&opts.projectID, flags.ProjectID, "", usage.ProjectID) + _ = cmd.MarkFlagRequired(flags.Out) + return cmd } diff --git a/internal/cli/ops_manager_logs_download_test.go b/internal/cli/ops_manager_logs_download_test.go index 7f1e458cd3..50aec00aea 100644 --- a/internal/cli/ops_manager_logs_download_test.go +++ b/internal/cli/ops_manager_logs_download_test.go @@ -1 +1,51 @@ +// 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/mocks" + "github.com/spf13/afero" +) + +func TestOpsManagerLogsDownloadOpts_Run(t *testing.T) { + ctrl := gomock.NewController(t) + mockStore := mocks.NewMockLogJobsDownloader(ctrl) + + appFS := afero.NewMemMapFs() + + opts := &opsManagerLogsDownloadOpts{ + id: "1", + fs: appFS, + store: mockStore, + } + + f, err := opts.newWriteCloser() + if err != nil { + t.Fatalf("newWriteCloser() unexpected error: %v", err) + } + + mockStore. + EXPECT(). + DownloadLogJob(opts.projectID, opts.id, f). + Return(nil). + Times(1) + + if err := opts.Run(); err != nil { + t.Fatalf("Run() unexpected error: %v", err) + } +} diff --git a/internal/description/description.go b/internal/description/description.go index f0a52bed03..c92c215f0a 100644 --- a/internal/description/description.go +++ b/internal/description/description.go @@ -94,7 +94,7 @@ A user’s roles apply to all the clusters in the project.` LogCollection = "Manage log collection jobs." StartLogCollectionJob = "Start a job to collect logs." ListLogCollectionJobs = "List log collection jobs." - DownloadLogs = "Download logs from a log collection job." + DownloadLogs = "Download logs from a log collection job." Owner = "Manage Ops Manager owners." CreateOwner = "Create the first user for Ops Manager." Servers = "Manage Ops Manager servers." diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 3a807e47e0..839f9971e7 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -116,7 +116,5 @@ const ( Strength = "strength" // Strength flag SizeRequestedPerFileBytes = "sizeRequestedPerFileBytes" //SizeRequestedPerFileBytes flag Redacted = "redacted" // Redacted flag - Verbose = "verbose" - ID = "id" //ID flag - FilePath ="filePath" // FilePath flag + Verbose = "verbose" // Verbose flag ) diff --git a/internal/mocks/mock_logs.go b/internal/mocks/mock_logs.go index 62aef1d7a7..82002952a9 100644 --- a/internal/mocks/mock_logs.go +++ b/internal/mocks/mock_logs.go @@ -49,6 +49,43 @@ func (mr *MockLogsDownloaderMockRecorder) DownloadLog(arg0, arg1, arg2, arg3, ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DownloadLog", reflect.TypeOf((*MockLogsDownloader)(nil).DownloadLog), arg0, arg1, arg2, arg3, arg4) } +// MockLogJobsDownloader is a mock of LogJobsDownloader interface +type MockLogJobsDownloader struct { + ctrl *gomock.Controller + recorder *MockLogJobsDownloaderMockRecorder +} + +// MockLogJobsDownloaderMockRecorder is the mock recorder for MockLogJobsDownloader +type MockLogJobsDownloaderMockRecorder struct { + mock *MockLogJobsDownloader +} + +// NewMockLogJobsDownloader creates a new mock instance +func NewMockLogJobsDownloader(ctrl *gomock.Controller) *MockLogJobsDownloader { + mock := &MockLogJobsDownloader{ctrl: ctrl} + mock.recorder = &MockLogJobsDownloaderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockLogJobsDownloader) EXPECT() *MockLogJobsDownloaderMockRecorder { + return m.recorder +} + +// DownloadLogJob mocks base method +func (m *MockLogJobsDownloader) DownloadLogJob(arg0, arg1 string, arg2 io.Writer) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DownloadLogJob", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// DownloadLogJob indicates an expected call of DownloadLogJob +func (mr *MockLogJobsDownloaderMockRecorder) DownloadLogJob(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DownloadLogJob", reflect.TypeOf((*MockLogJobsDownloader)(nil).DownloadLogJob), arg0, arg1, arg2) +} + // MockLogCollector is a mock of LogCollector interface type MockLogCollector struct { ctrl *gomock.Controller diff --git a/internal/store/logs.go b/internal/store/logs.go index 0568bc0e35..20c922a766 100644 --- a/internal/store/logs.go +++ b/internal/store/logs.go @@ -28,6 +28,10 @@ type LogsDownloader interface { DownloadLog(string, string, string, io.Writer, *atlas.DateRangetOptions) error } +type LogJobsDownloader interface { + DownloadLogJob(string, string, io.Writer) error +} + type LogCollector interface { Collect(string, *om.LogCollectionJob) (*om.LogCollectionJob, error) } @@ -68,3 +72,14 @@ func (s *Store) DownloadLog(groupID, host, name string, out io.Writer, opts *atl return fmt.Errorf("unsupported service: %s", s.service) } } + +// DownloadLogJob encapsulate the logic to manage different cloud providers +func (s *Store) DownloadLogJob(groupID, jobID string, out io.Writer) error { + switch s.service { + case config.OpsManagerService, config.CloudManagerService: + _, err := s.client.(*om.Client).Logs.Download(context.Background(), groupID, jobID, out) + return err + default: + return fmt.Errorf("unsupported service: %s", s.service) + } +} From 5f8cbc7ba29fbfea6d96e3079baf3a6d4e21a740 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 18:12:10 +0100 Subject: [PATCH 13/15] Added description --- internal/description/description.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/description/description.go b/internal/description/description.go index 78c82575d7..475015f0e5 100644 --- a/internal/description/description.go +++ b/internal/description/description.go @@ -84,6 +84,7 @@ const ( CreateProject = "Create a project." DeleteProject = "Delete a project." ListProjects = "List projects." + DownloadLogs = "Download logs from a log collection job." OpsManager = "Ops Manager operations." ListGlobalAlerts = "List global alerts." Automation = "Manage Ops Manager automation config." From 0cbf4ffabb573b687962a5abdffbc1c249c3e6a3 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 18:18:45 +0100 Subject: [PATCH 14/15] Create fs inside opsManagerLogsDownloadOpts --- internal/cli/ops_manager_logs_download.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/cli/ops_manager_logs_download.go b/internal/cli/ops_manager_logs_download.go index 083e4d4637..6120d820d5 100644 --- a/internal/cli/ops_manager_logs_download.go +++ b/internal/cli/ops_manager_logs_download.go @@ -62,7 +62,9 @@ func (opts *opsManagerLogsDownloadOpts) newWriteCloser() (io.WriteCloser, error) // mongocli om logs download id [--out out] [--projectId projectId] func OpsManagerLogsDownloadOptsBuilder() *cobra.Command { - opts := &opsManagerLogsDownloadOpts{} + opts := &opsManagerLogsDownloadOpts{ + fs: afero.NewOsFs(), + } cmd := &cobra.Command{ Use: "download [id]", Short: description.DownloadLogs, From b4e699c741fdfe8a9d6f96044b7da06129de0a14 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 24 Apr 2020 18:21:55 +0100 Subject: [PATCH 15/15] Refactoring --- internal/cli/atlas_logs_download.go | 7 +++---- internal/cli/ops_manager_logs_download.go | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/cli/atlas_logs_download.go b/internal/cli/atlas_logs_download.go index f2cf20e896..17078e0f8e 100644 --- a/internal/cli/atlas_logs_download.go +++ b/internal/cli/atlas_logs_download.go @@ -85,10 +85,9 @@ func AtlasLogsDownloadBuilder() *cobra.Command { fs: afero.NewOsFs(), } cmd := &cobra.Command{ - Use: "download [hostname] [logname]", - Short: description.ListDisks, - Aliases: []string{"ls"}, - Args: cobra.ExactArgs(2), + Use: "download [hostname] [logname]", + Short: description.ListDisks, + Args: cobra.ExactArgs(2), PreRunE: func(cmd *cobra.Command, args []string) error { return opts.PreRunE(opts.initStore) }, diff --git a/internal/cli/ops_manager_logs_download.go b/internal/cli/ops_manager_logs_download.go index 6120d820d5..83ffa452a8 100644 --- a/internal/cli/ops_manager_logs_download.go +++ b/internal/cli/ops_manager_logs_download.go @@ -68,6 +68,7 @@ func OpsManagerLogsDownloadOptsBuilder() *cobra.Command { cmd := &cobra.Command{ Use: "download [id]", Short: description.DownloadLogs, + Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { return opts.PreRunE(opts.initStore) },