Skip to content
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

CLOUDP-292365: Support Flex Clusters #2030

Merged
merged 35 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c049416
Add initial Flex types
roothorp Jan 2, 2025
1c27b82
Add Flex to translation layer
roothorp Jan 6, 2025
b8d37cd
Add Flex to Deployment controller
roothorp Jan 6, 2025
aca6643
Generate mocks
roothorp Jan 6, 2025
6c9f56d
Correctly propogate Flex API to controllers
roothorp Jan 9, 2025
41a4e82
fix tests
roothorp Jan 9, 2025
d266617
Add SDK Client Set to Contract tests
roothorp Jan 9, 2025
843c825
fix int tests
roothorp Jan 13, 2025
463f19a
check for flex before normal cluster
roothorp Jan 13, 2025
180519a
Add flex helper funcs
roothorp Jan 13, 2025
1c4b023
Update deployment validation to account for flex
roothorp Jan 13, 2025
cdf835a
small fixes after rebase
roothorp Jan 13, 2025
4d7aa43
Fix remaining broken tests
roothorp Jan 13, 2025
7830c69
add e2e test
roothorp Jan 16, 2025
4a3fc1c
changes per reviews
roothorp Jan 16, 2025
5414b94
Ignore dupl in _test.go files
roothorp Jan 16, 2025
1fec2b9
api: add optional kubebuilder for flex spec
s-urbaniak Jan 16, 2025
e4dca53
be more tolerant for flex API not available
s-urbaniak Jan 16, 2025
361897b
flex: respect gov
s-urbaniak Jan 16, 2025
2a99552
address linter comments
s-urbaniak Jan 17, 2025
c14e347
enable flex
s-urbaniak Jan 17, 2025
e4618ce
use ako deployment type for retrieving clusters from Atlas
s-urbaniak Jan 17, 2025
f0354bd
fix unit tests
s-urbaniak Jan 17, 2025
051fcef
fix unit tests
s-urbaniak Jan 17, 2025
a4f38fb
regenerate mocks
s-urbaniak Jan 17, 2025
0a941e1
remove checking for serverless vs. non-serverless constraints as it b…
s-urbaniak Jan 17, 2025
3607648
check for wrong cluster types explicitely
s-urbaniak Jan 17, 2025
2d4b3f6
act on not found errors only in GetFlexCluster
s-urbaniak Jan 17, 2025
667913e
use deployment names for getcluster
s-urbaniak Jan 17, 2025
3e93034
add error code from Atlas signalling wrong API call
s-urbaniak Jan 17, 2025
456b77d
fix tests
s-urbaniak Jan 17, 2025
f311ec0
fix linter
s-urbaniak Jan 17, 2025
fb2cf80
Fix flex e2e
roothorp Jan 17, 2025
ec908a4
address review comments
s-urbaniak Jan 20, 2025
e6e9d1d
add unit tests for flex handler
roothorp Jan 20, 2025
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 .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ jobs:
"reconcile-one",
"reconcile-two",
"backup-compliance",
"flex",
]
steps:
- name: Get repo files from cache
Expand Down
74 changes: 74 additions & 0 deletions api/v1/atlasdeployment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ type AtlasDeploymentSpec struct {
// ProcessArgs allows to modify Advanced Configuration Options
// +optional
ProcessArgs *ProcessArgs `json:"processArgs,omitempty"`

// Configuration for the Flex cluster API. https://www.mongodb.com/docs/atlas/reference/api-resources-spec/v2/#tag/Flex-Clusters
// +optional
FlexSpec *FlexSpec `json:"flexSpec,omitempty"`
}

