Skip to content

Commit

Permalink
CLOUDP-280230: Network peering translation layer (#1884)
Browse files Browse the repository at this point in the history
* Fix indirect go mod ref and update licenses

* CLOUDP-280230: Network peering translation layer

* Improve contract test helper

Signed-off-by: jose.vazquez <[email protected]>

* Add network peering contract tests

Signed-off-by: jose.vazquez <[email protected]>

---------

Signed-off-by: jose.vazquez <[email protected]>
  • Loading branch information
josvazg authored Nov 22, 2024
1 parent e92c44b commit 7b11f53
Show file tree
Hide file tree
Showing 21 changed files with 1,294 additions and 124 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/test-contract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,14 @@ jobs:
MCLI_ORG_ID: ${{ secrets.ATLAS_ORG_ID }}
MCLI_PUBLIC_API_KEY: ${{ secrets.ATLAS_PUBLIC_KEY }}
MCLI_PRIVATE_API_KEY: ${{ secrets.ATLAS_PRIVATE_KEY }}
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets. AZURE_CLIENT_SECRET }}
GOOGLE_PROJECT_ID: ${{ secrets.GOOGLE_PROJECT_ID }}
GCP_SA_CRED: ${{ secrets.GCP_SA_CRED}}
USE_KIND: "false" # Avoid launching a kind cluster yet again
run: devbox run -- 'make contract-tests'
2 changes: 1 addition & 1 deletion .licenses-gomod.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
100644 8b729a99c24b7dbc21e36509be5899392215190f go.mod
100644 c22a2d48ac68bce318aa0942dfd8a5366a68e291 go.mod
18 changes: 16 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ CONTAINER_SPEC=.spec.template.spec.containers[0]

SILK_ASSET_GROUP="atlas-kubernetes-operator"

HELM_REPO_URL = "https://mongodb.github.io/helm-charts"
HELM_AKO_INSTALL_NAME = local-ako-install
HELM_AKO_NAMESPACE = test-ako

.DEFAULT_GOAL := help
.PHONY: help
help: ## Show this help screen
Expand Down Expand Up @@ -567,7 +571,17 @@ download-from-silk: ## Download the latest augmented SBOM for a given architectu
store-silk-sboms: download-from-silk ## Download & Store the latest augmented SBOM for a given version & architecture
SILK_ASSET_GROUP=$(SILK_ASSET_GROUP) ./scripts/store-sbom-in-s3.sh $(VERSION) $(TARGET_ARCH)

.PHONY: install-ako-helm
install-ako-helm:
helm repo add mongodb $(HELM_REPO_URL)
helm upgrade $(HELM_AKO_INSTALL_NAME) mongodb/mongodb-atlas-operator --atomic --install \
--set-string atlasURI=$(MCLI_OPS_MANAGER_URL) \
--set objectDeletionProtection=false \
--set subobjectDeletionProtection=false \
--namespace=$(HELM_AKO_NAMESPACE) --create-namespace
kubectl get crds

.PHONY: contract-tests
contract-tests: run-kind ## Run contract tests
contract-tests: run-kind install-ako-helm ## Run contract tests with support by a k8s cluster and AKO
go clean -testcache
AKO_CONTRACT_TEST=1 go test -v -race -cover ./test/contract/...
AKO_CONTRACT_TEST=1 HELM_AKO_NAMESPACE=$(HELM_AKO_NAMESPACE) go test -v -race -cover ./test/contract/...
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
Expand Down
180 changes: 180 additions & 0 deletions internal/translation/networkpeering/conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package networkpeering

import (
"fmt"

"go.mongodb.org/atlas-sdk/v20231115008/admin"

"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/pointer"
akov2 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1"
"github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1/provider"
)

type NetworkPeer struct {
akov2.AtlasNetworkPeeringConfig
ID string
}

func NewNetworkPeer(id string, cfg *akov2.AtlasNetworkPeeringConfig) *NetworkPeer {
return &NetworkPeer{
AtlasNetworkPeeringConfig: *cfg,
ID: id,
}
}

type ProviderContainer struct {
akov2.AtlasProviderContainerConfig
ID string
Provider string
}

func NewProviderContainer(id string, provider string, cfg *akov2.AtlasProviderContainerConfig) *ProviderContainer {
return &ProviderContainer{
AtlasProviderContainerConfig: *cfg,
ID: id,
Provider: provider,
}
}

