Skip to content

Commit 03bf858

Browse files
josvazgs-urbaniak
andauthored
CLOUDP-280230: Add Network peering CRD and controller (#2103)
* Add Network Peering CRD * Add Network Peering controller Signed-off-by: jose.vazquez <[email protected]> * Update helm chart * Fixup peering not found return * Add deletion protection on network peerings * Fix indexers Signed-off-by: jose.vazquez <[email protected]> * Explain containers are required for peering * Allow to set ID to manage exiting peering Signed-off-by: jose.vazquez <[email protected]> * Avoid not found errors when already removed * Move CEL defs * Simplify conversion check * Remove unneeded extra lines * Clear TODO * Fix message wording * Remove unused copy left over error * Update api/v1/project_reference_cel_test.go Do log instead of print Co-authored-by: Sergiusz Urbaniak <[email protected]> * Fix deletion greediness properly * Remove trace --------- Signed-off-by: jose.vazquez <[email protected]> Co-authored-by: Sergiusz Urbaniak <[email protected]>
1 parent 96be0de commit 03bf858

File tree

52 files changed

+4555
-432
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+4555
-432
lines changed

.github/workflows/test-e2e.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ jobs:
186186
"ip-access-list",
187187
"dry-run",
188188
"networkcontainer-controller",
189+
"networkpeering-controller",
189190
]
190191
steps:
191192
- name: Get repo files from cache

.mockery.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ packages:
1717
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/maintenancewindow:
1818
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/encryptionatrest:
1919
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/networkcontainer:
20+
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/networkpeering:

PROJECT

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,12 @@ resources:
135135
kind: AtlasNetworkContainer
136136
path: github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1
137137
version: v1
138+
- api:
139+
crdVersion: v1
140+
namespaced: true
141+
domain: mongodb.com
142+
group: atlas
143+
kind: AtlasNetworkPeering
144+
path: github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1
145+
version: v1
138146
version: "3"

