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

[Stacked] CLOUDP-280230: Add Network peering CRD and controller #2103

Open
wants to merge 4 commits into
base: CLOUDP-299197/network-container-crd
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ packages:
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/networkcontainer:
github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/translation/networkpeering:
8 changes: 8 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,12 @@ resources:
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"
116 changes: 102 additions & 14 deletions api/v1/atlasnetworkpeering_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,76 @@ 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: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"`
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"
// +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)"

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

ContainerRef ContainerDualReference `json:"containerRef"`

AtlasNetworkPeeringConfig `json:",inline"`
}

// ContainerDualReference refers to an Network Container either by Kubernetes name or Atlas ID
type ContainerDualReference struct {
// Name of the container Kubernetes resource, must be present in the same namespace
// Use either name or ID, not both.
// +optional
Name string `json:"name,omitempty"`

// ID is the Atlas identifier of the Network Container Atlas resource this Peering Connection relies on
// Use either name or ID, not both.
// +optional
ID string `json:"id,omitempty"`
}

// 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
// +kubebuilder:validation:Required
Provider string `json:"provider"`
// ID of the network peer container. If not set, operator will create a new container with ContainerRegion and AtlasCIDRBlock input.
// +optional
ContainerID string `json:"containerId"`

// AWSConfiguration is the specific AWS settings for network peering
// +kubebuilder:validation:Optional
Expand All @@ -36,40 +98,66 @@ type AtlasNetworkPeeringConfig struct {
GCPConfiguration *GCPNetworkPeeringConfiguration `json:"gcpConfiguration,omitempty"`
}

type AtlasProviderContainerConfig struct {
// ContainerRegion is the provider region name of Atlas network peer container. If not set, AccepterRegionName is used.
// +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
// +kubebuilder:validation:Required
AccepterRegionName string `json:"accepterRegionName"`
// AccountID of the user's vpc.
// +kubebuilder:validation:Required
AWSAccountID string `json:"awsAccountId,omitempty"`
// User VPC CIDR.
// +kubebuilder:validation:Required
RouteTableCIDRBlock string `json:"routeTableCidrBlock,omitempty"`
// AWS VPC ID.
// +kubebuilder:validation:Required
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.
// +kubebuilder:validation:Required
AzureDirectoryID string `json:"azureDirectoryId,omitempty"`
// AzureSubscriptionID is the unique identifier of the Azure subscription in which the VNet resides.
// +kubebuilder:validation:Required
AzureSubscriptionID string `json:"azureSubscriptionId,omitempty"`
//ResourceGroupName is the name of your Azure resource group.
// +kubebuilder:validation:Required
ResourceGroupName string `json:"resourceGroupName,omitempty"`
// VNetName is name of your Azure VNet. Its applicable only for Azure.
VNetName string `json:"vnetName,omitempty"`
// +kubebuilder:validation:Required
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.
// +kubebuilder:validation:Required
GCPProjectID string `json:"gcpProjectId,omitempty"`
// GCP Network Peer Name. Its applicable only for GCP.
// +kubebuilder:validation:Required
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)
}
}
77 changes: 77 additions & 0 deletions api/v1/atlasnetworkpeering_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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/test/helper/cel"
)

func TestPeeringCELChecks(t *testing.T) {
for _, tc := range []struct {
title string
obj *AtlasNetworkPeering
expectedErrors []string
}{
{
title: "Missing container ref in peering fails",
obj: &AtlasNetworkPeering{
Spec: AtlasNetworkPeeringSpec{},
},
expectedErrors: []string{"spec: Invalid value: \"object\": must either have a container Atlas id or Kubernetes name, but not both (or neither)"},
},

{
title: "Named container ref works",
obj: &AtlasNetworkPeering{
Spec: AtlasNetworkPeeringSpec{
ContainerRef: ContainerDualReference{
Name: "Some-name",
},
},
},
},

{
title: "Container id ref works",
obj: &AtlasNetworkPeering{
Spec: AtlasNetworkPeeringSpec{
ContainerRef: ContainerDualReference{
ID: "some-id",
},
},
},
},

{
title: "Both container id and name ref fails",
obj: &AtlasNetworkPeering{
Spec: AtlasNetworkPeeringSpec{
ContainerRef: ContainerDualReference{
Name: "Some-name",
ID: "some-id",
},
},
},
expectedErrors: []string{"spec: Invalid value: \"object\": must either have a container Atlas id or Kubernetes name, but not both (or neither)"},
},
} {
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_atlasnetworkpeerings.yaml"
validator, err := cel.VersionValidatorFromFile(t, crdPath, "v1")
assert.NoError(t, err)
errs := validator(unstructuredObject, nil)

require.Equal(t, tc.expectedErrors, cel.ErrorListAsStrings(errs))
})
}
}
17 changes: 16 additions & 1 deletion api/v1/project_reference_cel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ var dualRefCRDs = []struct {
},
filename: "atlas.mongodb.com_atlasnetworkcontainers.yaml",
},
{
obj: &AtlasNetworkPeering{
Spec: AtlasNetworkPeeringSpec{ // Avoid triggering peering specific validations
ContainerRef: ContainerDualReference{Name: "fake-ref"},
},
},
filename: "atlas.mongodb.com_atlasnetworkpeerings.yaml",
},
}

var testCases = []struct {
Expand Down Expand Up @@ -165,7 +173,14 @@ func TestProjectDualReferenceCELValidations(t *testing.T) {
assert.NoError(t, err)
errs := validator(unstructuredObject, unstructuredOldObject)

require.Equal(t, tc.expectedErrors, cel.ErrorListAsStrings(errs))
for i, err := range errs {
fmt.Printf("%s error %d: %v\n", title, i, err)
}

require.Equal(t, len(tc.expectedErrors), len(errs))
for i, err := range errs {
assert.Equal(t, tc.expectedErrors[i], err.Error())
}
})
}
}
Expand Down
36 changes: 36 additions & 0 deletions api/v1/status/atlasnetworkpeering.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package status

import "github.com/mongodb/mongodb-atlas-kubernetes/v2/api"

// AtlasNetworkPeeringStatus is a status for the AtlasNetworkPeering Custom resource.
// Not the one included in the AtlasProject
type AtlasNetworkPeeringStatus struct {
api.Common `json:",inline"`

// ID recrods the identified of the peer created by Atlas
ID string `json:"id,omitempty"`

// Status describes the last status seen for the network peering setup
Status string `json:"status,omitempty"`

// AWSStatus contains AWS only related status information
AWSStatus *AWSPeeringStatus `json:"awsStatus,omitempty"`

// AzureStatus contains Azure only related status information
AzureStatus *AzureContainerStatus `json:"azureStatus,omitempty"`

// GCPStatus contains GCP only related status information
GCPStatus *GCPContainerStatus `json:"gcpStatus,omitempty"`
}

// AWSPeeringStatus contains AWS only related status for network peering & container
type AWSPeeringStatus struct {
AWSContainerStatus `json:",inline"`

// ConnectionID is the AWS VPC peering connection ID
ConnectionID string `json:"connectionId,omitempty"`
}

// +kubebuilder:object:generate=false

type AtlasNetworkPeeringStatusOption func(s *AtlasNetworkPeeringStatus)
47 changes: 47 additions & 0 deletions api/v1/status/zz_generated.deepcopy.go

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

Loading
Loading