func toAtlasConnection(peer *NetworkPeer) (*admin.BaseNetworkPeeringConnectionSettings, error) {
switch peer.Provider {
case string(provider.ProviderAWS):
if peer.AWSConfiguration == nil {
return nil, fmt.Errorf("unsupported AWS peer with AWSConfiguration unset")
}
return &admin.BaseNetworkPeeringConnectionSettings{
ContainerId: peer.ContainerID,
ProviderName: pointer.SetOrNil(peer.Provider, ""),
AccepterRegionName: pointer.SetOrNil(peer.AWSConfiguration.AccepterRegionName, ""),
AwsAccountId: pointer.SetOrNil(peer.AWSConfiguration.AWSAccountID, ""),
RouteTableCidrBlock: pointer.SetOrNil(peer.AWSConfiguration.RouteTableCIDRBlock, ""),
VpcId: pointer.SetOrNil(peer.AWSConfiguration.VpcID, ""),
}, nil
case string(provider.ProviderGCP):
if peer.GCPConfiguration == nil {
return nil, fmt.Errorf("unsupported Google peer with GCPConfiguration unset")
}
return &admin.BaseNetworkPeeringConnectionSettings{
ContainerId: peer.ContainerID,
ProviderName: pointer.SetOrNil(peer.Provider, ""),
GcpProjectId: pointer.SetOrNil(peer.GCPConfiguration.GCPProjectID, ""),
NetworkName: pointer.SetOrNil(peer.GCPConfiguration.NetworkName, ""),
}, nil
case string(provider.ProviderAzure):
if peer.AzureConfiguration == nil {
return nil, fmt.Errorf("unsupported Azure peer with AzureConfiguration unset")
}
return &admin.BaseNetworkPeeringConnectionSettings{
ContainerId: peer.ContainerID,
ProviderName: pointer.SetOrNil(peer.Provider, ""),
AzureDirectoryId: pointer.SetOrNil(peer.AzureConfiguration.AzureDirectoryID, ""),
AzureSubscriptionId: pointer.SetOrNil(peer.AzureConfiguration.AzureSubscriptionID, ""),
ResourceGroupName: pointer.SetOrNil(peer.AzureConfiguration.ResourceGroupName, ""),
VnetName: pointer.SetOrNil(peer.AzureConfiguration.VNetName, ""),
}, nil
default:
return nil, fmt.Errorf("unsupported provider %q", peer.Provider)
}
}

func fromAtlasConnection(conn *admin.BaseNetworkPeeringConnectionSettings) (*NetworkPeer, error) {
switch provider.ProviderName(conn.GetProviderName()) {
case provider.ProviderAWS:
return &NetworkPeer{
ID: conn.GetId(),
AtlasNetworkPeeringConfig: akov2.AtlasNetworkPeeringConfig{
ContainerID: conn.GetContainerId(),
Provider: conn.GetProviderName(),
AWSConfiguration: &akov2.AWSNetworkPeeringConfiguration{
AccepterRegionName: conn.GetAccepterRegionName(),
AWSAccountID: conn.GetAwsAccountId(),
RouteTableCIDRBlock: conn.GetRouteTableCidrBlock(),
VpcID: conn.GetVpcId(),
},
},
}, nil
case provider.ProviderGCP:
return &NetworkPeer{
ID: conn.GetId(),
AtlasNetworkPeeringConfig: akov2.AtlasNetworkPeeringConfig{
ContainerID: conn.GetContainerId(),
Provider: conn.GetProviderName(),
GCPConfiguration: &akov2.GCPNetworkPeeringConfiguration{
GCPProjectID: conn.GetGcpProjectId(),
NetworkName: conn.GetNetworkName(),
},
},
}, nil
case provider.ProviderAzure:
return &NetworkPeer{
ID: conn.GetId(),
AtlasNetworkPeeringConfig: akov2.AtlasNetworkPeeringConfig{
ContainerID: conn.GetContainerId(),
Provider: conn.GetProviderName(),
AzureConfiguration: &akov2.AzureNetworkPeeringConfiguration{
AzureDirectoryID: conn.GetAzureDirectoryId(),
AzureSubscriptionID: conn.GetAzureSubscriptionId(),
ResourceGroupName: conn.GetResourceGroupName(),
VNetName: conn.GetVnetName(),
},
},
}, nil
default:
return nil, fmt.Errorf("unsupported provider %q", conn.GetProviderName())
}
}

func fromAtlasConnectionList(list []admin.BaseNetworkPeeringConnectionSettings) ([]NetworkPeer, error) {
if list == nil {
return nil, nil
}
peers := make([]NetworkPeer, 0, len(list))
for i, conn := range list {
c, err := fromAtlasConnection(&conn)
if err != nil {
return nil, fmt.Errorf("failed to convert connection list item %d: %w", i, err)
}
peers = append(peers, *c)
}
return peers, nil
}

func toAtlasContainer(container *ProviderContainer) *admin.CloudProviderContainer {
cpc := &admin.CloudProviderContainer{
Id: pointer.SetOrNil(container.ID, ""),
ProviderName: pointer.SetOrNil(container.Provider, ""),
AtlasCidrBlock: pointer.SetOrNil(container.AtlasCIDRBlock, ""),
}
if cpc.GetProviderName() == string(provider.ProviderAWS) {
cpc.RegionName = pointer.SetOrNil(container.ContainerRegion, "")
} else {
cpc.Region = pointer.SetOrNil(container.ContainerRegion, "")
}
return cpc
}

