Skip to content

CLOUDP-57838: Create a rolling index, Atlas #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 9, 2020
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ gen-mocks: ## Generate mocks
mockgen -source=internal/store/global_alerts.go -destination=internal/mocks/mock_global_alerts.go -package=mocks
mockgen -source=internal/store/events.go -destination=internal/mocks/mock_events.go -package=mocks
mockgen -source=internal/store/process_measurements.go -destination=internal/mocks/mock_process_measurements.go -package=mocks
mockgen -source=internal/store/indexes.go -destination=internal/mocks/mock_indexes.go -package=mocks

.PHONY: build
build: ## Generate a binary in ./bin
Expand Down
19 changes: 19 additions & 0 deletions e2e/atlas_clusters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,25 @@ func TestAtlasClusters(t *testing.T) {
ensureCluster(t, cluster, clusterName, "4.2", 20)
})

// TODO: this fails as the cluster is not healthy we may need to re think how we test this
//t.Run("Create Index", func(t *testing.T) {
// cmd := exec.Command(cliPath,
// atlasEntity,
// clustersEntity,
// "indexes",
// "create",
// "--clusterName="+clusterName,
// "--db=tes",
// "--collection=tes",
// "--key=name:1")
// cmd.Env = os.Environ()
// resp, err := cmd.CombinedOutput()
//
// if err != nil {
// t.Fatalf("unexpected error: %v, resp: %v", err, string(resp))
// }
//})

t.Run("Delete", func(t *testing.T) {
cmd := exec.Command(cliPath, atlasEntity, clustersEntity, "delete", clusterName, "--force")
cmd.Env = os.Environ()
Expand Down
1 change: 1 addition & 0 deletions internal/cli/atlas_clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func AtlasClustersBuilder() *cobra.Command {
cmd.AddCommand(AtlasClustersDescribeBuilder())
cmd.AddCommand(AtlasClustersDeleteBuilder())
cmd.AddCommand(AtlasClustersUpdateBuilder())
cmd.AddCommand(AtlasClustersIndexesBuilder())

return cmd
}
32 changes: 32 additions & 0 deletions internal/cli/atlas_clusters_indexes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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 AtlasClustersIndexesBuilder() *cobra.Command {
cmd := &cobra.Command{
Use: "indexes",
Aliases: []string{"index"},
Short: description.Clusters,
Long: description.ClustersLong,
}
cmd.AddCommand(AtlasClustersIndexesCreateBuilder())

return cmd
}
136 changes: 136 additions & 0 deletions internal/cli/atlas_clusters_indexes_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// 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 (
"fmt"
"strings"

atlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas"
"github.com/mongodb/mongocli/internal/description"
"github.com/mongodb/mongocli/internal/flags"
"github.com/mongodb/mongocli/internal/store"
"github.com/mongodb/mongocli/internal/usage"
"github.com/spf13/cobra"
)

type atlasClustersIndexesCreateOpts struct {
*globalOpts
clusterName string
name string
db string
collection string
keys []string
unique bool
sparse bool
background bool
store store.IndexCreator
}

func (opts *atlasClustersIndexesCreateOpts) init() error {
if opts.ProjectID() == "" {
return errMissingProjectID
}

var err error
opts.store, err = store.New()
return err
}

func (opts *atlasClustersIndexesCreateOpts) Run() error {
req, err := opts.newIndex()
if err != nil {
return err
}
if err := opts.store.CreateIndex(opts.ProjectID(), opts.clusterName, req); err != nil {
return err
}
fmt.Println("Your index is being created")
return nil
}

func (opts *atlasClustersIndexesCreateOpts) newIndex() (*atlas.IndexConfiguration, error) {
keys, err := opts.indexKeys()
if err != nil {
return nil, err
}
i := new(atlas.IndexConfiguration)
i.DB = opts.db
i.Collection = opts.collection
i.Keys = keys
i.Options = opts.newIndexOptions()
return i, nil
}

func (opts *atlasClustersIndexesCreateOpts) newIndexOptions() *atlas.IndexOptions {
return &atlas.IndexOptions{
Background: opts.background,
Unique: opts.unique,
Sparse: opts.sparse,
Name: opts.name,
}
}

func (opts *atlasClustersIndexesCreateOpts) indexKeys() ([]map[string]string, error) {
keys := make([]map[string]string, len(opts.keys))
for i, key := range opts.keys {
value := strings.Split(key, ":")
if len(value) != 2 {
return nil, fmt.Errorf("unexpected key format: %s", key)
}
keys[i] = map[string]string{value[0]: value[1]}
}

return keys, nil
}

// AtlasClustersIndexesCreateBuilder builds a cobra.Command that can run as:
// mcli atlas clusters index create [name] --clusterName clusterName --collectionName collectionName --dbName dbName [--key field:type]
func AtlasClustersIndexesCreateBuilder() *cobra.Command {
opts := &atlasClustersIndexesCreateOpts{
globalOpts: newGlobalOpts(),
}
cmd := &cobra.Command{
Use: "create [name]",
Short: description.CreateCluster,
Args: cobra.MaximumNArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
return opts.init()
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 1 {
opts.name = args[0]
}
return opts.Run()
},
}

cmd.Flags().StringVar(&opts.clusterName, flags.ClusterName, "", usage.ClusterName)
cmd.Flags().StringVar(&opts.db, flags.Database, "", usage.Database)
cmd.Flags().StringVar(&opts.collection, flags.Collection, "", usage.Collection)
cmd.Flags().StringArrayVar(&opts.keys, flags.Key, nil, usage.Key)
cmd.Flags().BoolVar(&opts.unique, flags.Unique, false, usage.Unique)
cmd.Flags().BoolVar(&opts.sparse, flags.Sparse, false, usage.Sparse)
cmd.Flags().BoolVar(&opts.background, flags.Background, false, usage.Background)

cmd.Flags().StringVar(&opts.projectID, flags.ProjectID, "", usage.ProjectID)

_ = cmd.MarkFlagRequired(flags.ClusterName)
_ = cmd.MarkFlagRequired(flags.Database)
_ = cmd.MarkFlagRequired(flags.Collection)
_ = cmd.MarkFlagRequired(flags.Key)

return cmd
}
50 changes: 50 additions & 0 deletions internal/cli/atlas_clusters_indexes_create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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"
)

