openstack: find node CIDR with multiple subnets

This commit is contained in:
Malte Poll 2024-02-15 10:43:48 +01:00
parent d8185fdafb
commit 31f65fb486
6 changed files with 124 additions and 36 deletions

View File

@ -20,6 +20,7 @@ go_library(
"//internal/role",
"@com_github_gophercloud_gophercloud//:gophercloud",
"@com_github_gophercloud_gophercloud//openstack/compute/v2/servers",
"@com_github_gophercloud_gophercloud//openstack/networking/v2/networks",
"@com_github_gophercloud_gophercloud//openstack/networking/v2/subnets",
"@com_github_gophercloud_gophercloud//pagination",
"@com_github_gophercloud_utils//openstack/clientconfig",
@ -41,6 +42,7 @@ go_test(
"//internal/role",
"@com_github_gophercloud_gophercloud//:gophercloud",
"@com_github_gophercloud_gophercloud//openstack/compute/v2/servers",
"@com_github_gophercloud_gophercloud//openstack/networking/v2/networks",
"@com_github_gophercloud_gophercloud//openstack/networking/v2/subnets",
"@com_github_gophercloud_gophercloud//pagination",
"@com_github_stretchr_testify//assert",

View File

@ -11,6 +11,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/role"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
"github.com/gophercloud/gophercloud/pagination"
)
@ -27,6 +28,7 @@ type imdsAPI interface {
type serversAPI interface {
ListServers(opts servers.ListOptsBuilder) pagerAPI
ListNetworks(opts networks.ListOptsBuilder) pagerAPI
ListSubnets(opts subnets.ListOpts) pagerAPI
}

View File

@ -11,6 +11,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/role"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
"github.com/gophercloud/gophercloud/pagination"
)
@ -62,6 +63,7 @@ func (c *stubIMDSClient) vpcIP(_ context.Context) (string, error) {
type stubServersClient struct {
serversPager stubPager
netsPager stubPager
subnetsPager stubPager
}
@ -69,6 +71,10 @@ func (c *stubServersClient) ListServers(_ servers.ListOptsBuilder) pagerAPI {
return &c.serversPager
}
func (c *stubServersClient) ListNetworks(_ networks.ListOptsBuilder) pagerAPI {
return &c.netsPager
}
func (c *stubServersClient) ListSubnets(_ subnets.ListOpts) pagerAPI {
return &c.subnetsPager
}

View File

@ -19,6 +19,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/role"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
"github.com/gophercloud/utils/openstack/clientconfig"
)
@ -71,17 +72,17 @@ func New(ctx context.Context) (*Cloud, error) {
}
serversClient.Microversion = microversion
subnetsClient, err := clientconfig.NewServiceClient("network", clientOpts)
networksClient, err := clientconfig.NewServiceClient("network", clientOpts)
if err != nil {
return nil, fmt.Errorf("creating network client: %w", err)
}
subnetsClient.Microversion = microversion
networksClient.Microversion = microversion
return &Cloud{
imds: imds,
api: &apiClient{
servers: serversClient,
subnets: subnetsClient,
servers: serversClient,
networks: networksClient,
},
}, nil
}
@ -308,22 +309,43 @@ func (c *Cloud) getLoadBalancerHost(ctx context.Context) (string, error) {
}
func (c *Cloud) getSubnetCIDR(uidTag string) (netip.Prefix, error) {
listNetworksOpts := networks.ListOpts{Tags: uidTag}
networksPage, err := c.api.ListNetworks(listNetworksOpts).AllPages()
if err != nil {
return netip.Prefix{}, fmt.Errorf("listing networks: %w", err)
}
nets, err := networks.ExtractNetworks(networksPage)
if err != nil {
return netip.Prefix{}, fmt.Errorf("extracting networks: %w", err)
}
if len(nets) != 1 {
return netip.Prefix{}, fmt.Errorf("expected exactly one network, got %d", len(nets))
}
listSubnetsOpts := subnets.ListOpts{Tags: uidTag}
subnetsPage, err := c.api.ListSubnets(listSubnetsOpts).AllPages()
if err != nil {
return netip.Prefix{}, fmt.Errorf("listing subnets: %w", err)
}
nets, err := subnets.ExtractSubnets(subnetsPage)
snets, err := subnets.ExtractSubnets(subnetsPage)
if err != nil {
return netip.Prefix{}, fmt.Errorf("extracting subnets: %w", err)
}
if len(nets) != 1 {
return netip.Prefix{}, fmt.Errorf("expected exactly one subnet, got %d", len(nets))
if len(snets) < 1 {
return netip.Prefix{}, fmt.Errorf("expected at least one subnet, got %d", len(snets))
}
cidr, err := netip.ParsePrefix(nets[0].CIDR)
var rawCIDR string
for _, n := range snets {
if n.Name == nets[0].Name {
rawCIDR = n.CIDR
break
}
}
cidr, err := netip.ParsePrefix(rawCIDR)
if err != nil {
return netip.Prefix{}, fmt.Errorf("parsing subnet CIDR: %w", err)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/role"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
"github.com/gophercloud/gophercloud/pagination"
"github.com/stretchr/testify/assert"
@ -175,7 +176,8 @@ func TestList(t *testing.T) {
Addresses: newTestAddrs("198.51.100.1", ""),
},
}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
want: []metadata.InstanceMetadata{
{
@ -196,7 +198,8 @@ func TestList(t *testing.T) {
imds: &stubIMDSClient{uidResult: "uid"},
api: &stubServersClient{
serversPager: newSeverPager([]servers.Server{}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -204,9 +207,17 @@ func TestList(t *testing.T) {
imds: &stubIMDSClient{uidErr: someErr},
wantErr: true,
},
"list nets errors": {
imds: &stubIMDSClient{uidResult: "uid"},
api: &stubServersClient{
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, someErr),
},
wantErr: true,
},
"list subnets error": {
imds: &stubIMDSClient{uidResult: "uid"},
api: &stubServersClient{
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: stubPager{allPagesErr: someErr},
},
wantErr: true,
@ -214,16 +225,18 @@ func TestList(t *testing.T) {
"extract subnets error": {
imds: &stubIMDSClient{uidResult: "uid"},
api: &stubServersClient{
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{}, someErr),
},
wantErr: true,
},
"multiple subnets error": {
"subnet name mismatch error": {
imds: &stubIMDSClient{uidResult: "uid"},
api: &stubServersClient{
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{
{CIDR: "192.0.2.0/24"},
{CIDR: "198.51.100.0/24"},
{Name: "othernet", CIDR: "192.0.2.0/24"},
{Name: "yetanothernet", CIDR: "198.51.100.0/24"},
}, nil),
},
wantErr: true,
@ -231,7 +244,8 @@ func TestList(t *testing.T) {
"parse subnet error": {
imds: &stubIMDSClient{uidResult: "uid"},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "notAnIP"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "notAnIP"}}, nil),
},
wantErr: true,
},
@ -239,7 +253,8 @@ func TestList(t *testing.T) {
imds: &stubIMDSClient{uidResult: "uid"},
api: &stubServersClient{
serversPager: stubPager{allPagesErr: someErr},
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -247,7 +262,8 @@ func TestList(t *testing.T) {
imds: &stubIMDSClient{uidResult: "uid"},
api: &stubServersClient{
serversPager: newSeverPager([]servers.Server{}, someErr),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -261,7 +277,8 @@ func TestList(t *testing.T) {
Addresses: newTestAddrs("192.0.2.5", ""),
},
}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -275,7 +292,8 @@ func TestList(t *testing.T) {
Addresses: newTestAddrs("192.0.2.5", ""),
},
}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -289,7 +307,8 @@ func TestList(t *testing.T) {
Addresses: newTestAddrs("192.0.2.5", ""),
},
}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -304,7 +323,8 @@ func TestList(t *testing.T) {
Addresses: newTestAddrs("192.0.2.5", ""),
},
}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -319,7 +339,8 @@ func TestList(t *testing.T) {
Addresses: newTestAddrs("192.0.2.5", ""),
},
}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -334,7 +355,8 @@ func TestList(t *testing.T) {
Addresses: map[string]any{"foo": "bar"},
},
}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -349,7 +371,8 @@ func TestList(t *testing.T) {
Addresses: newTestAddrs("invalidIP", ""),
},
}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
},
wantErr: true,
},
@ -499,6 +522,7 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"error returned from getSubnetCIDR": {
imds: &stubIMDSClient{},
api: &stubServersClient{
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager(nil, errors.New("failed")),
},
wantErr: true,
@ -506,7 +530,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"error returned from getServers": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager(nil, errors.New("failed")),
},
wantErr: true,
@ -514,7 +539,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"sever with empty name skipped": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager([]servers.Server{
{
ID: "id1",
@ -528,7 +554,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"server with empty ID skipped": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager([]servers.Server{
{
Name: "name1",
@ -542,7 +569,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"sever with nil tags skipped": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager([]servers.Server{
{
Name: "name1",
@ -556,7 +584,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"server has invalid address": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager([]servers.Server{
{
Name: "name1",
@ -571,7 +600,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"server without parseable addresses skipped": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager([]servers.Server{
{
Name: "name1",
@ -588,7 +618,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"invalid endpoint returned from server addresses": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager([]servers.Server{
{
Name: "name1",
@ -603,7 +634,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"valid endpoint returned from server addresses not in subnet CIDR": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager([]servers.Server{
{
Name: "name1",
@ -618,7 +650,8 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
"first valid endpoint returned from server addresses not in subnet CIDR": {
imds: &stubIMDSClient{},
api: &stubServersClient{
subnetsPager: newSubnetPager([]subnets.Subnet{{CIDR: "192.0.2.0/24"}}, nil),
netsPager: newNetPager([]networks.Network{{Name: "mynet"}}, nil),
subnetsPager: newSubnetPager([]subnets.Subnet{{Name: "mynet", CIDR: "192.0.2.0/24"}}, nil),
serversPager: newSeverPager([]servers.Server{
{
Name: "name1",
@ -654,6 +687,24 @@ func TestGetLoadBalancerEndpoint(t *testing.T) {
}
}
// newNetPager returns a network pager as we would get from a ListNetworks.
func newNetPager(nets []networks.Network, err error) stubPager {
return stubPager{
page: networks.NetworkPage{
LinkedPageBase: pagination.LinkedPageBase{
PageResult: pagination.PageResult{
Result: gophercloud.Result{
Body: struct {
Networks []networks.Network `json:"networks"`
}{nets},
Err: err,
},
},
},
},
}
}
// newSubnetPager returns a subnet pager as we would get from a ListSubnets.
func newSubnetPager(nets []subnets.Subnet, err error) stubPager {
return stubPager{

View File

@ -9,18 +9,23 @@ package openstack
import (
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
)
type apiClient struct {
servers *gophercloud.ServiceClient
subnets *gophercloud.ServiceClient
servers *gophercloud.ServiceClient
networks *gophercloud.ServiceClient
}
func (c *apiClient) ListServers(opts servers.ListOptsBuilder) pagerAPI {
return servers.List(c.servers, opts)
}
func (c *apiClient) ListSubnets(opts subnets.ListOpts) pagerAPI {
return subnets.List(c.subnets, opts)
func (c *apiClient) ListNetworks(opts networks.ListOptsBuilder) pagerAPI {
return networks.List(c.networks, opts)
}
func (c *apiClient) ListSubnets(opts subnets.ListOpts) pagerAPI {
return subnets.List(c.networks, opts)
}