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-280230: Network peering controller and CRD #1930

Draft
wants to merge 29 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c5d6c81
CLOUDP-280230: Fix network peering translation layer & contract tests
josvazg Jan 22, 2025
230f3dc
CLOUDP-280230: Add network peering CRD
josvazg Jan 22, 2025
347dc04
CLOUDP-280230: Add network peering controller & tests
josvazg Jan 22, 2025
9815d27
Unit tests draft
josvazg Jan 22, 2025
0f51911
Treat container in use as a good cleanup exit
josvazg Jan 23, 2025
8cd506a
Remove unused reason
josvazg Jan 23, 2025
8623af4
Fix to comply to cluster usage over manager
josvazg Jan 23, 2025
595f862
Add AtlasNetworkContainer CRD
josvazg Jan 25, 2025
fbe0993
Refactor dual ref CEL validations
josvazg Jan 25, 2025
3f608f4
Add peering & container CEL validations
josvazg Jan 25, 2025
9e92bbb
add controller draft
josvazg Jan 25, 2025
f3e5d52
start network container controller
josvazg Jan 27, 2025
27f89ab
Add indexers and improve their test coverage
josvazg Jan 28, 2025
f559531
draft container controller with unit-tests
josvazg Jan 28, 2025
5b648c4
container passing custom resource tests
josvazg Jan 28, 2025
c2ba44f
add network container mocks
josvazg Jan 29, 2025
7ec7506
draft
josvazg Jan 29, 2025
edbcecb
more on network container controller tests
josvazg Jan 29, 2025
133a93d
fixup
josvazg Jan 29, 2025
7b9cb58
fixup project and kustomization
josvazg Jan 29, 2025
bc66bd8
Fix alias of containers clobbing the one from peerings
josvazg Jan 29, 2025
b18044a
Remove redundant controller registration
josvazg Jan 29, 2025
ae17c3f
Update helm charts with network container support
josvazg Jan 30, 2025
2e98e19
Complete container state machine with unit tests
josvazg Jan 30, 2025
3d95daf
Cover finalizers valdation in unit tests
josvazg Jan 30, 2025
93643f8
Use fixed helm-charts
josvazg Jan 30, 2025
4245a52
tweak state machine due to manual tests
josvazg Jan 30, 2025
5f3ea7d
Manual testsing fixes
josvazg Feb 2, 2025
06c0e1b
Implement greedyness deactivation and skips
josvazg Feb 3, 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 @@ -166,6 +166,7 @@ jobs:
"long-run",
"multinamespaced",
"networkpeering",
"networkpeering-controller",
"privatelink",
"private-endpoint",
"project-settings",
Expand Down
2 changes: 2 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ packages:
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/privateendpoint:
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/maintenancewindow:
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/encryptionatrest:
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/networkpeering:
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/networkcontainer:
17 changes: 17 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,21 @@ resources:
kind: AtlasIPAccessList
path: github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: mongodb.com
group: atlas
kind: AtlasNetworkContainer
path: github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
domain: mongodb.com
group: atlas
kind: AtlasNetworkPeering
path: github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1
version: v1
version: "3"
5 changes: 5 additions & 0 deletions api/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ const (
IPAccessListReady ConditionType = "IPAccessListReady"
)

// Atlas Network Container condition types
const (
NetworkContainerReady ConditionType = "NetworkContainerReady"
)

// Generic condition type
const (
ResourceVersionStatus ConditionType = "ResourceVersionIsValid"
Expand Down
106 changes: 106 additions & 0 deletions api/v1/atlasnetworkcontainer_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
Copyright 2025 MongoDB.

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 v1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/mongodb/mongodb-atlas-kubernetes/v2/api"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1/status"
)

func init() {
SchemeBuilder.Register(&AtlasNetworkContainer{}, &AtlasNetworkContainerList{})
}

// AtlasNetworkContainer is the Schema for the AtlasNetworkContainer API
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:object:root=true
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Provider",type=string,JSONPath=`.spec.provider`
// +kubebuilder:printcolumn:name="Id",type=string,JSONPath=`.status.id`
// +kubebuilder:printcolumn:name="Provisioned",type=string,JSONPath=`.status.provisioned`
// +kubebuilder:subresource:status
// +groupName:=atlas.mongodb.com
// +kubebuilder:resource:categories=atlas,shortName=anc
type AtlasNetworkContainer struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec AtlasNetworkContainerSpec `json:"spec,omitempty"`
Status status.AtlasNetworkContainerStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// AtlasNetworkContainerList contains a list of AtlasNetworkContainer
type AtlasNetworkContainerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []AtlasNetworkContainer `json:"items"`
}