func TestAtlasClustersIndexesCreate_Run(t *testing.T) {
ctrl := gomock.NewController(t)
mockStore := mocks.NewMockIndexCreator(ctrl)

defer ctrl.Finish()

createOpts := &atlasClustersIndexesCreateOpts{
globalOpts: newGlobalOpts(),
name: "ProjectBar",
clusterName: "US",
db: "test",
collection: "test",
keys: []string{"name:1"},
store: mockStore,
}

index, _ := createOpts.newIndex()
mockStore.
EXPECT().
CreateIndex(createOpts.projectID, createOpts.clusterName, index).Return(nil).
Times(1)

err := createOpts.Run()
if err != nil {
t.Fatalf("Run() unexpected error: %v", err)
}
}
6 changes: 6 additions & 0 deletions internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,10 @@ const (
MaxDate = "maxDate" // MaxDate flag
MinDate = "minDate" // MinDate flag
Granularity = "granularity" // Granularity flag
Key = "key" // Key flag
Collection = "collection" // Collection flag
Database = "db" // Database flag
Unique = "unique" // Unique flag
Sparse = "sparse" // Sparse flag
Background = "background" // Background flag
)
48 changes: 48 additions & 0 deletions internal/mocks/mock_indexes.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions internal/store/indexes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package store

import (
"context"
"fmt"

atlas "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas"
"github.com/mongodb/mongocli/internal/config"
)

type IndexCreator interface {
CreateIndex(string, string, *atlas.IndexConfiguration) error
}

// CreateIndex encapsulate the logic to manage different cloud providers
func (s *Store) CreateIndex(projectID, clusterName string, index *atlas.IndexConfiguration) error {
switch s.service {
case config.CloudService:
_, err := s.client.(*atlas.Client).Indexes.Create(context.Background(), projectID, clusterName, index)
return err
default:
return fmt.Errorf("unsupported service: %s", s.service)
}
}
10 changes: 8 additions & 2 deletions internal/usage/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,14 @@ const (
NotificationUsername = "Name of the Atlas user to which to send notifications."
NotificationVictorOpsRoutingKey = "VictorOps routing key."
SnapshotID = "Unique identifier of the snapshot to restore."
ClusterName = "Name of the cluster that contains the snapshots that you want to retrieve."
ClusterID = "Unique identifier of the cluster that the job represents."
Database = "Database name."
Collection = "Collection name."
Key = "Index keys. Should be formatted as field:type."
Unique = "Create a unique key index."
Sparse = "Create a sparse index."
ClusterName = "Name of the cluster."
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."
TargetClusterID = `Unique identifier of the target cluster.
For use only with automated restore jobs.`
Expand Down