api/v1/atlasnetworkcontainer_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ type AtlasNetworkContainerSpec struct {
7474

7575
// AtlasNetworkContainerConfig defines the Atlas specifics of the desired state of a Network Container
7676
type AtlasNetworkContainerConfig struct {
77-
// ID is the container identified for an already existent network container to be managed by the operator.
77+
// ID is the container identifier for an already existent network container to be managed by the operator.
7878
// This field can be used in conjunction with cidrBlock to update the cidrBlock of an existing container.
7979
// This field is immutable.
8080
// +optional

api/v1/atlasnetworkpeering_types.go

Lines changed: 107 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,81 @@ limitations under the License.
1616

1717
package v1
1818

19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
22+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/api"
23+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1/status"
24+
)
25+
26+
func init() {
27+
SchemeBuilder.Register(&AtlasNetworkPeering{}, &AtlasNetworkPeeringList{})
28+
}
29+
30+
// AtlasNetworkPeering is the Schema for the AtlasNetworkPeering API
31+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
32+
// +kubebuilder:object:root=true
33+
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
34+
// +kubebuilder:printcolumn:name="Provider",type=string,JSONPath=`.spec.provider`
35+
// +kubebuilder:printcolumn:name="Id",type=string,JSONPath=`.status.id`
36+
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status`
37+
// +kubebuilder:subresource:status
38+
// +groupName:=atlas.mongodb.com
39+
// +kubebuilder:resource:categories=atlas,shortName=anp
40+
type AtlasNetworkPeering struct {
41+
metav1.TypeMeta `json:",inline"`
42+
metav1.ObjectMeta `json:"metadata,omitempty"`
43+
44+
Spec AtlasNetworkPeeringSpec `json:"spec,omitempty"`
45+
Status status.AtlasNetworkPeeringStatus `json:"status,omitempty"`
46+
}
47+
48+
//+kubebuilder:object:root=true
49+
50+
// AtlasNetworkPeeringList contains a list of AtlasNetworkPeering
51+
type AtlasNetworkPeeringList struct {
52+
metav1.TypeMeta `json:",inline"`
53+
metav1.ListMeta `json:"metadata,omitempty"`
54+
Items []AtlasNetworkPeering `json:"items"`
55+
}
56+
57+
// AtlasNetworkPeeringSpec defines the desired state of AtlasNetworkPeering
58+
// +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"
59+
// +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"
60+
// +kubebuilder:validation:XValidation:rule="(has(self.containerRef.name) && !has(self.containerRef.id)) || (!has(self.containerRef.name) && has(self.containerRef.id))",message="must either have a container Atlas id or Kubernetes name, but not both (or neither)"
61+
// +kubebuilder:validation:XValidation:rule="(self.id == oldSelf.id) || (!has(self.id) && !has(oldSelf.id))",message="id is immutable"
62+
type AtlasNetworkPeeringSpec struct {
63+
ProjectDualReference `json:",inline"`
64+
65+
ContainerRef ContainerDualReference `json:"containerRef"`
66+
67+
AtlasNetworkPeeringConfig `json:",inline"`
68+
}
69+
70+
// ContainerDualReference refers to an Network Container either by Kubernetes name or Atlas ID
71+
type ContainerDualReference struct {
72+
// Name of the container Kubernetes resource, must be present in the same namespace
73+
// Use either name or ID, not both.
74+
// +optional
75+
Name string `json:"name,omitempty"`
76+
77+
// ID is the Atlas identifier of the Network Container Atlas resource this Peering Connection relies on
78+
// Use either name or ID, not both.
79+
// +optional
80+
ID string `json:"id,omitempty"`
81+
}
82+
83+
// AtlasNetworkPeeringConfig defines the Atlas specifics of the desired state of Peering Connections
1984
type AtlasNetworkPeeringConfig struct {
85+
// ID is the peering identifier for an already existent network peering to be managed by the operator.
86+
// This field is immutable.
87+
// +optional
88+
ID string `json:"id,omitempty"`
89+
2090
// Name of the cloud service provider for which you want to create the network peering service.
2191
// +kubebuilder:validation:Enum=AWS;GCP;AZURE
2292
// +kubebuilder:validation:Required
2393
Provider string `json:"provider"`
24-
// ID of the network peer container. If not set, operator will create a new container with ContainerRegion and AtlasCIDRBlock input.
25-
// +optional
26-
ContainerID string `json:"containerId"`
2794

2895
// AWSConfiguration is the specific AWS settings for network peering
2996
// +kubebuilder:validation:Optional
@@ -36,40 +103,66 @@ type AtlasNetworkPeeringConfig struct {
36103
GCPConfiguration *GCPNetworkPeeringConfiguration `json:"gcpConfiguration,omitempty"`
37104
}
38105

39-
type AtlasProviderContainerConfig struct {
40-
// ContainerRegion is the provider region name of Atlas network peer container. If not set, AccepterRegionName is used.
41-
// +optional
42-
ContainerRegion string `json:"containerRegion"`
43-
// Atlas CIDR. It needs to be set if ContainerID is not set.
44-
// +optional
45-
AtlasCIDRBlock string `json:"atlasCidrBlock"`
46-
}
47-
106+
// AWSNetworkPeeringConfiguration defines tha Atlas desired state for AWS
48107
type AWSNetworkPeeringConfiguration struct {
49-
// AccepterRegionName is the provider region name of user's vpc.
108+
// AccepterRegionName is the provider region name of user's vpc in AWS native region format
109+
// +kubebuilder:validation:Required
50110
AccepterRegionName string `json:"accepterRegionName"`
51111
// AccountID of the user's vpc.
112+
// +kubebuilder:validation:Required
52113
AWSAccountID string `json:"awsAccountId,omitempty"`
53114
// User VPC CIDR.
115+
// +kubebuilder:validation:Required
54116
RouteTableCIDRBlock string `json:"routeTableCidrBlock,omitempty"`
55117
// AWS VPC ID.
118+
// +kubebuilder:validation:Required
56119
VpcID string `json:"vpcId,omitempty"`
57120
}
58121

122+
// AzureNetworkPeeringConfiguration defines tha Atlas desired state for Azure
59123
type AzureNetworkPeeringConfiguration struct {
60124
//AzureDirectoryID is the unique identifier for an Azure AD directory.
125+
// +kubebuilder:validation:Required
61126
AzureDirectoryID string `json:"azureDirectoryId,omitempty"`
62127
// AzureSubscriptionID is the unique identifier of the Azure subscription in which the VNet resides.
128+
// +kubebuilder:validation:Required
63129
AzureSubscriptionID string `json:"azureSubscriptionId,omitempty"`
64130
//ResourceGroupName is the name of your Azure resource group.
131+
// +kubebuilder:validation:Required
65132
ResourceGroupName string `json:"resourceGroupName,omitempty"`
66133
// VNetName is name of your Azure VNet. Its applicable only for Azure.
67-
VNetName string `json:"vnetName,omitempty"`
134+
// +kubebuilder:validation:Required
135+
VNetName string `json:"vNetName,omitempty"`
68136
}
69137

138+
// GCPNetworkPeeringConfiguration defines tha Atlas desired state for Google
70139
type GCPNetworkPeeringConfiguration struct {
71140
// User GCP Project ID. Its applicable only for GCP.
141+
// +kubebuilder:validation:Required
72142
GCPProjectID string `json:"gcpProjectId,omitempty"`
73143
// GCP Network Peer Name. Its applicable only for GCP.
144+
// +kubebuilder:validation:Required
74145
NetworkName string `json:"networkName,omitempty"`
75146
}
147+
148+
func (np *AtlasNetworkPeering) GetStatus() api.Status {
149+
return np.Status
150+
}
151+
152+
func (np *AtlasNetworkPeering) Credentials() *api.LocalObjectReference {
153+
return np.Spec.ConnectionSecret
154+
}
155+
156+
func (np *AtlasNetworkPeering) ProjectDualRef() *ProjectDualReference {
157+
return &np.Spec.ProjectDualReference
158+
}
159+
160+
func (np *AtlasNetworkPeering) UpdateStatus(conditions []api.Condition, options ...api.Option) {
161+
np.Status.Conditions = conditions
162+
np.Status.ObservedGeneration = np.ObjectMeta.Generation
163+
164+
for _, o := range options {
165+
v := o.(status.AtlasNetworkPeeringStatusOption)
166+
v(&np.Status)
167+
}
168+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package v1
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
"k8s.io/apimachinery/pkg/runtime"
9+
10+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/api/v1/common"
11+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/test/helper/cel"
12+
)
13+
14+
func TestPeeringCELChecks(t *testing.T) {
15+
for _, tc := range []struct {
16+
title string
17+
old *AtlasNetworkPeering
18+
obj *AtlasNetworkPeering
19+
expectedErrors []string
20+
}{
21+
{
22+
title: "Missing container ref in peering fails",
23+
obj: &AtlasNetworkPeering{
24+
Spec: AtlasNetworkPeeringSpec{},
25+
},
26+
expectedErrors: []string{"spec: Invalid value: \"object\": must either have a container Atlas id or Kubernetes name, but not both (or neither)"},
27+
},
28+
29+
{
30+
title: "Named container ref works",
31+
obj: &AtlasNetworkPeering{
32+
Spec: AtlasNetworkPeeringSpec{
33+
ContainerRef: ContainerDualReference{
34+
Name: "Some-name",
35+
},
36+
},
37+
},
38+
},
39+
40+
{
41+
title: "Container id ref works",
42+
obj: &AtlasNetworkPeering{
43+
Spec: AtlasNetworkPeeringSpec{
44+
ContainerRef: ContainerDualReference{
45+
ID: "some-id",
46+
},
47+
},
48+
},
49+
},
50+
51+
{
52+
title: "Both container id and name ref fails",
53+
obj: &AtlasNetworkPeering{
54+
Spec: AtlasNetworkPeeringSpec{
55+
ContainerRef: ContainerDualReference{
56+
Name: "Some-name",
57+
ID: "some-id",
58+
},
59+
},
60+
},
61+
expectedErrors: []string{"spec: Invalid value: \"object\": must either have a container Atlas id or Kubernetes name, but not both (or neither)"},
62+
},
63+
64+
{
65+
title: "ID set works",
66+
obj: &AtlasNetworkPeering{
67+
Spec: AtlasNetworkPeeringSpec{
68+
ContainerRef: ContainerDualReference{
69+
ID: "some-id",
70+
},
71+
AtlasNetworkPeeringConfig: AtlasNetworkPeeringConfig{
72+
ID: "some-peering-id",
73+
},
74+
},
75+
},
76+
},
77+
78+
{
79+
title: "ID added in fails",
80+
old: &AtlasNetworkPeering{
81+
Spec: AtlasNetworkPeeringSpec{
82+
ContainerRef: ContainerDualReference{
83+
ID: "some-id",
84+
},
85+
},
86+
},
87+
obj: &AtlasNetworkPeering{
88+
Spec: AtlasNetworkPeeringSpec{
89+
ContainerRef: ContainerDualReference{
90+
ID: "some-id",
91+
},
92+
AtlasNetworkPeeringConfig: AtlasNetworkPeeringConfig{
93+
ID: "some-peering-id",
94+
},
95+
},
96+
},
97+
expectedErrors: []string{"spec: Invalid value: \"object\": no such key: id evaluating rule: id is immutable"},
98+
},
99+
100+
{
101+
title: "ID remove fails",
102+
old: &AtlasNetworkPeering{
103+
Spec: AtlasNetworkPeeringSpec{
104+
ContainerRef: ContainerDualReference{
105+
ID: "some-id",
106+
},
107+
AtlasNetworkPeeringConfig: AtlasNetworkPeeringConfig{
108+
ID: "some-peering-id",
109+
},
110+
},
111+
},
112+
obj: &AtlasNetworkPeering{
113+
Spec: AtlasNetworkPeeringSpec{
114+
ContainerRef: ContainerDualReference{
115+
ID: "some-id",
116+
},
117+
},
118+
},
119+
expectedErrors: []string{"spec: Invalid value: \"object\": no such key: id evaluating rule: id is immutable"},
120+
},
121+
122+
{
123+
title: "ID changed fails",
124+
old: &AtlasNetworkPeering{
125+
Spec: AtlasNetworkPeeringSpec{
126+
ContainerRef: ContainerDualReference{
127+
ID: "some-id",
128+
},
129+
AtlasNetworkPeeringConfig: AtlasNetworkPeeringConfig{
130+
ID: "some-peering-id",
131+
},
132+
},
133+
},
134+
obj: &AtlasNetworkPeering{
135+
Spec: AtlasNetworkPeeringSpec{
136+
ContainerRef: ContainerDualReference{
137+
ID: "some-id",
138+
},
139+
AtlasNetworkPeeringConfig: AtlasNetworkPeeringConfig{
140+
ID: "another-peering-id",
141+
},
142+
},
143+
},
144+
expectedErrors: []string{"spec: Invalid value: \"object\": id is immutable"},
145+
},
146+
147+
{
148+
title: "ID unchanged works",
149+
old: &AtlasNetworkPeering{
150+
Spec: AtlasNetworkPeeringSpec{
151+
ContainerRef: ContainerDualReference{
152+
ID: "some-id",
153+
},
154+
AtlasNetworkPeeringConfig: AtlasNetworkPeeringConfig{
155+
ID: "some-peering-id",
156+
},
157+
},
158+
},
159+
obj: &AtlasNetworkPeering{
160+
Spec: AtlasNetworkPeeringSpec{
161+
ContainerRef: ContainerDualReference{
162+
ID: "some-id",
163+
},
164+
AtlasNetworkPeeringConfig: AtlasNetworkPeeringConfig{
165+
ID: "some-peering-id",
166+
},
167+
},
168+
},
169+
},
170+
} {
171+
t.Run(tc.title, func(t *testing.T) {
172+
// inject a project to avoid other CEL validations being hit
173+
tc.obj.Spec.ProjectRef = &common.ResourceRefNamespaced{Name: "some-project"}
174+
unstructuredObject, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.obj)
175+
require.NoError(t, err)
176+
177+
unstructuredOld := map[string]interface{}{}
178+
if tc.old != nil {
179+
var err error
180+
tc.old.Spec.ProjectRef = &common.ResourceRefNamespaced{Name: "some-project"}
181+
unstructuredOld, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.old)
182+
require.NoError(t, err)
183+
}
184+
185+
crdPath := "../../config/crd/bases/atlas.mongodb.com_atlasnetworkpeerings.yaml"
186+
validator, err := cel.VersionValidatorFromFile(t, crdPath, "v1")
187+
assert.NoError(t, err)
188+
errs := validator(unstructuredObject, unstructuredOld)
189+
190+
require.Equal(t, tc.expectedErrors, cel.ErrorListAsStrings(errs))
191+
})
192+
}
193+
}

0 commit comments

Comments
 (0)