From 506d40f884e63ebd5b303d2575933a0ec2989cf7 Mon Sep 17 00:00:00 2001 From: Jason Rydberg Date: Tue, 18 Feb 2025 12:49:11 -0600 Subject: [PATCH 1/2] CLOUDP-291622: Add delete command for Atlas Stream Processing PrivateLinks --- .../atlas-streams-privateLinks-delete.txt | 103 ++++++++++++++++++ docs/command/atlas-streams-privateLinks.txt | 2 + internal/cli/streams/privatelink/delete.go | 90 +++++++++++++++ .../cli/streams/privatelink/delete_test.go | 75 +++++++++++++ .../cli/streams/privatelink/privatelink.go | 1 + internal/mocks/mock_streams.go | 39 ++++++- internal/store/streams.go | 11 +- test/README.md | 1 + test/e2e/atlas/streams_test.go | 19 ++++ 9 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 docs/command/atlas-streams-privateLinks-delete.txt create mode 100644 internal/cli/streams/privatelink/delete.go create mode 100644 internal/cli/streams/privatelink/delete_test.go diff --git a/docs/command/atlas-streams-privateLinks-delete.txt b/docs/command/atlas-streams-privateLinks-delete.txt new file mode 100644 index 0000000000..9d7ca42f84 --- /dev/null +++ b/docs/command/atlas-streams-privateLinks-delete.txt @@ -0,0 +1,103 @@ +.. _atlas-streams-privateLinks-delete: + +================================= +atlas streams privateLinks delete +================================= + +.. default-domain:: mongodb + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Deletes an Atlas Stream Processing PrivateLink endpoint. + +To use this command, you must authenticate with a user account or an API key with any of the following roles: Project Owner, Project Stream Processing Owner. + +Syntax +------ + +.. code-block:: + :caption: Command Syntax + + atlas streams privateLinks delete [options] + +.. Code end marker, please don't delete this comment + +Arguments +--------- + +.. list-table:: + :header-rows: 1 + :widths: 20 10 10 60 + + * - Name + - Type + - Required + - Description + * - connectionID + - string + - true + - ID of the PrivateLink endpoint. + +Options +------- + +.. list-table:: + :header-rows: 1 + :widths: 20 10 10 60 + + * - Name + - Type + - Required + - Description + * - --force + - + - false + - Flag that indicates whether to skip the confirmation prompt before proceeding with the requested action. + * - -h, --help + - + - false + - help for delete + * - --projectId + - string + - false + - Hexadecimal string that identifies the project to use. This option overrides the settings in the configuration file or environment variable. + +Inherited Options +----------------- + +.. list-table:: + :header-rows: 1 + :widths: 20 10 10 60 + + * - Name + - Type + - Required + - Description + * - -P, --profile + - string + - false + - Name of the profile to use from your configuration file. To learn about profiles for the Atlas CLI, see https://dochub.mongodb.org/core/atlas-cli-save-connection-settings. + +Output +------ + +If the command succeeds, the CLI returns output similar to the following sample. Values in brackets represent your values. + +.. code-block:: + + Atlas Stream Processing PrivateLink endpoint '' deleted. + + +Examples +-------- + +.. code-block:: + :copyable: false + + # delete an Atlas Stream Processing PrivateLink endpoint: + atlas streams privateLink delete 5e2211c17a3e5a48f5497de3 + diff --git a/docs/command/atlas-streams-privateLinks.txt b/docs/command/atlas-streams-privateLinks.txt index 21a0c1c18d..47740b5d20 100644 --- a/docs/command/atlas-streams-privateLinks.txt +++ b/docs/command/atlas-streams-privateLinks.txt @@ -52,6 +52,7 @@ Related Commands ---------------- * :ref:`atlas-streams-privateLinks-create` - Creates a PrivateLink endpoint that can be used as an Atlas Stream Processor connection. +* :ref:`atlas-streams-privateLinks-delete` - Deletes an Atlas Stream Processing PrivateLink endpoint. * :ref:`atlas-streams-privateLinks-describe` - Describes a PrivateLink endpoint that can be used as an Atlas Stream Processor connection. * :ref:`atlas-streams-privateLinks-list` - Lists the PrivateLink endpoints in the project that can be used as Atlas Stream Processor connections. @@ -60,6 +61,7 @@ Related Commands :titlesonly: create + delete describe list diff --git a/internal/cli/streams/privatelink/delete.go b/internal/cli/streams/privatelink/delete.go new file mode 100644 index 0000000000..71287048e4 --- /dev/null +++ b/internal/cli/streams/privatelink/delete.go @@ -0,0 +1,90 @@ +// Copyright 2025 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 privatelink + +import ( + "context" + "fmt" + + "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli" + "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/require" + "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/config" + "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/flag" + "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store" + "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/usage" + "github.com/spf13/cobra" +) + +var successDeleteTemplate = "Atlas Stream Processing PrivateLink endpoint '%s' deleted.\n" +var failDeleteTemplate = "Atlas Stream Processing PrivateLink endpoint not deleted" + +type DeleteOpts struct { + cli.ProjectOpts + *cli.DeleteOpts + store store.PrivateLinkDeleter +} + +func (opts *DeleteOpts) Run() error { + return opts.Delete(opts.store.DeletePrivateLinkEndpoint, opts.ConfigProjectID()) +} + +func (opts *DeleteOpts) initStore(ctx context.Context) func() error { + return func() error { + var err error + opts.store, err = store.New(store.AuthenticatedPreset(config.Default()), store.WithContext(ctx)) + return err + } +} + +// atlas streams privateLink deleted +// Deletes a PrivateLink endpoint. +func DeleteBuilder() *cobra.Command { + opts := &DeleteOpts{ + DeleteOpts: cli.NewDeleteOpts(successDeleteTemplate, failDeleteTemplate), + } + cmd := &cobra.Command{ + Use: "delete ", + Aliases: []string{"rm"}, + Short: "Deletes an Atlas Stream Processing PrivateLink endpoint.", + Long: fmt.Sprintf(usage.RequiredOneOfRoles, commandRoles), + Args: require.ExactArgs(1), + Annotations: map[string]string{ + "connectionIDDesc": "ID of the PrivateLink endpoint.", + "output": successDeleteTemplate, + }, + Example: `# delete an Atlas Stream Processing PrivateLink endpoint: + atlas streams privateLink delete 5e2211c17a3e5a48f5497de3 +`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := opts.PreRunE( + opts.ValidateProjectID, + opts.initStore(cmd.Context()), + ); err != nil { + return err + } + opts.Entry = args[0] + return opts.Prompt() + }, + RunE: func(_ *cobra.Command, _ []string) error { + return opts.Run() + }, + } + + cmd.Flags().BoolVar(&opts.Confirm, flag.Force, false, usage.Force) + + opts.AddProjectOptsFlags(cmd) + + return cmd +} diff --git a/internal/cli/streams/privatelink/delete_test.go b/internal/cli/streams/privatelink/delete_test.go new file mode 100644 index 0000000000..2bd0e0319f --- /dev/null +++ b/internal/cli/streams/privatelink/delete_test.go @@ -0,0 +1,75 @@ +// Copyright 2025 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 privatelink + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli" + "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/mocks" + "github.com/stretchr/testify/require" +) + +func TestDeleteOpts_Run(t *testing.T) { + t.Run("should call the store delete privateLink method with the correct parameters", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockStore := mocks.NewMockPrivateLinkDeleter(ctrl) + + const projectID = "a-project-id" + const connectionID = "the-connection-id" + + deleteOpts := &DeleteOpts{ + store: mockStore, + ProjectOpts: cli.ProjectOpts{ + ProjectID: projectID, + }, + DeleteOpts: &cli.DeleteOpts{ + Confirm: true, + Entry: connectionID, + }, + } + + mockStore. + EXPECT(). + DeletePrivateLinkEndpoint(gomock.Eq(projectID), gomock.Eq(connectionID)). + Times(1) + + require.NoError(t, deleteOpts.Run()) + }) + + t.Run("should delete without error", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockStore := mocks.NewMockPrivateLinkDeleter(ctrl) + + deleteOpts := &DeleteOpts{ + DeleteOpts: &cli.DeleteOpts{ + Entry: "some-connection-id", + Confirm: true, + }, + store: mockStore, + } + + mockStore. + EXPECT(). + DeletePrivateLinkEndpoint(gomock.Any(), gomock.Any()). + Return(nil). + Times(1) + + err := deleteOpts.Run() + + require.NoError(t, err) + }) +} diff --git a/internal/cli/streams/privatelink/privatelink.go b/internal/cli/streams/privatelink/privatelink.go index 421ab9d8c9..b11534844d 100644 --- a/internal/cli/streams/privatelink/privatelink.go +++ b/internal/cli/streams/privatelink/privatelink.go @@ -36,6 +36,7 @@ func Builder() *cobra.Command { CreateBuilder(), ListBuilder(), DescribeBuilder(), + DeleteBuilder(), ) return cmd diff --git a/internal/mocks/mock_streams.go b/internal/mocks/mock_streams.go index 40f9025669..fc663942ec 100644 --- a/internal/mocks/mock_streams.go +++ b/internal/mocks/mock_streams.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store (interfaces: StreamsLister,StreamsDescriber,StreamsCreator,StreamsDeleter,StreamsUpdater,StreamsDownloader,ConnectionCreator,ConnectionDeleter,ConnectionUpdater,StreamsConnectionDescriber,StreamsConnectionLister,PrivateLinkCreator,PrivateLinkLister,PrivateLinkDescriber) +// Source: github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store (interfaces: StreamsLister,StreamsDescriber,StreamsCreator,StreamsDeleter,StreamsUpdater,StreamsDownloader,ConnectionCreator,ConnectionDeleter,ConnectionUpdater,StreamsConnectionDescriber,StreamsConnectionLister,PrivateLinkCreator,PrivateLinkLister,PrivateLinkDescriber,PrivateLinkDeleter) // Package mocks is a generated GoMock package. package mocks @@ -541,3 +541,40 @@ func (mr *MockPrivateLinkDescriberMockRecorder) DescribePrivateLinkEndpoint(arg0 mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribePrivateLinkEndpoint", reflect.TypeOf((*MockPrivateLinkDescriber)(nil).DescribePrivateLinkEndpoint), arg0, arg1) } + +// MockPrivateLinkDeleter is a mock of PrivateLinkDeleter interface. +type MockPrivateLinkDeleter struct { + ctrl *gomock.Controller + recorder *MockPrivateLinkDeleterMockRecorder +} + +// MockPrivateLinkDeleterMockRecorder is the mock recorder for MockPrivateLinkDeleter. +type MockPrivateLinkDeleterMockRecorder struct { + mock *MockPrivateLinkDeleter +} + +// NewMockPrivateLinkDeleter creates a new mock instance. +func NewMockPrivateLinkDeleter(ctrl *gomock.Controller) *MockPrivateLinkDeleter { + mock := &MockPrivateLinkDeleter{ctrl: ctrl} + mock.recorder = &MockPrivateLinkDeleterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPrivateLinkDeleter) EXPECT() *MockPrivateLinkDeleterMockRecorder { + return m.recorder +} + +// DeletePrivateLinkEndpoint mocks base method. +func (m *MockPrivateLinkDeleter) DeletePrivateLinkEndpoint(arg0, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeletePrivateLinkEndpoint", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeletePrivateLinkEndpoint indicates an expected call of DeletePrivateLinkEndpoint. +func (mr *MockPrivateLinkDeleterMockRecorder) DeletePrivateLinkEndpoint(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePrivateLinkEndpoint", reflect.TypeOf((*MockPrivateLinkDeleter)(nil).DeletePrivateLinkEndpoint), arg0, arg1) +} diff --git a/internal/store/streams.go b/internal/store/streams.go index cdfe5fdda9..a54a2c041f 100644 --- a/internal/store/streams.go +++ b/internal/store/streams.go @@ -21,7 +21,7 @@ import ( atlasv2 "go.mongodb.org/atlas-sdk/v20241113005/admin" ) -//go:generate mockgen -destination=../mocks/mock_streams.go -package=mocks github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store StreamsLister,StreamsDescriber,StreamsCreator,StreamsDeleter,StreamsUpdater,StreamsDownloader,ConnectionCreator,ConnectionDeleter,ConnectionUpdater,StreamsConnectionDescriber,StreamsConnectionLister,PrivateLinkCreator,PrivateLinkLister,PrivateLinkDescriber +//go:generate mockgen -destination=../mocks/mock_streams.go -package=mocks github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store StreamsLister,StreamsDescriber,StreamsCreator,StreamsDeleter,StreamsUpdater,StreamsDownloader,ConnectionCreator,ConnectionDeleter,ConnectionUpdater,StreamsConnectionDescriber,StreamsConnectionLister,PrivateLinkCreator,PrivateLinkLister,PrivateLinkDescriber,PrivateLinkDeleter type StreamsLister interface { ProjectStreams(*atlasv2.ListStreamInstancesApiParams) (*atlasv2.PaginatedApiStreamsTenant, error) @@ -79,6 +79,10 @@ type PrivateLinkDescriber interface { DescribePrivateLinkEndpoint(projectID, connectionID string) (*atlasv2.StreamsPrivateLinkConnection, error) } +type PrivateLinkDeleter interface { + DeletePrivateLinkEndpoint(projectID, connectionID string) error +} + func (s *Store) ProjectStreams(opts *atlasv2.ListStreamInstancesApiParams) (*atlasv2.PaginatedApiStreamsTenant, error) { result, _, err := s.clientv2.StreamsApi.ListStreamInstancesWithParams(s.ctx, opts).Execute() return result, err @@ -159,3 +163,8 @@ func (s *Store) DescribePrivateLinkEndpoint(projectID, connectionID string) (*at result, _, err := s.clientv2.StreamsApi.GetPrivateLinkConnection(s.ctx, projectID, connectionID).Execute() return result, err } + +func (s *Store) DeletePrivateLinkEndpoint(projectID, connectionID string) error { + _, _, err := s.clientv2.StreamsApi.DeletePrivateLinkConnection(s.ctx, projectID, connectionID).Execute() + return err +} diff --git a/test/README.md b/test/README.md index ba7e35fbba..4ac78de2fc 100644 --- a/test/README.md +++ b/test/README.md @@ -197,6 +197,7 @@ | `streams privateLink create` | Y | Y | | `streams privateLink list` | Y | Y | | `streams privateLink describe` | Y | Y | +| `streams privateLink delete` | Y | Y | | `config` | | | | `completion` | Y | Y | | `config delete` | Y | Y | diff --git a/test/e2e/atlas/streams_test.go b/test/e2e/atlas/streams_test.go index ea75b0f360..b7b262e24d 100644 --- a/test/e2e/atlas/streams_test.go +++ b/test/e2e/atlas/streams_test.go @@ -277,6 +277,25 @@ func TestStreams(t *testing.T) { a.Equal("test-namespace.servicebus.windows.net", privateLinkEndpoints.GetResults()[0].GetDnsDomain()) }) + t.Run("Deleting a streams privateLink endpoint", func(t *testing.T) { + streamsCmd := exec.Command(cliPath, + "streams", + "privateLink", + "delete", + endpointID, + "--force", + "--projectId", + g.projectID, + ) + + streamsCmd.Env = os.Environ() + resp, err := e2e.RunAndGetStdOut(streamsCmd) + require.NoError(t, err, string(resp)) + + expected := fmt.Sprintf("Atlas Stream Processing PrivateLink endpoint '%s' deleted.\n", endpointID) + assert.Equal(t, expected, string(resp)) + }) + // Connections t.Run("Creating a streams connection", func(t *testing.T) { cmd := exec.Command(cliPath, From e6d8c11ec01dc2a16ac6ae6bf46efa4bb9e1f091 Mon Sep 17 00:00:00 2001 From: Jason Rydberg Date: Wed, 19 Feb 2025 14:56:16 -0600 Subject: [PATCH 2/2] add a test with confirm=false --- .../cli/streams/privatelink/delete_test.go | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/internal/cli/streams/privatelink/delete_test.go b/internal/cli/streams/privatelink/delete_test.go index 2bd0e0319f..00d9412bf4 100644 --- a/internal/cli/streams/privatelink/delete_test.go +++ b/internal/cli/streams/privatelink/delete_test.go @@ -72,4 +72,26 @@ func TestDeleteOpts_Run(t *testing.T) { require.NoError(t, err) }) + + t.Run("should not call delete if confirm is false", func(t *testing.T) { + ctrl := gomock.NewController(t) + mockStore := mocks.NewMockPrivateLinkDeleter(ctrl) + + deleteOpts := &DeleteOpts{ + DeleteOpts: &cli.DeleteOpts{ + Entry: "another-connection-id", + Confirm: false, + }, + store: mockStore, + } + + mockStore. + EXPECT(). + DeletePrivateLinkEndpoint(gomock.Any(), gomock.Any()). + Times(0) + + err := deleteOpts.Run() + + require.NoError(t, err) + }) }