// +kubebuilder:validation:XValidation:rule="(has(self.externalProjectRef) && !has(self.projectRef)) || (!has(self.externalProjectRef) && has(self.projectRef))",message="must define only one project reference through externalProjectRef or projectRef"
// +kubebuilder:validation:XValidation:rule="(has(self.externalProjectRef) && has(self.connectionSecret)) || !has(self.externalProjectRef)",message="must define a local connection secret when referencing an external project"
// +kubebuilder:validation:XValidation:rule="(self.provider == 'GCP' && !has(self.region)) || (self.provider != 'GCP')",message="must not set region for GCP containers"
// +kubebuilder:validation:XValidation:rule="((self.provider == 'AWS' || self.provider == 'AZURE') && has(self.region)) || (self.provider == 'GCP')",message="must set region for AWS and Azure containers"

// AtlasNetworkContainerSpec defines the desired state of an AtlasNetworkContainer
type AtlasNetworkContainerSpec struct {
ProjectDualReference `json:",inline"`

// Provider is the name of the cloud provider hosting the network container
// +kubebuilder:validation:Enum=AWS;GCP;AZURE
// +kubebuilder:validation:Required
Provider string `json:"provider"`

AtlasNetworkContainerConfig `json:",inline"`
}

// AtlasNetworkContainerConfig defines the Atlas specifics of the desired state of a Network Container
type AtlasNetworkContainerConfig struct {
// ContainerRegion is the provider region name of Atlas network peer container in Atlas region format
// This is required by AWS and Azure, but not used by GCP
// +optional
Region string `json:"region,omitempty"`

// Atlas CIDR. It needs to be set if ContainerID is not set.
// +required
CIDRBlock string `json:"cidrBlock"`
}

func (np *AtlasNetworkContainer) GetStatus() api.Status {
return np.Status
}

func (np *AtlasNetworkContainer) Credentials() *api.LocalObjectReference {
return np.Spec.ConnectionSecret
}

func (np *AtlasNetworkContainer) ProjectDualRef() *ProjectDualReference {
return &np.Spec.ProjectDualReference
}

func (np *AtlasNetworkContainer) UpdateStatus(conditions []api.Condition, options ...api.Option) {
np.Status.Conditions = conditions
np.Status.ObservedGeneration = np.ObjectMeta.Generation

for _, o := range options {
v := o.(status.AtlasNetworkContainerStatusOption)
v(&np.Status)
}
}
101 changes: 101 additions & 0 deletions api/v1/atlasnetworkcontainer_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package v1

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime"

"github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1/common"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1/provider"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/cel"
)

func TestGCPRegionCELCcheck(t *testing.T) {
for _, tc := range []struct {
title string
obj *AtlasNetworkContainer
expectedErrors []string
}{
{
title: "GCP fails with a region",
obj: &AtlasNetworkContainer{
Spec: AtlasNetworkContainerSpec{
Provider: string(provider.ProviderGCP),
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{
Region: "some-region",
},
},
},
expectedErrors: []string{"spec: Invalid value: \"object\": must not set region for GCP containers"},
},
{
title: "GCP succeeds without a region",
obj: &AtlasNetworkContainer{
Spec: AtlasNetworkContainerSpec{
Provider: string(provider.ProviderGCP),
},
},
},
{
title: "AWS succeeds with a region",
obj: &AtlasNetworkContainer{
Spec: AtlasNetworkContainerSpec{
Provider: string(provider.ProviderAWS),
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{
Region: "some-region",
},
},
},
},
{
title: "Azure succeeds with a region",
obj: &AtlasNetworkContainer{
Spec: AtlasNetworkContainerSpec{
Provider: string(provider.ProviderAzure),
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{
Region: "some-region",
},
},
},
},
{
title: "AWS fails without a region",
obj: &AtlasNetworkContainer{
Spec: AtlasNetworkContainerSpec{
Provider: string(provider.ProviderAWS),
},
},
expectedErrors: []string{"spec: Invalid value: \"object\": must set region for AWS and Azure containers"},
},
{
title: "Azure fails without a region",
obj: &AtlasNetworkContainer{
Spec: AtlasNetworkContainerSpec{
Provider: string(provider.ProviderAzure),
},
},
expectedErrors: []string{"spec: Invalid value: \"object\": must set region for AWS and Azure containers"},
},
} {
t.Run(tc.title, func(t *testing.T) {
// inject a project to avoid other CEL validations being hit
tc.obj.Spec.ProjectRef = &common.ResourceRefNamespaced{Name: "some-project"}
unstructuredObject, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.obj)
require.NoError(t, err)

crdPath := "../../config/crd/bases/atlas.mongodb.com_atlasnetworkcontainers.yaml"
validator, err := cel.VersionValidatorFromFile(t, crdPath, "v1")
assert.NoError(t, err)
errs := validator(unstructuredObject, nil)

require.Equal(t, len(tc.expectedErrors), len(errs), fmt.Sprintf("errs: %v", errs))

for i, err := range errs {
assert.Equal(t, tc.expectedErrors[i], err.Error())
}
})
}
}
82 changes: 80 additions & 2 deletions api/v1/atlasnetworkpeering_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,58 @@ limitations under the License.

package v1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/mongodb/mongodb-atlas-kubernetes/v2/api"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1/status"
)