func fromAtlasContainer(container *admin.CloudProviderContainer) *ProviderContainer {
region := container.GetRegion()
if container.GetProviderName() == string(provider.ProviderAWS) {
region = container.GetRegionName()
}
return &ProviderContainer{
ID: container.GetId(),
Provider: container.GetProviderName(),
AtlasProviderContainerConfig: akov2.AtlasProviderContainerConfig{
AtlasCIDRBlock: container.GetAtlasCidrBlock(),
ContainerRegion: region,
},
}
}

func fromAtlasContainerList(list []admin.CloudProviderContainer) []ProviderContainer {
if list == nil {
return nil
}
containers := make([]ProviderContainer, 0, len(list))
for _, container := range list {
containers = append(containers, *fromAtlasContainer(&container))
}
return containers
}
111 changes: 111 additions & 0 deletions internal/translation/networkpeering/conversion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package networkpeering

import (
"fmt"
"testing"

gofuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/atlas-sdk/v20231115008/admin"

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

const fuzzIterations = 100

var providerNames = []string{
string(provider.ProviderAWS),
string(provider.ProviderAzure),
string(provider.ProviderGCP),
}

func FuzzConvertConnection(f *testing.F) {
for i := uint(0); i < fuzzIterations; i++ {
f.Add(([]byte)(fmt.Sprintf("seed sample %x", i)), i)
}
f.Fuzz(func(t *testing.T, data []byte, index uint) {
peerData := NetworkPeer{}
gofuzz.NewFromGoFuzz(data).Fuzz(&peerData)
peerData.Provider = providerNames[index%3]
cleanupPeer(&peerData)
atlasConn, err := toAtlasConnection(&peerData)
require.NoError(t, err)
result, err := fromAtlasConnection(atlasConn)
require.NoError(t, err)
assert.Equal(t, &peerData, result, "failed for index=%d", index)
})
}

func FuzzConvertListOfConnections(f *testing.F) {
for i := uint(0); i < fuzzIterations; i++ {
f.Add(([]byte)(fmt.Sprintf("seed sample %x", i)), i, (i % 5))
}
f.Fuzz(func(t *testing.T, data []byte, index uint, size uint) {
conns := []admin.BaseNetworkPeeringConnectionSettings{}
expected := []NetworkPeer{}
for i := uint(0); i < size; i++ {
peerData := NetworkPeer{}
gofuzz.NewFromGoFuzz(data).Fuzz(&peerData)
peerData.Provider = providerNames[index%3]
cleanupPeer(&peerData)
atlasConn, err := toAtlasConnection(&peerData)
require.NoError(t, err)
expectedConn, err := fromAtlasConnection(atlasConn)
require.NoError(t, err)
expected = append(expected, *expectedConn)
atlasConnItem, err := toAtlasConnection(&peerData)
require.NoError(t, err)
conns = append(conns, *atlasConnItem)
}
result, err := fromAtlasConnectionList(conns)
require.NoError(t, err)
assert.Equal(t, expected, result)
})
}

func FuzzConvertContainer(f *testing.F) {
for i := uint(0); i < fuzzIterations; i++ {
f.Add(([]byte)(fmt.Sprintf("seed sample %x", i)), i)
}
f.Fuzz(func(t *testing.T, data []byte, index uint) {
containerData := ProviderContainer{}
gofuzz.NewFromGoFuzz(data).Fuzz(&containerData)
containerData.Provider = providerNames[index%3]
result := fromAtlasContainer(toAtlasContainer(&containerData))
assert.Equal(t, &containerData, result, "failed for index=%d", index)
})
}

func FuzzConvertListOfContainers(f *testing.F) {
for i := uint(0); i < fuzzIterations; i++ {
f.Add(([]byte)(fmt.Sprintf("seed sample %x", i)), i, (i % 5))
}
f.Fuzz(func(t *testing.T, data []byte, index uint, size uint) {
containers := []admin.CloudProviderContainer{}
expected := []ProviderContainer{}
for i := uint(0); i < size; i++ {
containerData := ProviderContainer{}
gofuzz.NewFromGoFuzz(data).Fuzz(&containerData)
containerData.Provider = providerNames[index%3]
expectedContainer := fromAtlasContainer(toAtlasContainer(&containerData))
expected = append(expected, *expectedContainer)
containers = append(containers, *toAtlasContainer(&containerData))
}
result := fromAtlasContainerList(containers)
assert.Equal(t, expected, result)
})
}

func cleanupPeer(peer *NetworkPeer) {
peer.ID = ""
if peer.Provider != string(provider.ProviderAWS) {
peer.AWSConfiguration = nil
}
if peer.Provider != string(provider.ProviderGCP) {
peer.GCPConfiguration = nil
}
if peer.Provider != string(provider.ProviderAzure) {
peer.AzureConfiguration = nil
}
}
Loading

0 comments on commit 7b11f53

Please sign in to comment.