Skip to content

Commit

Permalink
CLOUDP-165325: Data Federation and private endpoint Atlas CLI Changes (
Browse files Browse the repository at this point in the history
…#2152)

Signed-off-by: Jose Vazquez <[email protected]>
Co-authored-by: Jose Vazquez <[email protected]>
Co-authored-by: Igor Karpukhin <[email protected]>
  • Loading branch information
3 people authored Sep 5, 2023
1 parent 10f4043 commit c74dbb4
Show file tree
Hide file tree
Showing 18 changed files with 1,711 additions and 18 deletions.
12 changes: 11 additions & 1 deletion docs/atlascli/command/atlas-kubernetes-config-generate.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ Options
- strings
- false
- One or more comma separated cluster names to import
* - --dataFederationName
- strings
- false
- One or more comma separated data federation names to import
* - -h, --help
-
- false
Expand Down Expand Up @@ -105,11 +109,17 @@ Examples

.. code-block::

# Export Project, DatabaseUsers, and specific Deployment resources for a specific project including connection and integration secrets to a specific namespace:
# Export Project, DatabaseUsers, DataFederations and specific Deployment resources for a specific project including connection and integration secrets to a specific namespace:
atlas kubernetes config generate --projectId=<projectId> --clusterName=<cluster-name-1, cluster-name-2> --includeSecrets --targetNamespace=<namespace>


.. code-block::

# Export resources for a specific version of the Atlas Kubernetes Operator:
atlas kubernetes config generate --projectId=<projectId> --targetNamespace=<namespace> --operatorVersion=1.5.1


.. code-block::

# Export Project, DatabaseUsers, Clusters and specific DataFederation resources for a specific project to a specific namespace:
atlas kubernetes config generate --projectId=<projectId> --dataFederationName=<data-federation-name-1, data-federation-name-2> --targetNamespace=<namespace>
24 changes: 15 additions & 9 deletions internal/cli/atlas/kubernetes/config/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ var ErrUnsupportedOperatorVersionFmt = "version %q is not supported. Supported v
type GenerateOpts struct {
cli.GlobalOpts
cli.OutputOpts
clusterName []string
includeSecrets bool
targetNamespace string
operatorVersion string
store store.AtlasOperatorGenericStore
credsStore store.CredentialsGetter
crdsProvider crds.AtlasOperatorCRDProvider
clusterName []string
dataFederationName []string
includeSecrets bool
targetNamespace string
operatorVersion string
store store.AtlasOperatorGenericStore
credsStore store.CredentialsGetter
crdsProvider crds.AtlasOperatorCRDProvider
}

func (opts *GenerateOpts) ValidateTargetNamespace() error {
Expand Down Expand Up @@ -87,6 +88,7 @@ func (opts *GenerateOpts) Run() error {
WithSecretsData(opts.includeSecrets).
WithTargetOperatorVersion(opts.operatorVersion).
WithFeatureValidator(featureValidator).
WithDataFederationNames(opts.dataFederationName).
Run()
if err != nil {
return err
Expand Down Expand Up @@ -115,11 +117,14 @@ func GenerateBuilder() *cobra.Command {
# Export Project, DatabaseUsers, Deployments resources for a specific project including connection and integration secrets to a specific namespace:
atlas kubernetes config generate --projectId=<projectId> --includeSecrets --targetNamespace=<namespace>
# Export Project, DatabaseUsers, and specific Deployment resources for a specific project including connection and integration secrets to a specific namespace:
# Export Project, DatabaseUsers, DataFederations and specific Deployment resources for a specific project including connection and integration secrets to a specific namespace:
atlas kubernetes config generate --projectId=<projectId> --clusterName=<cluster-name-1, cluster-name-2> --includeSecrets --targetNamespace=<namespace>
# Export resources for a specific version of the Atlas Kubernetes Operator:
atlas kubernetes config generate --projectId=<projectId> --targetNamespace=<namespace> --operatorVersion=1.5.1`,
atlas kubernetes config generate --projectId=<projectId> --targetNamespace=<namespace> --operatorVersion=1.5.1
# Export Project, DatabaseUsers, Clusters and specific DataFederation resources for a specific project to a specific namespace:
atlas kubernetes config generate --projectId=<projectId> --dataFederationName=<data-federation-name-1, data-federation-name-2> --targetNamespace=<namespace>`,
PreRunE: func(cmd *cobra.Command, args []string) error {
return opts.PreRunE(
opts.ValidateProjectID,
Expand All @@ -140,6 +145,7 @@ func GenerateBuilder() *cobra.Command {
cmd.Flags().BoolVar(&opts.includeSecrets, flag.OperatorIncludeSecrets, false, usage.OperatorIncludeSecrets)
cmd.Flags().StringVar(&opts.targetNamespace, flag.OperatorTargetNamespace, "", usage.OperatorTargetNamespace)
cmd.Flags().StringVar(&opts.operatorVersion, flag.OperatorVersion, features.LatestOperatorMajorVersion, usage.OperatorVersion)
cmd.Flags().StringSliceVar(&opts.dataFederationName, flag.DataFederationName, []string{}, usage.ExporterDataFederationName)

return cmd
}
1 change: 1 addition & 0 deletions internal/flag/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ const (
AWSRoleID = "awsRoleId" // AWSRoleID flag
AWSTestS3Bucket = "awsTestS3Bucket" // AWSTestS3Bucket flag
DataFederation = "dataFederation" // DataFederation flag
DataFederationName = "dataFederationName" // DataFederationName flag
Value = "value" // Value flag
OverrunPolicy = "overrunPolicy" // OverrunPolicy flag
AuthorizedEmail = "authorizedEmail" // authorizedEmail flag
Expand Down
62 changes: 56 additions & 6 deletions internal/kubernetes/operator/config_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"reflect"

"github.com/mongodb/mongodb-atlas-cli/internal/kubernetes/operator/datafederation"
"github.com/mongodb/mongodb-atlas-cli/internal/kubernetes/operator/dbusers"
"github.com/mongodb/mongodb-atlas-cli/internal/kubernetes/operator/deployment"
"github.com/mongodb/mongodb-atlas-cli/internal/kubernetes/operator/features"
Expand All @@ -44,12 +45,13 @@ type ConfigExporter struct {
dataProvider store.AtlasOperatorGenericStore
credsProvider store.CredentialsGetter
projectID string
clusters []string
clusterNames []string
targetNamespace string
operatorVersion string
includeSecretsData bool
orgID string
dictionaryForAtlasNames map[string]string
dataFederationNames []string
}

var (
Expand All @@ -62,7 +64,8 @@ func NewConfigExporter(dataProvider store.AtlasOperatorGenericStore, credsProvid
dataProvider: dataProvider,
credsProvider: credsProvider,
projectID: projectID,
clusters: []string{},
clusterNames: []string{},
dataFederationNames: []string{},
targetNamespace: "",
includeSecretsData: false,
orgID: orgID,
Expand All @@ -71,7 +74,7 @@ func NewConfigExporter(dataProvider store.AtlasOperatorGenericStore, credsProvid
}

func (e *ConfigExporter) WithClustersNames(clusters []string) *ConfigExporter {
e.clusters = clusters
e.clusterNames = clusters
return e
}

Expand All @@ -95,6 +98,11 @@ func (e *ConfigExporter) WithFeatureValidator(validator features.FeatureValidato
return e
}

func (e *ConfigExporter) WithDataFederationNames(dataFederations []string) *ConfigExporter {
e.dataFederationNames = dataFederations
return e
}

func (e *ConfigExporter) Run() (string, error) {
// TODO: Add REST to OPERATOR entities matcher

Expand All @@ -116,6 +124,12 @@ func (e *ConfigExporter) Run() (string, error) {
}
resources = append(resources, deploymentsResources...)

dataFederationResource, err := e.exportDataFederation(projectName)
if err != nil {
return "", err
}
resources = append(resources, dataFederationResource...)

for _, res := range resources {
err = serializer.Encode(res, output)
if err != nil {
Expand Down Expand Up @@ -168,15 +182,15 @@ func (e *ConfigExporter) exportProject() ([]runtime.Object, string, error) {
func (e *ConfigExporter) exportDeployments(projectName string) ([]runtime.Object, error) {
var result []runtime.Object

if len(e.clusters) == 0 {
if len(e.clusterNames) == 0 {
clusters, err := fetchClusterNames(e.dataProvider, e.projectID)
if err != nil {
return nil, err
}
e.clusters = clusters
e.clusterNames = clusters
}

for _, deploymentName := range e.clusters {
for _, deploymentName := range e.clusterNames {
// Try advanced cluster first
if advancedCluster, err := deployment.BuildAtlasAdvancedDeployment(e.dataProvider, e.featureValidator, e.projectID, projectName, deploymentName, e.targetNamespace, e.dictionaryForAtlasNames, e.operatorVersion); err == nil {
if advancedCluster != nil {
Expand Down Expand Up @@ -245,3 +259,39 @@ func fetchClusterNames(clustersProvider store.AtlasAllClustersLister, projectID

return result, nil
}

func (e *ConfigExporter) exportDataFederation(projectName string) ([]runtime.Object, error) {
var result []runtime.Object
nameList := e.dataFederationNames
if len(nameList) == 0 {
dataFederations, err := e.fetchDataFederationNames()
if err != nil {
return nil, err
}
nameList = dataFederations
}
for _, name := range nameList {
atlasDataFederations, err := datafederation.BuildAtlasDataFederation(e.dataProvider, name, e.projectID, projectName, e.operatorVersion, e.targetNamespace, e.dictionaryForAtlasNames)
if err != nil {
return nil, err
}
result = append(result, atlasDataFederations)
}
return result, nil
}

func (e *ConfigExporter) fetchDataFederationNames() ([]string, error) {
var result []string
dataFederations, err := e.dataProvider.DataFederationList(e.projectID)
if err != nil {
return nil, err
}
for _, obj := range dataFederations {
name := obj.GetName()
if reflect.ValueOf(name).IsZero() {
continue
}
result = append(result, name)
}
return result, nil
}
91 changes: 91 additions & 0 deletions internal/kubernetes/operator/config_exporter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2023 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.

//go:build unit

package operator

import (
"testing"

"github.com/go-test/deep"
"github.com/golang/mock/gomock"
"github.com/mongodb/mongodb-atlas-cli/internal/mocks"
"github.com/mongodb/mongodb-atlas-cli/internal/pointer"
"go.mongodb.org/atlas-sdk/v20230201004/admin"
)

const projectID = "TestProjectID"

func Test_fetchDataFederationNames(t *testing.T) {
ctl := gomock.NewController(t)
atlasOperatorGenericStore := mocks.NewMockAtlasOperatorGenericStore(ctl)

t.Run("Can fetch Data Federation Instance names", func(t *testing.T) {
dataFederations := []admin.DataLakeTenant{
{
DataProcessRegion: &admin.DataLakeDataProcessRegion{
CloudProvider: "TestProvider",
Region: "TestRegion",
},
Name: pointer.Get("DataFederationInstance0"),
State: pointer.Get("TestState"),
Storage: &admin.DataLakeStorage{
Databases: nil,
Stores: nil,
},
},
{
DataProcessRegion: &admin.DataLakeDataProcessRegion{
CloudProvider: "TestProvider",
Region: "TestRegion",
},
Name: pointer.Get("DataFederationInstance1"),
State: pointer.Get("TestState"),
Storage: &admin.DataLakeStorage{
Databases: nil,
Stores: nil,
},
},
{
DataProcessRegion: &admin.DataLakeDataProcessRegion{
CloudProvider: "TestProvider",
Region: "TestRegion",
},
Name: pointer.Get("DataFederationInstance2"),
State: pointer.Get("TestState"),
Storage: &admin.DataLakeStorage{
Databases: nil,
Stores: nil,
},
},
}

atlasOperatorGenericStore.EXPECT().DataFederationList(projectID).Return(dataFederations, nil)
expected := []string{"DataFederationInstance0", "DataFederationInstance1", "DataFederationInstance2"}
ce := NewConfigExporter(
atlasOperatorGenericStore,
nil, // credsProvider (not used)
projectID, "", // orgID (not used)
)
got, err := ce.fetchDataFederationNames()
if err != nil {
t.Fatalf("%v", err)
}

if diff := deep.Equal(got, expected); diff != nil {
t.Error(diff)
}
})
}
Loading

0 comments on commit c74dbb4

Please sign in to comment.