func init() {
SchemeBuilder.Register(&AtlasNetworkPeering{}, &AtlasNetworkPeeringList{})
}

// AtlasNetworkPeering is the Schema for the AtlasNetworkPeering API
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:object:root=true
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Provider",type=string,JSONPath=`.spec.provider`
// +kubebuilder:printcolumn:name="Id",type=string,JSONPath=`.status.id`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status`
// +kubebuilder:printcolumn:name="Container Provisioned",type=string,JSONPath=`.status.containerProvisioned`
// +kubebuilder:subresource:status
// +groupName:=atlas.mongodb.com
// +kubebuilder:resource:categories=atlas,shortName=anp
type AtlasNetworkPeering struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec AtlasNetworkPeeringSpec `json:"spec,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will double check that for when we surface this again, both for the NetworkContainer and the PeeringConnection CRDs.

Status status.AtlasNetworkPeeringStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// AtlasNetworkPeeringList contains a list of AtlasNetworkPeering
type AtlasNetworkPeeringList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []AtlasNetworkPeering `json:"items"`
}

// +kubebuilder:validation:XValidation:rule="(has(self.externalProjectRef) && !has(self.projectRef)) || (!has(self.externalProjectRef) && has(self.projectRef))",message="must define only one project reference through externalProjectRef or projectRef"
// +kubebuilder:validation:XValidation:rule="(has(self.externalProjectRef) && has(self.connectionSecret)) || !has(self.externalProjectRef)",message="must define a local connection secret when referencing an external project"

// AtlasNetworkPeeringSpec defines the desired state of AtlasNetworkPeering
type AtlasNetworkPeeringSpec struct {
ProjectDualReference `json:",inline"`

AtlasNetworkPeeringConfig `json:",inline"`

AtlasProviderContainerConfig `json:",inline"`
}

// AtlasNetworkPeeringConfig defines the Atlas specifics of the desired state of Peering Connections
type AtlasNetworkPeeringConfig struct {
// Name of the cloud service provider for which you want to create the network peering service.
// +kubebuilder:validation:Enum=AWS;GCP;AZURE
Expand All @@ -36,17 +88,19 @@ type AtlasNetworkPeeringConfig struct {
GCPConfiguration *GCPNetworkPeeringConfiguration `json:"gcpConfiguration,omitempty"`
}

// AtlasProviderContainerConfig defines the Atlas specifics of the desired state of Peering Container
type AtlasProviderContainerConfig struct {
// ContainerRegion is the provider region name of Atlas network peer container. If not set, AccepterRegionName is used.
// ContainerRegion is the provider region name of Atlas network peer container in Atlas region format
// +optional
ContainerRegion string `json:"containerRegion"`
// Atlas CIDR. It needs to be set if ContainerID is not set.
// +optional
AtlasCIDRBlock string `json:"atlasCidrBlock"`
}

// AWSNetworkPeeringConfiguration defines tha Atlas desired state for AWS
type AWSNetworkPeeringConfiguration struct {
// AccepterRegionName is the provider region name of user's vpc.
// AccepterRegionName is the provider region name of user's vpc in AWS native region format
AccepterRegionName string `json:"accepterRegionName"`
// AccountID of the user's vpc.
AWSAccountID string `json:"awsAccountId,omitempty"`
Expand All @@ -56,6 +110,7 @@ type AWSNetworkPeeringConfiguration struct {
VpcID string `json:"vpcId,omitempty"`
}

// AzureNetworkPeeringConfiguration defines tha Atlas desired state for Azure
type AzureNetworkPeeringConfiguration struct {
//AzureDirectoryID is the unique identifier for an Azure AD directory.
AzureDirectoryID string `json:"azureDirectoryId,omitempty"`
Expand All @@ -67,9 +122,32 @@ type AzureNetworkPeeringConfiguration struct {
VNetName string `json:"vnetName,omitempty"`
}

// GCPNetworkPeeringConfiguration defines tha Atlas desired state for Google
type GCPNetworkPeeringConfiguration struct {
// User GCP Project ID. Its applicable only for GCP.
GCPProjectID string `json:"gcpProjectId,omitempty"`
// GCP Network Peer Name. Its applicable only for GCP.
NetworkName string `json:"networkName,omitempty"`
}

func (np *AtlasNetworkPeering) GetStatus() api.Status {
return np.Status
}

func (np *AtlasNetworkPeering) Credentials() *api.LocalObjectReference {
return np.Spec.ConnectionSecret
}

func (np *AtlasNetworkPeering) ProjectDualRef() *ProjectDualReference {
return &np.Spec.ProjectDualReference
}

func (np *AtlasNetworkPeering) UpdateStatus(conditions []api.Condition, options ...api.Option) {
np.Status.Conditions = conditions
np.Status.ObservedGeneration = np.ObjectMeta.Generation

for _, o := range options {
v := o.(status.AtlasNetworkPeeringStatusOption)
v(&np.Status)
}
}
Loading
Loading