-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CLOUDP-299197: AtlasNetworkContainer CRD Controller (#2100)
* Add AtlasNetworkContainer CRD Signed-off-by: jose.vazquez <[email protected]> * update rebased helm chart * Add Network Container controller * Add deletion protection on network containers --------- Signed-off-by: jose.vazquez <[email protected]>
- Loading branch information
Showing
45 changed files
with
3,822 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
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: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" | ||
// +kubebuilder:validation:XValidation:rule="(self.id == oldSelf.id) || (!has(self.id) && !has(oldSelf.id))",message="id is immutable" | ||
// +kubebuilder:validation:XValidation:rule="(self.region == oldSelf.region) || (!has(self.region) && !has(oldSelf.region))",message="region is immutable" | ||
|
||
// 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 { | ||
// ID is the container identified for an already existent network container to be managed by the operator. | ||
// This field can be used in conjunction with cidrBlock to update the cidrBlock of an existing container. | ||
// This field is immutable. | ||
// +optional | ||
ID string `json:"id,omitempty"` | ||
|
||
// 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. | ||
// This field is immutable, Atlas does not admit network container changes. | ||
// +optional | ||
Region string `json:"region,omitempty"` | ||
|
||
// Atlas CIDR. It needs to be set if ContainerID is not set. | ||
// +optional | ||
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
package v1 | ||
|
||
import ( | ||
"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 TestContainerCELChecks(t *testing.T) { | ||
for _, tc := range []struct { | ||
title string | ||
old, 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"}, | ||
}, | ||
{ | ||
title: "ID cannot be changed", | ||
old: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderGCP), | ||
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{ | ||
ID: "old-id", | ||
}, | ||
}, | ||
}, | ||
obj: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderGCP), | ||
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{ | ||
ID: "new-id", | ||
}, | ||
}, | ||
}, | ||
expectedErrors: []string{"spec: Invalid value: \"object\": id is immutable"}, | ||
}, | ||
{ | ||
title: "ID can be unset", | ||
old: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderGCP), | ||
}, | ||
}, | ||
obj: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderGCP), | ||
}, | ||
}, | ||
}, | ||
{ | ||
title: "ID can be set", | ||
obj: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderGCP), | ||
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{ | ||
ID: "new-id", | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
title: "Region cannot be changed", | ||
old: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderAWS), | ||
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{ | ||
Region: "old-region", | ||
}, | ||
}, | ||
}, | ||
obj: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderAWS), | ||
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{ | ||
Region: "new-region", | ||
}, | ||
}, | ||
}, | ||
expectedErrors: []string{"spec: Invalid value: \"object\": region is immutable"}, | ||
}, | ||
{ | ||
title: "Region can be unset (for GCP)", | ||
old: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderGCP), | ||
}, | ||
}, | ||
obj: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderGCP), | ||
}, | ||
}, | ||
}, | ||
{ | ||
title: "Region can be set", | ||
obj: &AtlasNetworkContainer{ | ||
Spec: AtlasNetworkContainerSpec{ | ||
Provider: string(provider.ProviderAWS), | ||
AtlasNetworkContainerConfig: AtlasNetworkContainerConfig{ | ||
Region: "new-region", | ||
}, | ||
}, | ||
}, | ||
}, | ||
} { | ||
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"} | ||
unstructuredOldObject, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.old) | ||
require.NoError(t, err) | ||
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, unstructuredOldObject) | ||
|
||
require.Equal(t, tc.expectedErrors, cel.ErrorListAsStrings(errs)) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package status | ||
|
||
import "github.com/mongodb/mongodb-atlas-kubernetes/v2/api" | ||
|
||
// AtlasNetworkContainerStatus is a status for the AtlasNetworkContainer Custom resource. | ||
// Not the one included in the AtlasProject | ||
type AtlasNetworkContainerStatus struct { | ||
api.Common `json:",inline"` | ||
|
||
// ID record the identifier of the container in Atlas | ||
ID string `json:"id,omitempty"` | ||
|
||
// Provisioned is true when clusters have been deployed to the container before | ||
// the last reconciliation | ||
Provisioned bool `json:"provisioned,omitempty"` | ||
} | ||
|
||
// +kubebuilder:object:generate=false | ||
|
||
type AtlasNetworkContainerStatusOption func(s *AtlasNetworkContainerStatus) |
Oops, something went wrong.