type SearchNode struct {
Expand Down Expand Up @@ -465,6 +469,9 @@ func (c *AtlasDeployment) GetDeploymentName() string {
if c.IsServerless() {
return c.Spec.ServerlessSpec.Name
}
if c.IsFlex() {
return c.Spec.FlexSpec.Name
}
if c.IsAdvancedDeployment() {
return c.Spec.DeploymentSpec.Name
}
Expand All @@ -482,6 +489,10 @@ func (c *AtlasDeployment) IsAdvancedDeployment() bool {
return c.Spec.DeploymentSpec != nil
}

func (c *AtlasDeployment) IsFlex() bool {
return c.Spec.FlexSpec != nil
}

func (c *AtlasDeployment) GetReplicationSetID() string {
if len(c.Status.ReplicaSets) > 0 {
return c.Status.ReplicaSets[0].ID
Expand Down Expand Up @@ -530,6 +541,41 @@ func (c *AtlasDeployment) ProjectDualRef() *ProjectDualReference {
return &c.Spec.ProjectDualReference
}

type FlexSpec struct {
// Human-readable label that identifies the instance.
// +required
Name string `json:"name"`
s-urbaniak marked this conversation as resolved.
Show resolved Hide resolved

// List that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the instance.
// +kubebuilder:validation:MaxItems=50
// +optional
Tags []*TagSpec `json:"tags,omitempty"`

// Flag that indicates whether termination protection is enabled on the cluster.
// If set to true, MongoDB Cloud won't delete the cluster. If set to false, MongoDB Cloud will delete the cluster.
// +kubebuilder:default:=false
// +optional
TerminationProtectionEnabled bool `json:"terminationProtectionEnabled,omitempty"`

// Group of cloud provider settings that configure the provisioned MongoDB flex cluster.
// +required
ProviderSettings *FlexProviderSettings `json:"providerSettings"`
s-urbaniak marked this conversation as resolved.
Show resolved Hide resolved
}

type FlexProviderSettings struct {
// Cloud service provider on which MongoDB Atlas provisions the flex cluster.
// +kubebuilder:validation:Enum=AWS;GCP;AZURE
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Backing Provider cannot be modified after cluster creation"
// +required
BackingProviderName string `json:"backingProviderName,omitempty"`
s-urbaniak marked this conversation as resolved.
Show resolved Hide resolved

// Human-readable label that identifies the geographic location of your MongoDB flex cluster.
// The region you choose can affect network latency for clients accessing your databases.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Region Name cannot be modified after cluster creation"
// +required
RegionName string `json:"regionName,omitempty"`
s-urbaniak marked this conversation as resolved.
Show resolved Hide resolved
}

// ************************************ Builder methods *************************************************

func NewDeployment(namespace, name, nameInAtlas string) *AtlasDeployment {
Expand Down Expand Up @@ -583,6 +629,24 @@ func newServerlessInstance(namespace, name, nameInAtlas, backingProviderName, re
}
}

func newFlexInstance(namespace, name, nameInAtlas, backingProviderName, regionName string) *AtlasDeployment {
return &AtlasDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: AtlasDeploymentSpec{
FlexSpec: &FlexSpec{
Name: nameInAtlas,
ProviderSettings: &FlexProviderSettings{
BackingProviderName: backingProviderName,
RegionName: regionName,
},
},
},
}
}

func addReplicaIfNotAdded(deployment *AtlasDeployment) {
if deployment == nil {
return
Expand Down Expand Up @@ -767,6 +831,16 @@ func NewDefaultAWSServerlessInstance(namespace, projectName string) *AtlasDeploy
).WithProjectName(projectName)
}

func NewDefaultAWSFlexInstance(namespace, projectName string) *AtlasDeployment {
return newFlexInstance(
namespace,
"test-flex-instance-k8s",
"test-flex-instance",
"AWS",
"US_EAST_1",
).WithProjectName(projectName)
}

func (c *AtlasDeployment) AtlasName() string {
if c.Spec.DeploymentSpec != nil {
return c.Spec.DeploymentSpec.Name
Expand Down
51 changes: 51 additions & 0 deletions api/v1/zz_generated.deepcopy.go

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

67 changes: 67 additions & 0 deletions config/crd/bases/atlas.mongodb.com_atlasdeployments.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,73 @@ spec:
required:
- id
type: object
flexSpec:
description: Configuration for the Flex cluster API. https://www.mongodb.com/docs/atlas/reference/api-resources-spec/v2/#tag/Flex-Clusters
properties:
name:
description: Human-readable label that identifies the instance.
type: string
providerSettings:
description: Group of cloud provider settings that configure the
provisioned MongoDB flex cluster.
properties:
backingProviderName:
description: Cloud service provider on which MongoDB Atlas
provisions the flex cluster.
enum:
- AWS
- GCP
- AZURE
type: string
x-kubernetes-validations:
- message: Backing Provider cannot be modified after cluster
creation
rule: self == oldSelf
regionName:
description: |-
Human-readable label that identifies the geographic location of your MongoDB flex cluster.
The region you choose can affect network latency for clients accessing your databases.
type: string
x-kubernetes-validations:
- message: Region Name cannot be modified after cluster creation
rule: self == oldSelf
required:
- backingProviderName
- regionName
type: object
tags:
description: List that contains key-value pairs between 1 to 255
characters in length for tagging and categorizing the instance.
items:
description: TagSpec holds a key-value pair for resource tagging
on this deployment.
properties:
key:
maxLength: 255
minLength: 1
pattern: ^[a-zA-Z0-9][a-zA-Z0-9 @_.+`;`-]*$
type: string
value:
maxLength: 255
minLength: 1
pattern: ^[a-zA-Z0-9][a-zA-Z0-9@_.+`;`-]*$
type: string
required:
- key
- value
type: object
maxItems: 50
type: array
terminationProtectionEnabled:
default: false
description: |-
Flag that indicates whether termination protection is enabled on the cluster.
If set to true, MongoDB Cloud won't delete the cluster. If set to false, MongoDB Cloud will delete the cluster.
type: boolean
required:
- name
- providerSettings
type: object
processArgs:
description: ProcessArgs allows to modify Advanced Configuration Options
properties:
Expand Down
7 changes: 7 additions & 0 deletions internal/controller/atlas/api_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const (
// a serverless instance from the cluster API, which is not allowed
ServerlessInstanceFromClusterAPI = "CANNOT_USE_SERVERLESS_INSTANCE_IN_CLUSTER_API"

ClusterInstanceFromServerlessAPI = "CANNOT_USE_CLUSTER_IN_SERVERLESS_INSTANCE_API"

// Resource not found
ResourceNotFound = "RESOURCE_NOT_FOUND"

Expand All @@ -37,4 +39,9 @@ const (
BackupComplianceNotMet = "BACKUP_POLICIES_NOT_MEETING_BACKUP_COMPLIANCE_POLICY_REQUIREMENTS"

ProviderUnsupported = "PROVIDER_UNSUPPORTED"

// Cannot use the Flex API to interact with non-Flex clusters
NonFlexInFlexAPI = "CANNOT_USE_NON_FLEX_CLUSTER_IN_FLEX_API"

FeatureUnsupported = "FEATURE_UNSUPPORTED"
)
3 changes: 1 addition & 2 deletions internal/controller/atlas/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,8 @@ func (p *ProductionProvider) IsResourceSupported(resource api.AtlasCustomResourc
return false
case *akov2.AtlasDeployment:
hasSearchNodes := atlasResource.Spec.DeploymentSpec != nil && len(atlasResource.Spec.DeploymentSpec.SearchNodes) > 0
isServerless := atlasResource.Spec.ServerlessSpec != nil

return !(isServerless || hasSearchNodes)
return !(atlasResource.IsServerless() || atlasResource.IsFlex() || hasSearchNodes)
}

return false
Expand Down
9 changes: 9 additions & 0 deletions internal/controller/atlas/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ func TestProvider_IsResourceSupported(t *testing.T) {
},
expectation: false,
},
"should return false when it's Atlas Gov and resource is Flex Deployment": {
domain: "https://cloud.mongodbgov.com",
resource: &akov2.AtlasDeployment{
Spec: akov2.AtlasDeploymentSpec{
FlexSpec: &akov2.FlexSpec{},
},
},
expectation: false,
},
"should return false when it's Atlas Gov and resource is a Deployment with search nodes": {
domain: "https://cloud.mongodbgov.com",
resource: &akov2.AtlasDeployment{
Expand Down
6 changes: 5 additions & 1 deletion internal/controller/atlasdatabaseuser/databaseuser.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ func (r *AtlasDatabaseUserReconciler) handleDatabaseUser(ctx *workflow.Context,
if err != nil {
return r.terminate(ctx, atlasDatabaseUser, api.DatabaseUserReadyType, workflow.AtlasAPIAccessNotConfigured, true, err)
}
sdkClientSet, _, err := r.AtlasProvider.SdkClientSet(ctx.Context, credentials, r.Log)
if err != nil {
return r.terminate(ctx, atlasDatabaseUser, api.DatabaseUserReadyType, workflow.AtlasAPIAccessNotConfigured, true, err)
}
dbUserService := dbuser.NewAtlasUsers(sdkClient.DatabaseUsersApi)
deploymentService := deployment.NewAtlasDeployments(sdkClient.ClustersApi, sdkClient.ServerlessInstancesApi, sdkClient.GlobalClustersApi, r.AtlasProvider.IsCloudGov())
deploymentService := deployment.NewAtlasDeployments(sdkClient.ClustersApi, sdkClient.ServerlessInstancesApi, sdkClient.GlobalClustersApi, sdkClientSet.SdkClient20241113001.FlexClustersApi, r.AtlasProvider.IsCloudGov())
atlasProject, err := r.ResolveProject(ctx.Context, sdkClient, atlasDatabaseUser, orgID)
if err != nil {
return r.terminate(ctx, atlasDatabaseUser, api.DatabaseUserReadyType, workflow.AtlasAPIAccessNotConfigured, true, err)
Expand Down
8 changes: 8 additions & 0 deletions internal/controller/atlasdatabaseuser/databaseuser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/stretchr/testify/mock"
"go.mongodb.org/atlas-sdk/v20231115008/admin"
"go.mongodb.org/atlas-sdk/v20231115008/mockadmin"
adminv20241113001 "go.mongodb.org/atlas-sdk/v20241113001/admin"

"go.uber.org/zap"
"go.uber.org/zap/zaptest"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -191,6 +193,9 @@ func TestHandleDatabaseUser(t *testing.T) {

return &admin.APIClient{ProjectsApi: projectAPI, ClustersApi: clusterAPI, DatabaseUsersApi: userAPI}, "", nil
},
SdkSetClientFunc: func(secretRef *client.ObjectKey, log *zap.SugaredLogger) (*atlas.ClientSet, string, error) {
return &atlas.ClientSet{SdkClient20241113001: &adminv20241113001.APIClient{}}, "", nil
},
},
expectedResult: ctrl.Result{RequeueAfter: workflow.DefaultRetry},
expectedConditions: []api.Condition{
Expand Down Expand Up @@ -2247,5 +2252,8 @@ func DefaultTestProvider(t *testing.T) *atlasmock.TestProvider {
DatabaseUsersApi: userAPI,
}, "", nil
},
SdkSetClientFunc: func(secretRef *client.ObjectKey, log *zap.SugaredLogger) (*atlas.ClientSet, string, error) {
return &atlas.ClientSet{SdkClient20241113001: &adminv20241113001.APIClient{}}, "", nil
},
}
}
Loading
Loading